以下是 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}
以下是 的内容myfile.tex
:
[1]
foo
[2]
\foo{bar}
[3]
baz
它会导致这个错误:
Undefined control sequence.
\foo #1->\def \tempfoo
{#1}#1
l.39 \displayfile{2}
因为\foo
命令和\def\tempfoo{#1}
线路。
正如@DavidCarliste 所说,这可能是由扩展上下文引起的:https://tex.stackexchange.com/a/667865/237709
但我在这里不明白。
编辑:
所以,我认为是测试\equal{\aftertodisplay}{\fileline}
导致了错误。
[编辑:我写的都是废话:也许 \equal 会阻止 \fileline 的扩展?在这种情况下,如何强制扩展 \fileline?
编辑(2): 但是,就我的情况而言,测试:
\ifthenelse{\equal{\todisplay}{\protect\fileline}}
总是错误的,而以下情况则为真:
\ifthenelse{\equal{\todisplay}{\fileline}}
\fileline
但是,在我的例子中,\protect\fileline
必须给出相同的[2]
吗?
在我的例子中,\displayfile{2}
产生\protect\fileline
了 0 页...
编辑(3):
测试
\typeout{\fileline}
\typeout{\expandafter\protect\fileline}
给出了一个有趣的输出:
...
[2]
[2]
bar
\foo{bar}
\par
\par
...
编辑(4):
我只是不明白为什么\protect\fileline
显示的[2]
不等于\todisplay
,而\fileline
显示的(显然)相同的[2]
却相等。
编辑(5):(@egreg 回答) 我无法解释为什么在测试中\unexpanded\expandafter{\fileline}
而不是起作用。\protect\fileline
\ifthenelse
答案1
问题在于\ifthenelse
完全扩展后,\edef{\def\tempfoo{#1}}
几乎肯定会出现错误,无论是否\tempfoo
定义。
您可以使用\protect\fileline
或停止完全扩展
\unexpanded\expandafter{\fileline}
删除所有那些\immediate
没有任何作用的标记。
但现在有了更好的方法。
\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}
我们可以在斑点上设置分隔符,当然也可以输入我们喜欢的任何文件。
该\ior_map_inline:Nn
函数逐行读取文件。如果该行等于指定的起点,我们将show
布尔值设置为 true。如果该行等于终点,则将布尔show
值设置为 false。在所有其他情况下,只要布尔值为 true,就会打印该行。
答案2
如果我理解您的意图,那么当使用宏时,您只想读取和处理文件[num]
标签之后和空行之前的部分。宏可以定义如下:\displayfile{num}
\displayfile
\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}
请注意,仅在使用 LaTeX 时才需要第一行\expandafter\let ...
后跟用法\primitiveinput
(而不是),因为(不幸的是)LaTeX 重新定义了原始。\input
\input