0

I want to ensure in my LaTeX code, that my .csv input data which will be printed within an \pgfplotstable environment are valid or at least will not end up in a compiler error. So the LaTeX code needs to be so "secure", that anyone can edit the input data and when there is a fault, I will report the concrete error reason to the end user via a pdf comment. Of course this could also be done with other more high level tools, but this is a good project for me to learn a lot of how LaTeX works :)

The error handling is almost done, but I need to check for a dedicated pattern in the first (2) rows.

To be maximum variable and extendable, I created a function, where I just need to put in a defined pattern as an array and all the rest should work automatically.

For this, I wrote a more or less complicated routine to check the content about the first rows. This seems to work apart from this line: \noexpand\ifthenelse{\noexpand\equal{\noexpand\pgfplotsretval}{\noexpand\elementY}} There seems to be an expansion issue, but when you look at the output it should be correct.

MWE

    \documentclass[10pt,oneside,a4paper]{article}
    \usepackage{ifthen}
    \usepackage{pgfplotstable}
    \pgfplotsset{compat=newest}
    \setlength\parindent{0pt}

    \def\truncdiv#1#2{((#1-(#2-1)/2)/#2)}%
    \def\moduloop#1#2{(#1-\truncdiv{#1}{#2}*#2)}%
    \def\modulo#1#2{\number\numexpr\moduloop{#1}{#2}\relax}%

    \def\firstRowArray{{A},{B},{C}}
    \def\secondRowArray{{D},{E},{F}}
    \def\combinedArray{{\noexpand\firstRowArray},{\noexpand\secondRowArray}}

    \makeatletter
    \newcommand{\countlist}[1]{%
    \count@=\z@
    \@for\next:=#1\do{\advance\count@\@ne}%
    \the\count@
    }
    \makeatother

    \begin{filecontents}{data.csv}
    A;B;C  
    D;E;G 
    ?!;?!;?! 
    \end{filecontents}
    \pgfplotstableread[col sep=semicolon]{data.csv}{\csvdata}

    % Check the content of the first rows
    \newcommand{\checkFirstRows}[1]{
    \newcounter{columnCounter}
    \setcounter{columnCounter}{0}

    \newcounter{rowCounter}
    \setcounter{rowCounter}{-1}
    
    \def\maxColumnCount{\countlist{\firstRowArray}}

    \foreach \elementX in #1 {
        RowCounter:$\the\value{rowCounter}$\newline\newline
        \begingroup
        \edef\temp{\endgroup
            \noexpand\foreach \noexpand\elementY in \elementX {
                ColumnCounter:\noexpand\the\value{columnCounter}\newline
                ColumnCnt:\noexpand\maxColumnCount\newline
                 
                \noexpand\ifthenelse{\value{columnCounter}<3}{
                    \noexpand\pgfplotstablegetcolumnnamebyindex{\value{columnCounter}}\noexpand\of{\noexpand\csvdata}\noexpand\to\noexpand\pgfplotsretval
                    RET-1:\noexpand\pgfplotsretval\newline
                    ELE-Y1:\noexpand\elementY\newline 
                    \noexpand\ifthenelse{\noexpand\equal{\noexpand\pgfplotsretval}{\noexpand\elementY}}{
                            YES\newline
                    }{
                            NO\newline
                    }
                }{
                    \noexpand\gdef\noexpand\modValue{\noexpand\modulo{\noexpand\the\value{columnCounter}}{3}}
                    MOD:\noexpand\modValue\newline
                    \noexpand\pgfplotstablegetelem{\value{rowCounter}}{[index]\noexpand\modValue}\noexpand\of{\noexpand\csvdata}
                    RET-2:\noexpand\pgfplotsretval\newline
                    ELE-Y2:\noexpand\elementY\newline 
                    \noexpand\ifthenelse{\noexpand\equal{\noexpand\pgfplotsretval}{\noexpand\elementY}}{
                            YES\newline
                    }{
                            NO\newline
                    }
                }
                \noexpand\stepcounter{columnCounter}
                }
        }\temp 
        \stepcounter{rowCounter}   
      }
    }

    \pgfplotstableset{verb string type}

    \begin{document}
      \pgfplotstabletypeset{\csvdata}\\\\\\
      \checkFirstRows{\combinedArray}
    \end{document}

