我正在使用filecontents
包,并希望将其包含在宏中,以便分解一些复杂的处理。问题是,后面\begin{filecontents}
除了空格和实际的尾随换行符外什么都不用跟,不幸的是,TeX/LaTex 处理宏定义的方式吸收了换行符。这是 M(not)WE :
\documentclass{article}
\usepackage{filecontents}
\thispagestyle{empty}
\newcommand{\cf}[2]{%
% next-line-compulsorily-ends-only-with-spaces-then-newline
\begin{filecontents}{prefix-#1.tex}
factored-complex-processing-of{#2}
\end{filecontents}
}
\show\cf
%
\cf{suffix1}{somefirststuff}
%
\begin{filecontents}{prefix-suffix2.tex}
complex-processing-of{somenextstuff}
\end{filecontents}
\begin{document}
some-package-macro-processing-using-suffix-number{1,2}
Equivalent to:
\input{prefix-suffix1.tex}
\input{prefix-suffix2.tex}
\end{document}
\begin{filecontents}
由于吸收了行尾不存在的换行符\newcommand{\cf}
,因此 TeX/LaTeX 不会理解此命令并将其余部分处理为要输出的文本,这会导致错误Missing \begin{document}
。
我曾尝试寻找涉及\def
、、、等\edef
的变通方法,但没有找到任何有用的东西。\noexpand
\catcode
那么有没有办法将这个实际输入的换行符/行尾符保留在宏定义中?
答案1
\documentclass{article}
\usepackage{verbatim}
\usepackage{filecontents}
\thispagestyle{empty}
\newcommand\exchange[2]{#2#1}
% The integer-parameter \newlinechar usually has the value 10.
% This means if at the time of writing to text-file (La)TeX encounters a
% character whose code-point's number in (La)TeX's internal character-encoding
% (which either is ASCII or is UTF-8) is 10, it will not write that character
% but will write a linebreak. 10 denotes the Line Feed-character and using
% (La)TeX's ^^-notation you can also write it as ^^J.
\begingroup
\newcommand\cf[2]{%
\endgroup
\newcommand{\cf}[2]{%
\scantokens{%
#1{prefix-##1.tex}^^J%
factored-complex-processing-of{##2}^^J%
#2%
}%
}%
}%
\expandafter\exchange\expandafter{\expandafter{\string\end{filecontents}}}%
{\expandafter\cf\expandafter{\string\begin{filecontents}}}%
\cf{suffix1}{ somefirststuff\LaTeX}%
\begin{filecontents}{prefix-suffix2.tex}
factored-complex-processing-of{ somenextstuff\LaTeX}
\end{filecontents}
\begin{document}
\noindent prefix-suffix1.tex is:
\verbatiminput{prefix-suffix1.tex}
\noindent\hrulefill\null
\noindent prefix-suffix2.tex is:
\verbatiminput{prefix-suffix2.tex}
\noindent\hrulefill\null
Do you see the subtle difference?
In prefix-suffix1.tex there is a space behind the phrase \verb|\LaTeX|.
In prefix-suffix2.tex there is no space behind the phrase \verb|\LaTeX|.
The reason is:
prefix-suffix1.tex comes from the \verb|\cf|-command.
The tokens forming the second argument of \verb|\cf| were
tokenized under normal catcode-r\'egime. Thus the phrase \verb|\LaTeX| got
tokenized as control-word-token. When \verb|\scantokens| did its simulated
unexpanded-writing-part, that control-word-token got written unexpanded
with a trailing space as \LaTeX{} always writes control-word-tokens with
a trailing space.\\
Besides this, hashes of catcode 6 will be doubled at writing time also.
If you don't like such side-effects, you need to have \LaTeX{} read the
second argument of \verb|\cf| under verbatim-catcode-r\'egime. How to do
that is exhibited in the other example.
\noindent\hrulefill\null
\noindent\verb|\input{prefix-suffix1.tex}| yields:
\noindent\input{prefix-suffix1.tex}
\noindent\hrulefill\null
\noindent\verb|\input{prefix-suffix2.tex}| yields:
\noindent\input{prefix-suffix2.tex}
\end{document}
请注意\scantokens{<stuff>}
,由 e-TeX 扩展提供的 类似于
\newtoks\myscratchtoks
\newwrite\myscratchwrite
...
\immediate\openout\myscratchwrite temp.tex\relax
\myscratchtoks{<stuff>}%
\immediate\write\myscratchwrite{\the\myscratchtoks}%
\immediate\closeout\myscratchwrite
\input temp.tex %\@@@input with LaTeX 2e.
我说“类似”是因为它并不完全相同:\scantokens
它本身是可扩展的,而上面的代码片段则不可扩展。
例如,\scantokens
你可以做
\expandafter\def\expandafter\temp\scantokens{{definition text}}
当调整上述代码片段时,必须执行以下操作:
\newtoks\myscratchtoks
\newwrite\myscratchwrite
...
\immediate\openout\myscratchwrite temp.tex\relax
\myscratchtoks{{definition text}}%
\immediate\write\myscratchwrite{\the\myscratchtoks}%
\immediate\closeout\myscratchwrite
\expandafter\def\expandafter\temp\input temp.tex %\@@@input with LaTeX 2e.
这两种方法都会发生将标记未扩展写入/模拟未扩展写入文本文件的情况。
请注意,当发生标记的未扩展写入时,catcode 6 的字符标记将加倍,即,#
catcode 6 的哈希值将加倍。
请注意,当发生未扩展的标记写入时,控制字标记将带有尾随空格。
如果您不想要这些效果,您需要转变\cf
为一个在 verbatimized-catcode-conditions 下读取其内容的宏。如果这样做,\cf
则不得通过其他宏获取其提供的参数,而必须通过从 tex-input-file 读取和标记来获取它们。即,与 -command 相同的限制适用\verb
。
我可以提供一个例程\UDcollectverbarg
:
\documentclass{article}
\usepackage{verbatim}
\usepackage{filecontents}
\thispagestyle{empty}
\makeatletter
\newcommand\UD@firstofone[1]{#1}%
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@exchange[2]{#2#1}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
%%<-------------------- Code for \UDcollectverbarg -------------------->
%% Check whether argument is empty:
%%......................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
\UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%......................................................................
\begingroup
\catcode`\^^M=12 %
\UD@firstofone{%
\endgroup%
\newcommand\UDEndlreplace[2]{\romannumeral0\@UDEndlreplace{#2}#1^^M\relax{}}%
\newcommand*\@UDEndlreplace{}%
\long\def\@UDEndlreplace#1#2^^M#3\relax#4#5{%
\UD@CheckWhetherNull{#3}%
{ #5{#4#2}}{\@UDEndlreplace{#1}#3\relax{#4#2#1}{#5}}%
}%
}%
\newcommand\UDcollectverbarg[3]{%
\begingroup
\let\do\@makeother % <- this and the next line switch to
\dospecials % verbatim-category-code-régime.
\catcode`\{=1 % <- give opening curly brace the usual catcode so a
% curly-brace-balanced argument can be gathered in
% case of the first thing of the verbatimized-argument
% being a curly opening brace.
\catcode`\ =10 % <- give space and horizontal tab the usual catcode so \UD@collectverbarg
\catcode`\^^I=10 % cannot catch a space or a horizontal tab as its 4th undelimited argument.
% (Its 4th undelimited argument denotes the verbatim-
% syntax-delimiter in case of not gathering a
% curly-brace-nested argument.)
\kernel@ifnextchar\bgroup
{% seems a curly-brace-nested argument is to be caught:
\catcode`\}=2 % <- give closing curly brace the usual catcode also.
\UD@collectverbarg{#1}{#2}{#3}{}%
}{% seems an argument with verbatim-syntax-delimiter is to be caught:
\do\{% <- give opening curly brace the verbatim-catcode again.
\UD@collectverbarg{#1}{#2}{#3}%
}%
}%
\newcommand\UD@collectverbarg[4]{%
\do\ % <- Now that \UD@collectverbarg has the delimiter or
\do\^^I% emptiness in its 4th arg, give space and horizontal tab
% the verbatim-catcode again.
\do\^^M% <- Give the carriage-return-character the verbatim-catcode.
\long\def\@tempb##1#4{%
%\edef\@tempb{##1}%
\def\@tempb{##1}%
\@onelevel@sanitize\@tempb % <- Turn characters into their "12/other"-pendants.
% This may be important with things like the
% inputenc-package which may make characters
% active/which give them catcode 13(active).
\expandafter\UDEndlreplace\expandafter{\@tempb}{#1}{\def\@tempb}% <- this starts
% the loop for replacing endline-characters.
\expandafter\UD@@collectverbarg\expandafter{\@tempb}{#2}{#3}% <- this "spits
% out the result.
}%
\@tempb
}%
\newcommand\UD@@collectverbarg[3]{%
\endgroup
#2{#3{#1}}%
}%
%%<---------------- End of code for \UDcollectverbarg ----------------->
% The integer-parameter \newlinechar usually has the value 10.
% This means if at the time of writing to text-file (La)TeX encounters a
% character whose code-point's number in (La)TeX's internal character-encoding
% (which either is ASCII or is UTF-8) is 10, it will not write that character
% but will write a linebreak. 10 denotes the Line Feed-character and using
% (La)TeX's ^^-notation you can also write it as ^^J.
\newcommand{\cf}[1]{%
\UDcollectverbarg{^^J}{\UD@firstofone}{\innercf{#1}}%
}%
\begingroup
\newcommand\innercf[2]{%
\endgroup
\newcommand\innercf[2]{%
\scantokens{%
#1{prefix-##1.tex}^^J%
factored-complex-processing-of{##2}^^J%
#2%
}%
}%
}%
\UDcollectverbarg{^^J}{%
\UDcollectverbarg{^^J}{\UD@firstofone}%
}{\innercf}|\begin{filecontents}||\end{filecontents}|%
\cf{suffix1}{ somefirststuff\string#\LaTeX}%
\begin{filecontents}{prefix-suffix2.tex}
factored-complex-processing-of{ somenextstuff\string#\LaTeX}
\end{filecontents}
\begin{document}
\noindent prefix-suffix1.tex is:
\verbatiminput{prefix-suffix1.tex}
\noindent\hrulefill\null
\noindent prefix-suffix2.tex is:
\verbatiminput{prefix-suffix2.tex}
\noindent\hrulefill\null
\noindent\verb|\input{prefix-suffix1.tex}| yields:
\noindent\input{prefix-suffix1.tex}
\noindent\hrulefill\null
\noindent\verb|\input{prefix-suffix2.tex}| yields:
\noindent\input{prefix-suffix2.tex}
\end{document}
如果您希望在将\cf
第二个参数写入文件之前对其进行完全评估/扩展,您可以应用\protected@edef
并\@onelevel@sanitize
:
\documentclass{article}
\usepackage{verbatim}
\usepackage{filecontents}
\thispagestyle{empty}
\newcommand\exchange[2]{#2#1}
% The integer-parameter \newlinechar usually has the value 10.
% This means if at the time of writing to text-file (La)TeX encounters a
% character whose code-point's number in (La)TeX's internal character-encoding
% (which either is ASCII or is UTF-8) is 10, it will not write that character
% but will write a linebreak. 10 denotes the Line Feed-character and using
% (La)TeX's ^^-notation you can also write it as ^^J.
\newenvironment{localscope}{}{}%
\begingroup
\makeatletter
\newcommand\cf[2]{%
\endgroup
\newcommand{\cf}[2]{%
\begin{localscope}%
\protected@edef\UD@tempa{##2}%
\@onelevel@sanitize\UD@tempa
\expandafter\exchange\expandafter{%
\expandafter\scantokens\expandafter{%
\romannumeral0\expandafter\exchange\expandafter{\expandafter{\UD@tempa}}{ %
#1{prefix-##1.tex}^^J%
factored-complex-processing-of}^^J%
#2%
}}%
{\end{localscope}}%
}%
}%
\expandafter\exchange\expandafter{\expandafter{\string\end{filecontents}}}%
{\expandafter\cf\expandafter{\string\begin{filecontents}}}%
\newcommand\firststage{\secondstage}
\newcommand\secondstage{\thirdstage}
\newcommand\thirdstage{Expanded. \LaTeX}
\cf{suffix1}{ somefirststuff \firststage}%
\begin{filecontents}{prefix-suffix2.tex}
factored-complex-processing-of{ somenextstuff \firststage}
\end{filecontents}
\begin{document}
\noindent prefix-suffix1.tex is:
\verbatiminput{prefix-suffix1.tex}
\noindent\hrulefill\null
\noindent prefix-suffix2.tex is:
\verbatiminput{prefix-suffix2.tex}
\noindent\hrulefill\null
\noindent\verb|\input{prefix-suffix1.tex}| yields:
\noindent\input{prefix-suffix1.tex}
\noindent\hrulefill\null
\noindent\verb|\input{prefix-suffix2.tex}| yields:
\noindent\input{prefix-suffix2.tex}
\end{document}