2

I want to read some lines from a text file, wrap a "style" which in this case is \texttt around each line, and as such concatenate it to a macro that I can use in the document. I came up with this MWE:

\documentclass{article}

\usepackage{filecontents} % tlmgr install filecontents

\begin{filecontents*}{test-2.txt}
some more
words in
here so as
to eventually
print
\end{filecontents*}

\usepackage{xstring}

\def\totcontent{}
\def\mystyle#1{\texttt{#1}}

% related: http://tex.stackexchange.com/questions/116078/expand-command-read-from-file
\newread\myread
\makeatletter
\immediate\openin\myread=test-2.txt
\@whilesw\unless\ifeof\myread\fi{%
  \readline\myread t\expandafter o\csname tmpline\endcsname %
  \typeout{Got tmpline: \tmpline} %
  \StrGobbleRight{\tmpline}{1}[\tmpline] % remove the ^^M newline (last 1 char)
  \ifx\tmpline\empty\else %
    %\edef\totcontent{\totcontent, \tmpline} % works without mystyle
    %\edef\totcontent{\totcontent, \mystyle{\tmpline}} % fail, breaks
    % via http://tex.stackexchange.com/a/74709/2595:
    \g@addto@macro\totcontent{, \mystyle{\tmpline}} % compiles, but no content in output
  \fi %
}
\immediate\closein\myread
\makeatother


\begin{document}

\typeout{totcontent is: \totcontent.}

I got this: \totcontent.

\end{document}

In all cases, I get this for the file reading:

Got tmpline: some more^^M
Got tmpline: words in^^M
Got tmpline: here so as^^M
Got tmpline: to eventually^^M
Got tmpline: print^^M
Got tmpline: ^^M

... which means the line ending is kept, so I remove the character at the end of the "string" with xstring's \StrGobbleRight.

The first case, \edef\totcontent{\totcontent, \tmpline} works fine, but has no style - the typeout then says:

totcontent is: , some more, words in, here so as, to eventually, print.

The second case, \edef\totcontent{\totcontent, \mystyle{\tmpline}}, breaks completely:

! Argument of \@xs@StrGobbleRight has an extra }.
<inserted text> 
                \par 
l.30 }

Here I intended on using \mystyle as a placeholder macro - expecting that in concatenation \edefs it would expand down to \texttt tokens, which I remember are \protected, and so won't expand further. Obviously, that didn't work.

The third case, \g@addto@macro\totcontent{, \mystyle{\tmpline}} actually does compile, but gives:

totcontent is: , \texttt {}, \texttt {}, \texttt {}, \texttt {}, \texttt {}.

... which is close, but still not what I wanted (I wanted the \texttt to be populated by the respective arguments from the loop).

So, how can I perform the concatenation, so in the end I end up with:

totcontent is: , \texttt {some more}, \texttt {words in}, \texttt {here so as}, \texttt {to eventually}, \texttt {print}.

... such that it would also typeset as we'd expect it to?


As per comments, example with `\read`:

\documentclass{article}

\usepackage{filecontents} % tlmgr install filecontents

\begin{filecontents*}{test-2.txt}
some more
words in
here so as
to eventually
print
\end{filecontents*}

\def\totcontent{}
\def\mystyle#1{\texttt{#1}}

\newread\myread
\makeatletter
\immediate\openin\myread=test-2.txt
\@whilesw\unless\ifeof\myread\fi{%
  \read\myread to \tmpline %
  \typeout{Got tmpline: \tmpline} %
%   \ifx\tmpline\empty\else %
  \ifx\tmpline\par\else %
    %\edef\totcontent{\totcontent, \tmpline} % works without mystyle
    %\edef\totcontent{\totcontent, \mystyle{\tmpline}} % fail, breaks
    \g@addto@macro\totcontent{, \mystyle{\tmpline}} % compiles, but no content in output
  \fi %
}
\immediate\closein\myread
\makeatother


\begin{document}

