Here is a MWE:
\documentclass{extbook}\usepackage{xspace}\usepackage{xstring}\usepackage{ifthen}\newcommand\foo[1]{%\def\tempfoo{#1}%#1%}\newcommand\separation[1]{%[\the\numexpr #1\relax ]%}\immediate\newread\myfile\newboolean{isdisplaying}\newcommand\displayfile[1]{\edef\todisplay{\separation{#1} }\edef\aftertodisplay{\separation{#1+1} }\typeout{\todisplay}\typeout{\aftertodisplay}\setboolean{isdisplaying}{false}\immediate\openin\myfile=myfile.tex\loop\unless\ifeof\myfile\immediate\read\myfile to\fileline\ifthenelse{\equal{\aftertodisplay}{\fileline}}{\setboolean{isdisplaying}{false}}{}\ifthenelse{\boolean{isdisplaying}}{\fileline}{}\ifthenelse{\equal{\todisplay}{\fileline}}{\setboolean{isdisplaying}{true}}{}\repeat\immediate\closein\myfile}\begin{document}\displayfile{2}\end{document}Here is the content ofmyfile.tex:
[1]foo[2]\foo{bar}[3]bazIt causes this error:
Undefined control sequence.\foo #1->\def \tempfoo {#1}#1l.39 \displayfile{2}because of the\foo command and the\def\tempfoo{#1} line.
As @DavidCarliste said, it can be caused by a context of expansion:https://tex.stackexchange.com/a/667865/237709
But I don't understand here.
EDIT:
SO, I suppose it is the test\equal{\aftertodisplay}{\fileline} that causes the error.
[edit: that is bullshits i have written: Maybe \equal prevent the expansion of \fileline? How can I force the expansion of \fileline in this situation?]
EDIT (2):But, in my situation, the test:
\ifthenelse{\equal{\todisplay}{\protect\fileline}}is always false, while it was true with:
\ifthenelse{\equal{\todisplay}{\fileline}}However, in my example,\fileline and\protect\fileline have to give the same[2]?
In my example, \displayfile{2} with\protect\fileline produces 0 pages...
EDIT (3):
a test with
\typeout{\fileline}\typeout{\expandafter\protect\fileline}gives an interesting output:
...[2] [2] bar \foo{bar} \par \par...EDIT (4):
I just don't understand why\protect\fileline, which displays[2], is not equal with\todisplay, while\fileline, which displays (apparently) the same[2], is equal.
EDIT (5): (@egreg answer)I can't explain why\unexpanded\expandafter{\fileline}, instead of\protect\fileline, works in the test\ifthenelse.
- 1
\ifthenelse{\equal{\aftertodisplay}{\fileline}}does edef of each argument then compares with\ifxbut the edef fails here as you can not use \foo in an edef you need \protect\fooDavid Carlisle– David Carlisle2022-12-08 16:30:27 +00:00CommentedDec 8, 2022 at 16:30 - 1You do not want to force the expansion of \fileline, you want to prevent it expanding as it contains tokens such as \foo which are not safe in an expansion contextDavid Carlisle– David Carlisle2022-12-08 16:32:33 +00:00CommentedDec 8, 2022 at 16:32
- So, yes, with
\ifthenelse{\equal{\aftertodisplay}{\protect\fileline}it works. But why "edef" needs "protect", David?Pierre Dufays– Pierre Dufays2022-12-08 16:32:46 +00:00CommentedDec 8, 2022 at 16:32 - 1well ifthenelse uses
\protected@edefwhich defines\protectas\noexpand\protect\noexpandso\protect\foois\noexpand\protect\noexpand\foowhich expands to\protect\foothen the edef moves on...David Carlisle– David Carlisle2022-12-08 16:36:31 +00:00CommentedDec 8, 2022 at 16:36 - 1I did write ifthenelse, you shouldn't beso surprised I have an idea about what it does:-)David Carlisle– David Carlisle2022-12-08 16:39:06 +00:00CommentedDec 8, 2022 at 16:39
2 Answers2
The problem is that\ifthenelse does full expansion, and with\edef{\def\tempfoo{#1}} you get almost surely an error, whether or not\tempfoo is defined.
You can stop full expansion by using\protect\fileline or
\unexpanded\expandafter{\fileline}Remove all those\immediate tokens that do nothing at all.
But there are far better methods nowadays.
\begin{filecontents*}{\jobname-data.tex}[1]foo[2]\foo{bar}[3]baz\end{filecontents*}\begin{filecontents*}{\jobname-data2.tex}-1-foo-2-\foo{bar}-3-baz\end{filecontents*}\documentclass{article}\ExplSyntaxOn%% user interface\NewDocumentCommand{\displayfile}{omm} { % #1 = separation format % #2 = filename % #3 = part to display \dufays_displayfile:nnn { #1 } { #2 } { #3 } }%% variables\ior_new:N \g_dufays_displayfile_ior\str_new:N \l__dufays_displayfile_before_str\str_new:N \l__dufays_displayfile_after_str\bool_new:N \l__dufays_displayfile_show_bool%% private functions and needed variants\cs_new:Nn \__dufays_displayfile_sep:n { }\cs_generate_variant:Nn \__dufays_displayfile_sep:n { e }%% public functions\cs_new_protected:Nn \dufays_displayfile:nnn { % if there's no optional argument, use a default separator format \tl_if_novalue:nTF { #1 } { \cs_set:Nn \__dufays_displayfile_sep:n { [##1] } } { \cs_set:Nn \__dufays_displayfile_sep:n { #1 } } \str_set:Nx \l__dufays_displayfile_before_str { \__dufays_displayfile_sep:n { #3 } ~ } \str_set:Nx \l__dufays_displayfile_after_str { \__dufays_displayfile_sep:e { \int_eval:n { #3+1 } } ~ } \ior_open:Nn \g_dufays_displayfile_ior { #2 } \bool_set_false:N \l__dufays_displayfile_show_bool \ior_map_inline:Nn \g_dufays_displayfile_ior { \str_if_eq:VnTF \l__dufays_displayfile_before_str { ##1 } {% OK, next we print \bool_set_true:N \l__dufays_displayfile_show_bool } {% print or set false \str_if_eq:VnTF \l__dufays_displayfile_after_str { ##1 } { \bool_set_false:N \l__dufays_displayfile_show_bool } { \bool_if:NT \l__dufays_displayfile_show_bool { ##1 } } } } \ior_close:N \g_dufays_displayfile_ior }\ExplSyntaxOff\newcommand\foo[1]{% \def\tempfoo{#1}% #1%}\begin{document}\displayfile{\jobname-data}{2}This is \verb|\tempfoo|: \tempfoo\displayfile[-#1-]{\jobname-data2}{3}\end{document}We can set the separator on the spots and of course input whatever file we please.
The\ior_map_inline:Nn function reads the file line by line. If the line equals the stated starting point, we set theshow boolean to true. If the line equals the end point, theshow boolean is set to false. In all other cases, the line is printed provided the boolean is true.
- Your mastery of expl3 is very impressive. I have, indeed, to learn this new methods. Very impressive.Pierre Dufays– Pierre Dufays2022-12-08 17:52:31 +00:00CommentedDec 8, 2022 at 17:52
- In my case,
\unexpanded\expandafter{\fileline}works with the testifthenelse. I do not understand whyprotect filelinedoes not the same, but... Thank you @egreg (and David alos of course).Pierre Dufays– Pierre Dufays2022-12-08 17:56:06 +00:00CommentedDec 8, 2022 at 17:56
If I understand what is your intend, you want to read and process only a part of the file after[num] label and before empty line when the\displayfile{num} macro is used. The\displayfile macro can be definded as follows:
\expandafter\let \expandafter\primitiveinput \csname @@input\endcsname\def\displayfile#1{% \everyeof={\par} \long\def\scanfile ##1[#1]##2\par{##2\endinput} \expandafter\scanfile\primitiveinput myfile \everyeof={}}\def\foo#1{\def\tempfoo{#1}#1}Test:\displayfile{1}\displayfile{3}\displayfile{2}Note that the first line\expandafter\let ... followed by usage\primitiveinput (instead\input) is needed only when you are using LaTeX, because (unfortunately) LaTeX redefines\input primitive.
- Thank you @wipet. I have to test your (I'm sure) great proposal to understand it and improve my (little) knowledge of TeX.Pierre Dufays– Pierre Dufays2022-12-09 14:24:14 +00:00CommentedDec 9, 2022 at 14:24
You mustlog in to answer this question.
Explore related questions
See similar questions with these tags.



