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?

  • 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 Dec 25 '12 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. – Nicholas Hamilton Dec 25 '12 at 12:34
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.

  • 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? – Penguin Nurse Dec 25 '12 at 16:38

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.