\typeout{totcontent is: \totcontent.}

I got this: \totcontent.

\end{document}

Prints:

Got tmpline: some more 
Got tmpline: words in 
Got tmpline: here so as 
Got tmpline: to eventually 
Got tmpline: print 
Got tmpline: \par 

Then \edef\totcontent{\totcontent, \mystyle{\tmpline}} fails w:

! Use of \@pr@videpackage doesn't match its definition.

and \g@addto@macro\totcontent{, \mystyle{\tmpline}} produces:

totcontent is: , \texttt {\par }, \texttt {\par }, \texttt {\par }, \texttt {\par }, \texttt {\par }, \texttt {\par }.

... which is still not what I wanted...

sdaau
  • 17,079
  • 1
    \read\myread to \tmpline will yield the StrGobble... stuff unnecessary –  Oct 14 '14 at 12:25
  • 1
    \readline is the wrong tool, use \read. – egreg Oct 14 '14 at 12:26
  • Many thanks, @ChristianHupfer and @egreg - I changed the example so it uses multi words per line; and indeed \read reads the whole line without LF (but the empty line at end is shown as \par), and does make StrGobble redundant. Still, that doesn't solve my problem: the \edef concat with \mystyle{\tmpline} then fails with ! Use of \@pr@videpackage doesn't match its definition.; and the \g@addto@macro produces \texttt {\par }, \texttt {\par }, .... – sdaau Oct 14 '14 at 12:37
  • 1
    One problem is, that \texttt is not expandable, as far as I know –  Oct 14 '14 at 12:45
  • Thanks, @ChristianHupfer: that is actually what I was counting on: I didn't use \edef because I want to expand - I used it because it was the only method I know of concatenation. What I want to achieve is to get a command as if I verbatim wrote e.g. \def\tst{\texttt{something here}, \texttt{and else}}, and then use \tst in the document to be typeset - except that here it should be the result of concatenation of lines of file input as shown in MWE. Cheers! – sdaau Oct 14 '14 at 12:50

3 Answers3

2

The usual problem are the end of lines. You don't want \readline, but the normal \read.

\begin{filecontents*}{\jobname.txt}
some more
words in
here so as
to eventually
print
\end{filecontents*}

\documentclass{article}

\makeatletter
\newread\myread
\makeatletter
\def\strip@final@space{\expandafter\strip@final@sp@ce\@tempa\strip@final@sp@ce}
\def\strip@final@sp@ce#1 \strip@final@sp@ce{\def\@tempa{#1}}
\def\a@par{\par}
\toks@={}
\openin\myread=\jobname.txt
\@whilesw\unless\ifeof\myread\fi{%
  \read\myread to \@tempa
  \typeout{Got tmpline: \@tempa}%
  \ifx\@tempa\a@par
  \else
    \strip@final@space
    \toks@=\expandafter{\the\expandafter\toks@\expandafter\texttt\expandafter{\@tempa}, }%
  \fi
}
\edef\totcontent{\the\toks@}
\closein\myread
\makeatother


\begin{document}

\typeout{totcontent is: \totcontent.}

I got this: \totcontent.

\end{document}

This is the output in the log file

totcontent is: \texttt {some more}, \texttt {words in}, \texttt {here so as}, \
texttt {to eventually}, \texttt {print}, .

Note that \@tempa will never be empty; an empty line or the final empty line will make \@tempa to expand to \par.

A simpler approach with expl3:

\begin{filecontents*}{\jobname.txt}
some more
words in
here so as
to eventually
print
\end{filecontents*}

\documentclass{article}
\usepackage{expl3}

