Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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?

share|improve this question
    
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

2 Answers 2

up vote 6 down vote accepted

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}
share|improve this answer

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.

share|improve this answer
    
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

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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