2

I require to save the value of the first read line for use in the next pass of the loop. On the second read, I then want to output both the previous read line's contents and the current read line's content. e.g

Input file:
line 1
line 2
line 3
line 4
line 5
line 6

Expected output:
line 1 / line 2
line 3 / line 4
line 5 / line 6

The \read in the following code resets the contents of the newcommand \firstline to be the same as the contents of the current read

\documentclass[]{article}
\usepackage{ifthen}
\usepackage{filecontents}
\begin{filecontents*}{tmp.txt}
line 1
line 2
line 3
line 4
line 5
line 6
\end{filecontents*}
\newread\reader
\endlinechar=-1
\newcounter{2xLines}
\newcommand\IterateLines{
    \begingroup
        \newcommand{\first}{}
        \setcounter{2xLines}{0}
        \openin\reader=./tmp.txt\relax
        \loop
            \read\reader to \line
            \unless\ifeof\reader
                \stepcounter{2xLines}
                \ifnum\the\value{2xLines}=2
                    \first / \line\par
                    \renewcommand{\first}{}
                    \setcounter{2xLines}{0}
                \else
                    \renewcommand{\first}{\line}
                \fi
        \repeat
        \closein\reader
    \endgroup
}
\makeatother
\begin{document}
\IterateLines
\end{document}

Apologies if not correctly formatted - first post

3

The problem is in \renewcommand{\first}{\line} instead of \let\first\line.

The difference is that the former just tell TeX to replace \first with \line; the latter defines \first to have the same current meaning of \line.

There are a few other improvements.

\begin{filecontents*}{\jobname.txt}
line 1
line 2
line 3
line 4
line 5
line 6
\end{filecontents*}

\documentclass{article}

\newread\reader
\newcounter{2xLines}

\newcommand\IterateLines{%
  \begingroup
  \endlinechar=-1
  \setcounter{2xLines}{0}%
  \openin\reader=./\jobname.txt\relax
  \loop
  \read\reader to \line
  \unless\ifeof\reader
  \stepcounter{2xLines}
  \ifodd\value{2xLines}
    \let\first=\line
  \else
    \first/\line\par
  \fi
  \repeat
  \closein\reader
  \endgroup
}
\begin{document}

\IterateLines

\end{document}

I used \jobname just to be sure of not clobbering my files, it's an unimportant detail. More important is that you should set \endlinechar only when reading the file, not globally.

Also the choice between even or odd can be made easier with \ifodd. Note that \value{2xLines} is better than \the\value{2xLines} in this context.

You may be interested in a different implementation using expl3.

\begin{filecontents*}{\jobname.txt}
line 1
line 2
line 3
line 4
line 5
line 6
\end{filecontents*}

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand\IterateLines{m}
 {
  \egreg_iteratelines:n { #1 }
 }

\seq_new:N \l__egreg_iteratelines_seq
\ior_new:N \g__egreg_iteratelines_stream

\cs_new_protected:Nn \egreg_iteratelines:n
 {
  \seq_clear:N \l__egreg_iteratelines_seq
  \ior_open:Nn \g__egreg_iteratelines_stream { #1 }
  \ior_map_inline:Nn \g__egreg_iteratelines_stream
   {
    \seq_put_right:Nx \l__egreg_iteratelines_seq { \tl_trim_spaces:n { ##1 } }
   }
  \int_step_inline:nnnn { 1 } { 2 } { \seq_count:N \l__egreg_iteratelines_seq }
   {
    \seq_item:Nn \l__egreg_iteratelines_seq { ##1 }
    /
    \seq_item:Nn \l__egreg_iteratelines_seq { ##1 + 1 }
    \par
   }
 }
\ExplSyntaxOff

\begin{document}

\IterateLines{\jobname.txt}

\end{document}

The main difference is that the reading part is decoupled from the typesetting part. First I read the whole file, storing each line in a sequence. Then the sequence is read back for typesetting two items per line.

  • Excellent. A valuable lesson with regards the the actions of \renewcommand and \let. – user150182 Dec 8 '17 at 10:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.