\ExplSyntaxOn
\seq_new:N \l_sdaau_lines_seq
\tl_new:N \l_sdaau_totcontent_tl
\ior_new:N \sdaau_read_stream
\ior_open:Nn \sdaau_read_stream { \c_job_name_tl.txt }
\ior_map_inline:Nn \sdaau_read_stream
 {
  %\msg_term:n {Got~line:~|#1|} % uncomment for debugging
  \seq_put_right:Nn \l_sdaau_lines_seq { \texttt { #1 } }
 }
\tl_set:Nx \l_sdaau_totcontent_tl { \seq_use:Nn \l_sdaau_lines_seq { ,~ } }

\tl_set_eq:NN \totcontent \l_sdaau_totcontent_tl
\ExplSyntaxOff

\begin{document}

\typeout{totcontent is: \totcontent.}

I got this: \totcontent.

\end{document}

The output is

totcontent is: \texttt {somemore}, \texttt {wordsin}, \texttt {heresoas}, \text
tt {toeventually}, \texttt {print}.

Note that there's no trailing comma.

If you plan to have blank lines in the file, then change the line

  \seq_put_right:Nn \l_sdaau_lines_seq { \texttt { #1 } }

into

  \tl_if_blank:nF { #1 }
   {
    \seq_put_right:Nn \l_sdaau_lines_seq { \texttt { #1 } }
   }
egreg
  • 1,121,712
2

You can use \addto macro by the following way:

\long\def\addto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}
\def\addtolist#1{\global\addto\totcontent{, \texttt{#1}}}

\newread\infile
\openin\infile=test-2.txt
\bgroup
  \endlinechar=-1 \gdef\totcontent{}%
  \loop
    \unless\ifeof\infile
       \read\infile to\tmp
       \ifx\tmp\empty \else \expandafter\addtolist\expandafter{\tmp}\fi
       \repeat
\egroup

\message{totcontent is: \meaning\totcontent}

The terminal prints:

totcontent is: macro:->, \texttt {some more}, \texttt {words in}, \texttt {here
 so as}, \texttt {to eventually}, \texttt {print} 
wipet
  • 74,238
0

Ok, I got at least a working version - though I cannot say I understand what is going on; so a more erudite answer would still be appreciated. But after some messing about, I got a "two-step" approach with call to \protected@edef to work. Here is the MWE:

\documentclass{article}

\usepackage{filecontents} % tlmgr install filecontents

\begin{filecontents*}{test-2.txt}
some more
words in
here so as
to eventually
print
\end{filecontents*}

\def\totcontent{}
\def\mystyle#1{\texttt{#1}}

\def\tst{\texttt{something here}, \texttt{and else}}

\newread\myread
\makeatletter
\immediate\openin\myread=test-2.txt
\@whilesw\unless\ifeof\myread\fi{%
  \read\myread to \tmpline %
  \typeout{Got tmpline: \tmpline} %
  %\ifx\tmpline\par % does NOT work if \tmpline is \par!
  \if\tmpline\par % does work if \tmpline is \par!
    \typeout{PAR} %
  \else %
    %\edef\mytmp{\par} \typeout{1: -\mytmp-}      % dbg
    %\edef\mytmp{\tmpline} \typeout{2: -\mytmp-}  % dbg
    \protected@edef\mytmp{\mystyle{\tmpline}} %
    \typeout{mytmp: \mytmp} %
    \protected@edef\totcontent{\totcontent, \mytmp}
  \fi %
}
\immediate\closein\myread
\makeatother


\begin{document}

\typeout{totcontent is: \totcontent.}

I had this: \tst;

I got this: \totcontent.

\end{document}

This produces in terminal printout:

Got tmpline: some more 
mytmp: \texttt {some more }
Got tmpline: words in 
mytmp: \texttt {words in }
Got tmpline: here so as 
mytmp: \texttt {here so as }
Got tmpline: to eventually 
mytmp: \texttt {to eventually }
Got tmpline: print 
mytmp: \texttt {print }
Got tmpline: \par 
PAR

totcontent is: , \texttt {some more }, \texttt {words in }, \texttt {here so as
 }, \texttt {to eventually }, \texttt {print }.

... and \totcontent is typeset as you'd expect, with right fonts and everything; which is what I originally wanted.

sdaau
  • 17,079