当我使用“\def”时出现“!未定义的控制序列。”

当我使用“\def”时出现“!未定义的控制序列。”

以下是 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,就会打印该行。

enter image description here

答案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

相关内容