OUTPUT

Output

EXPECTED OUTPUT (without counter values)

RET-1:A

ELE-Y1:A

YES:1

RET-1:B

ELE-Y1:B

YES:1

RET-1:C

ELE-Y1:C

YES:1

=> Only YES until here would mean the header is correct!

RET-2:D

ELE-Y2:D

YES:2

RET-2:E

ELE-Y2:E

YES:2

RET-2:G

ELE-Y2:F

NO:2 => Failure! I got a "G" but an "F" is expected!

So basically, I would like to know the following two things:

  1. Why \noexpand\ifthenelse{\noexpand\equal{\noexpand\pgfplotsretval}{\noexpand\elementY}} never returns "YES"
  2. It would be great to get a hint how to replace the magic number 3 with the dynamically generated number: \def\maxColumnCount{\countlist{\firstRowArray}}
11
  • I take it 'secure' doesn't mean secure? because you really can't make it secure in the security sense. if you need that, you need to isolate it in a suitable sandbox/container environment. Commented Jul 30, 2024 at 18:41
  • @cfr Yes, you are right! This was the reason, why I put it into "". Secure in this case just means, that my dad who is also working with this input file will not destroy anything^^ Commented Jul 30, 2024 at 18:48
  • @cfr I just retested it a second ago with overleaf and it is working at my end... Commented Jul 30, 2024 at 18:52
  • 1
    for the second issue, you just need to use a counter. preferably a latex counter since this is latex and that will avoid some complications. I have no idea why overleaf gives you a different result. unless it's just because I've got newer versions. Commented Jul 30, 2024 at 19:13
  • 1
    @cfr Probably it will be my fault… but I still can‘t explain. Especially in the retest, when you told me, that it is not compiling I should have seen it, because I copy pasted this code here. But anyway, it‘s working now and you also solved the second issue. Thanks a lot! Commented Jul 31, 2024 at 5:42

1 Answer 1

1

\def doesn't make a counter and \def\macroname{3} isn't the same as assigning 3 to a count register. Since TeX expects a number here, you need to either use a count register or, preferably in LaTeX, a LaTeX counter (or the expl3 equivalent).

For example,

\makeatletter
\newcounter{pascallistcount}
\newcommand{\countlist}[1]{%
  \setcounter{pascallistcount}{0}%
  \@for\next:=#1\do{\stepcounter{pascallistcount}}%
}
\makeatother

Then you can use

\countlist{\firstRowArray}%

and \countlist will store the number in the counter, so you just need to use, say, \ifthenelse{\value{columnCounter}<\value{pascallistcount}} where a number is expected and \thepascallistcount when you want to typeset that number, for example.

This also simplifies your code a little bit, though it still seems overly complex for the job at hand, and I suspect you are duplicating some of pgfplotstable's functionality, though I'm not sufficiently familiar with the package to say for sure.

Note that your code is also introducing a great many spaces, which may not be what you want, even though multiples will be merged.

2
  • This works as expected 👌 Now all my checks are working and I don‘t get it broken anymore :) Apart of the scenario when the csv format is wrong. e.g. when one line has an additional row. Do you have a hint how to check this? Commented Jul 31, 2024 at 19:36
  • 1
    @PascalS like I said before, i don't know anything about pgfplotstable and I'm not sure what you mean by a line having an extra row. what is a line in this context, if not a row? does pgfplotstable offer anything to help with this? rolling your own in TeX may be an educational experience, but it is not necessarily the most efficient route to solving the problem ;). Commented Jul 31, 2024 at 23:41

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.