7

In order to strip comments from code I would like to input from a file into TeX, I am using the following loop

\documentclass{article}
\def\getfirst#1#2\relax{#1}
\begin{document}
\openin1=mysrcfile.txt
  \newcount\linecount
  \global\linecount1
  \loop
    \unless\ifeof1
    \read1 to \myinput
    \if\expandafter\getfirst\expandafter\myinput\relax #  % Here's the problem
      \relax
    \else
      \myinput
    \fi
    \global\advance\linecount1
  \repeat
\closein1
\end{document}

The code works without the loop, but inside the loop it gives me an error message, because # indicates I would like to hand on parameters to the \loop command, which is not what I want.

My first idea was of course escaping # using \char"23 or \#, but theses two expressions don't match the hash character and placing an \expandafter in front of it doesn't seem to change that.

Does anyone of you know about a way which escapes the number sign, but still allows for matching it to input chars?

2
  • Do you need to read the file such that # is a parameter token, or is it acceptable to read with an alternative catcode?
    – Joseph Wright
    Commented Dec 25, 2012 at 11:28
  • My only suggestion is to perhaps do a global replace inside the input file, replacing '#' for something like '<REM>', which will then not be subject to the same issues. Commented Dec 25, 2012 at 12:34

2 Answers 2

6

You have to hide # from the text of \loop, since it does

\def\iterate{<text up to \repeat>}

A way can be as follows

\documentclass{article}
\edef\hashmark{\string#}
\def\getfirst#1#2\getfirst{\string#1}
\newread\myread
\begin{document}
\openin\myread=mysrcfile.txt
  \newcount\linecount
  \global\linecount1
  \loop
    \unless\ifeof\myread
    \read\myread to \myinput
    \if\hashmark\expandafter\getfirst\detokenize\expandafter{\myinput}\getfirst                      
    \else
      \expandafter\myinput
    \fi
    \global\advance\linecount1
  \repeat
\closein\myread
\end{document}

The delimiter, instead of \relax is \getfirst which should not appear in the input file.

This requires e-TeX; without e-TeX you can change the category code of #:

\long\def\getfirst#1#2\getfirst{#1}
\openin\myread=mysrcfile.txt
  \newcount\linecount
  \global\linecount1
  \begingroup\catcode`#=12
  \loop
    \unless\ifeof\myread
    \read\myread to \myinput
    \if\hashmark\expandafter\getfirst\myinput\getfirst
    \else
      \expandafter\myinput
    \fi
    \global\advance\linecount1
  \repeat
  \endgroup
\closein\myread

The \long is to protect against empty lines in the input file.


A possible implementation with LaTeX3 macros; it's always quite hard to deal with #:

\documentclass{article}
\usepackage{xparse,l3str}
\ExplSyntaxOn
% the user level command
\NewDocumentCommand{\readremovingcomments}{m}
 {
  \penguin_read_nocomments:n { #1 }
 }
% the low level command
\cs_new_protected:Npn \penguin_read_nocomments:n #1
 {
  % open the input stream
  \ior_open:Nn \l_penguin_source_ior { #1 }
  % read one line at a time
  \ior_map_inline:Nn \l_penguin_source_ior
   {
    % extract the first token from the input line (after stringifying it)
    \tl_set:Nx \l__penguin_temp_tl { \tl_head:f { \tl_to_str:n { ##1 } } }
    % check whether the first token is #; if not, print the line
    \tl_if_eq:NNF \l__penguin_temp_tl \c_hash_str { ##1 }
   }
   \ior_close:N \l_penguin_source_ior
 }
\tl_new:N \l__penguin_temp_tl
\ior_new:N \l_penguin_source_ior
\ExplSyntaxOff

\begin{document}
\readremovingcomments{mysrcfile.txt}
\end{document}
5

You can test for a parameter token by setting up a macro such as

\let\parameter@token=\relax
\long\def\if@parameter@token@TF#1{%
  \ifcat\noexpand#1\parameter@token
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
}
\let\parameter@token=#%

where the key is that at the point of definition \parameter@token is not a #, but when used it is. That leads to something like

\documentclass{article}
\long\def\getfirst#1#2\relax{#1}
\makeatletter
\let\parameter@token=\relax
\long\def\if@parameter@token@TF#1{%
  \ifcat\noexpand#1\parameter@token
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
}
\let\parameter@token=#%
\begin{document}
\newread\myread
\openin\myread=mysrcfile.txt %
\loop
  \unless\ifeof\myread
    \read\myread to \myinput
    \expandafter\expandafter\expandafter\if@parameter@token@TF
      \expandafter\getfirst\myinput\relax
      {}
      {\myinput}%
  \repeat
\immediate\closeout\myread
\end{document}

On the other hand, you could just read with # as an 'other' character and avoid the issue, or indeed use \readline as e-TeX is in use.

1
  • Reading # with another character might be a very elegant solution to the problem, but although using LaTeX for quite a while now, I don't know how to do this in plain TeX. Could you point out a bit further on how to do this? Commented Dec 25, 2012 at 16:38

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .