我很困惑为什么下面的 MWE 会产生任何黑色文本。代码如下,输出为:
但是,如果您注释掉该\IfStrEq
行,它就可以正常工作并且黑色文本会消失?
\documentclass{article}
\usepackage{xcolor}
\usepackage{xstring}
\usepackage{xparse}
\NewDocumentCommand{\GetTexFilePath}{m m m}{%
\IfStrEq{#1}{SpecialValue}{}{}% this should do nothing
%
../../../#1/#2/#3%
}%
\begin{document}
\def\FileWithPath{\GetTexFilePath{01}{02}{z}}%
\IfFileExists{\FileWithPath}{%
\textcolor{red}{File \FileWithPath\ found.}%
}{%
\textcolor{red}{File \FileWithPath\ not found.}%
}%
\end{document}
答案1
您的问题归结为\IfStrEq
无法扩展,但\IfFileExist
仍然试图扩展它。本质上,\IfFileExist
执行以下操作:(#1
是您的文件,此处\IfStrEq...
)
\openin\@inputcheck#1\relax % open the file
\ifeof\@inputcheck % did the file exist?
File exists.
\else
\@iffileonpath {#1} % search in \input@path
{File exists.}{File doesn't exist.}
\fi
TeX 基元\openin
将流号\@inputcheck
作为其第一个参数,然后尽可能多地扩展,抓取文件名的字符,直到遇到空格(它会删除空格)或不可扩展的命令。如果文件名#1
由字符组成,或者扩展为字符,则\openin
找到的文件名会在 处停止\relax
,并\IfFileExists
搜索正确的文件。但是,在您的情况下,\FileWithPath
TeX 会尽可能多地从左侧扩展,直到到达不可扩展的命令。在此阶段,没有遇到任何字符(如预期的那样:在实际开始进行测试\let
之前没有排版任何内容),因此找到一个空文件名。\IfStrEq
\openin
但整体\FileWithPath
仍然存在,只是稍微扩展了一点。如您所料,\IfStrEq
什么都没有排版,但路径的其余部分已排版。然后是测试\ifeof
:由于您的 TeX 发行版包含一个文件.tex
(您可以通过尝试找到它的路径\input\relax
),空文件\openin
已在流中打开该文件\@inputcheck
,因此\ifeof
测试为真。条件分支File found
被采用,让我们认为一切都进展顺利。
那么……如何解决这个问题?例如,使用可扩展的字符串比较。无论如何,您通过 加载的包expl3
(来自包)提供和应该适合您的需求(第一个在逐个字符比较它们之前像在 中一样扩展其参数;第二个不扩展)。要使用这样的“代码”命令,我们需要转到“Expl 语法”。l3kernel
xparse
\str_if_eq:xxTF
\str_if_eq:nnTF
\edef
\documentclass{article}
\usepackage{xcolor}
\usepackage{xparse}
\ExplSyntaxOn
\DeclareExpandableDocumentCommand{\GetTexFilePath}{m m m}
{
\str_if_eq:xxTF{#1}{SpecialValue}{}{}
../../../#1/#2/#3
}
\ExplSyntaxOff
\begin{document}
\def\FileWithPath{\GetTexFilePath{01}{02}{z}}%
\IfFileExists{\FileWithPath}{%
\textcolor{red}{File \FileWithPath\ found.}%
}{%
\textcolor{red}{File \FileWithPath\ not found.}%
}%
\end{document}
[编辑以替换\NewDocumentCommand
:\NewExpandableDocumentCommand
即使前者恰好在这种情况下起作用,但一般来说,它定义了一个不可扩展的命令,并且通常不会扩展,让您回到这个答案第一部分描述的扩展问题。][重新编辑:Peter Grill 提到不\NewExpandableDocumentCommand
存在。]
或者,您可以使用稍微奇怪的原语\pdfstrcmp
(或者\strcmp
在 XeTeX 中,或者在 LuaTeX 中,Heiko 提供的一些命令?),它接受两个参数(将它们扩展为\edef
),并根据字符串的字典顺序扩展为 -1、0 或 1,如果它们相等则为 0。
编辑:鉴于你的进一步问题,我认为更简单的方法是在定义时“计算”完整路径,\FileWithPath
而不是定义\FileWithPath
计算路径。为此,请替换\def\FileWithPath{...}
为对分配函数的调用\defTeXFilePath
,该函数执行一些检查(可扩展或不可扩展),然后决定在中存储什么\FileWithPath
。这里我选择了\tl_if_eq:nnTF
而不是\str_if_eq:xxTF
。区别:“tl”而不是“str”意味着我们正在比较标记及其字符代码和类别代码,而不是简单的字符串(我认为这是颜色与黑白);“n”而不是“x”意味着我们不执行扩展。请随意更改以满足您的需要。
\documentclass{article}
\usepackage{xcolor}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\defTeXFilePath}{m m m m}
{
\tl_if_eq:nnTF{#2}{SpecialValue}
{ \tl_set:Nn #1 { ../../../#2/#3/#4 } }
{ \tl_set:Nn #1 { ../../../#2/xxx#3/#4 } }
}
\ExplSyntaxOff
\begin{document}
\defTeXFilePath{\FileWithPath}{01}{02}{z}%
%\show\FileWithPath % see that \FileWithPath contains no macro call.
\IfFileExists{\FileWithPath}{%
\textcolor{red}{File \FileWithPath\ found.}%
}{%
\textcolor{red}{File \FileWithPath\ not found.}%
}%
\end{document}