Processing input files

What is the easiest technique to get the n-th line from a input file called input and pass it on to a macro called \process. Please note that I'm not asking about outputing the n-th line verbatim but about catching the line and passing it on to the macro. The macro \process is defined as follows:

\def\process#1\endprocess{%
% process #1
}


The n-th line of the input file should be passed as the argument #1 to the macro \process.

-
@werner The external-files tag is good. Thanks for providing it. The input tag, which I added in a related thread, was removed twice:-) –  Marc van Dongen Jan 4 '12 at 23:41
Now that you mention it, input seems to be inappropriate here since you're not actually using (or requesting the use of) \input, but you're processing part of an external-file. –  Werner Jan 4 '12 at 23:44
@werner I agree that input isn't so good. –  Marc van Dongen Jan 4 '12 at 23:47
add comment

1 Answer

\makeatletter
\newread\dongen@in
\newcommand{\dongenprocess}[2]{% #1=file name, #2=line to read
\begingroup\endlinechar=\m@ne
\openin\dongen@in=#1\relax
\count@=\z@
\loop\ifnum\count@<#2\relax
\ifeof\dongen@in
\ERROR
\else
\read\dongen@in to \@tempa
\fi
\advance\count@\@ne
\repeat
\ifx\@tempa\@empty\ERROR\fi
\closein\dongen@in
\expandafter\endgroup
\expandafter\process\@tempa\endprocess
}
\makeatother


If you try to get past the end of the file you'll get some errors; of course you can define \ERROR as you wish, for example providing a fallback. I assume that lines are not empty. The second \ERROR corresponds to the empty line that TeX implicitly assumes at the end of a file.

Basically, I do a loop reading one line at a time until reaching the desired one. Then \@tempa (expanded) is passed to \process.

Call it as

\dongenprocess{dongen.dat}{2}


to read the second line.

Here is a possible LaTeX3 implementation:

\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\dongenprocess}{mm}
{
\group_begin: \tex_endlinechar:D \c_minus_one
\ior_open:Nn \l_dongen_read_s { #1 }
\prg_replicate:nn {#2}
{
\ior_if_eof:NTF \l_dongen_read_s
{ \dongen_error: }
{ \ior_to:NN \l_dongen_read_s \l_dongen_read_tl }
}
\ior_close:N \l_dongen_read_s
\tl_if_empty:NT \l_dongen_read_tl { \dongen_error: }
\exp_args:NNo \group_end: \dongen_process:n { \l_dongen_read_tl }
}

\ior_new:N \l_dongen_read_s
\tl_new:N \l_dongen_read_tl
\cs_new:Npn \dongen_process:n #1 { \process#1\endprocess }
\cs_new:Npn \dongen_error: { \tl_set:Nn \l_dongen_read_tl { INEXISTENT~LINE } }
\ExplSyntaxOff


EDIT: alternative version of \dongenprocess that trims spaces around the input read from the file

\NewDocumentCommand{\dongenprocess}{mm}
{
\group_begin:
\ior_open:Nn \l_dongen_read_s { #1 }
\prg_replicate:nn {#2}
{
\ior_if_eof:NTF \l_dongen_read_s
{ \dongen_error: }
{ \ior_to:NN \l_dongen_read_s \l_dongen_read_tl }
}
\ior_close:N \l_dongen_read_s
\tl_trim_spaces:N \l_dongen_read_tl
\tl_if_in:NnT \l_dongen_read_tl { \par } { \dongen_error: }
\exp_args:NNo \group_end: \dongen_process:n { \l_dongen_read_tl }
}

-
Thanks. Looks like an easy enough solution for a file processing toolkit. I've uppped your score but I'll wait a bit longer before I choose the/some best solution. –  Marc van Dongen Jan 4 '12 at 23:43
I like the elegance of the naming convention of the macros: \dongenprocess, \l_dongen_read_tl, ... –  Marc van Dongen Jan 5 '12 at 2:24
add comment