我无法追踪一个错误,它看起来像是缺少输入错误,但发生在一个奇怪的地方。我试图获取一个带有下划线的去标记字符串(实际上是我实际文档中的一个宏),并将去标记的下划线替换为“取消的”下划线(即:)\_
。我将结果传递给\Result
,然后尝试打印它以检查它是否有效。它没有。事实上,错误是第 13 行,而不是初始宏所在的第 9 行。
但是,如果我\_
用任何其他非控制字符替换,则不会收到任何错误。有人能解释为什么我会遇到这个错误以及如何修复它吗?
错误:
1 Tutorials/Example1/Example1.tex|13 error| Missing number, treated as zero.
2 Tutorials/Example1/Example1.tex|13 error| Missing number, treated as zero.
例子:
\documentclass{minimal} % EDIT: From answer, minimal shouldn't be used.
\usepackage[abspath]{currfile}
\usepackage{xstring}
%%% Set a fake return.
\def\theabsdir{\detokenize{/Users/Me/Example_with_underscore}}
\StrSubstitute[0]{\theabsdir}{\detokenize{_}}{\_}[\Result] % LINE 9
\begin{document}
Before: \theabsdir\\
After: \Result % LINE 13
\end{document}
这是预期的用法,但由于各种原因,它不起作用。本质上是伪代码。
\documentclass{article}
\usepackage[abspath]{currfile}
\usepackage{xstring}
% Determine path of current file
\getabspath{Example.tex} % this provides \theabsdir, the file's directory.
% Delete folders up to the project's root directory.
\StrDel{\theabsdir}{path/to/project_root/}[\ProjectRoot]
% Add the location of custom packages
\def\StylePath{\ProjectRoot style_name}
% Load custom package
\usepackage[options]{\StylePath}
%%% End Preamble
答案1
欢迎来到 TeX.SE!
首先,您不应该使用该类minimal
;它不是为最终用户设计的,仅供 LaTeX 开发人员进行特定测试。
\usepackage[T1]{fontenc}
其次,如果您添加到文档中,问题就会消失。T1
比更现代OT1
,这可能是您想要做的。
这里使用时的问题OT1
是由于在内部使用它时\_
不会保持不变1\edef
,根据文档,当您在默认机制中xstring
调用时,这种情况会在后台发生(请参阅\StrSubstitute
\fullexpandarg
论据的扩展在xstring
文档中)。
不过,我怀疑该文档并未说明全部事实,否则以下内容即使在 中也应该有效OT1
:
\StrSubstitute[0]{\theabsdir}{\detokenize{_}}{\noexpand\_}[\Result]
也
\StrSubstitute[0]{\theabsdir}{\detokenize{_}}{\unexpanded{\_}}[\Result]
您可以使用以下测试文档确信\_
编码OT1
不会存在:\edef
\documentclass{article}
\begin{document}
\edef\x{\_}
\x
\end{document}
打印结果为:
./test.tex:5: Missing number, treated as zero.
<to be read again>
\let
l.5 \end{document}
有了\usepackage[T1]{fontenc}
,它就能很好地运行。
避免在OT1
继续使用时出现错误的一种方法xstring
是将其包装\_
在\protected
宏内——这样的宏永远不会被\edef
、\write
等扩展:
\documentclass{article}
\usepackage{xstring}
\def\theabsdir{\detokenize{/Users/Me/Example_with_underscore}}
\protected\def\zzz{\_}
\StrSubstitute[0]{\theabsdir}{\detokenize{_}}{\zzz}[\Result]
\begin{document}
Before: \theabsdir\par
After: \Result
\end{document}
但这可能不是您所希望的。解决此问题的可靠方法是使用expl3
函数。我建议使用略有不同的输入,我认为这对您的实际用例更有用。以下代码的输入可以是:
% Note: no \detokenize here
\def\theabsdir{/Users/Me/Example_with_underscore}
...
\sanitizeMacro{\theabsdir}{\Result}
或者
\sanitizeString{/Users/Me/Example_with_underscore}{\Result}
\def\theabsdir{\detokenize{/Users/Me/Example_with_underscore}}
(但如果您确实愿意,我可以为您处理)。
以下是代码:
\documentclass{article}
% Probably a good idea to load this, even if it works without.
%\usepackage[T1]{fontenc}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new_protected:Npn \lagix_sanitize:nN #1#2
{
\tl_set:Nx \l_tmpa_tl { \tl_to_str:n {#1} } % \tl_to_str:n is \detokenize
\tl_replace_all:Nxn \l_tmpa_tl { \char_generate:nn { `_ } { 12 } } { \_ }
% Alternative to the previous line:
% \regex_replace_all:nnN { \cD\_ } { \c{_} } \l_tmpa_tl
\tl_set_eq:NN #2 \l_tmpa_tl
}
\cs_generate_variant:Nn \lagix_sanitize:nN { V }
% Not needed if you use the \regex_replace_all:nnN way above:
\cs_generate_variant:Nn \tl_replace_all:Nnn { Nx }
\NewDocumentCommand \sanitizeString { m m }
{
\lagix_sanitize:nN {#1} #2
}
\NewDocumentCommand \sanitizeMacro { m m }
{
\lagix_sanitize:VN #1 #2
}
\ExplSyntaxOff
% Note: no \detokenize here
\def\theabsdir{/Users/Me/Example_with_underscore}
\begin{document}
\sanitizeMacro{\theabsdir}{\Resulti}
\sanitizeString{/Users/Me/Example_with_underscore}{\Resultii}
Before: \detokenize{/Users/Me/Example_with_underscore}\par
After: \Resulti, \Resultii.
\end{document}
脚注
- 在的时候会进行各种测试,
\edef
并且这些测试的结果会在 记录的扩展中被硬编码\edef
,因为每个测试都会丢弃一个分支(假设测试本身可以执行,但如果扩展得太早就不一定了!)。
答案2
问题是将对xstring
参数执行“全程扩展”,\StrSubstitute
并且\_
无法在此过程中存活。
\documentclass{article}
\usepackage[abspath]{currfile}
\usepackage{xstring}
%%% Set a fake return.
\def\theabsdir{\detokenize{/Users/Me/Example_with_underscore}}
\StrSubstitute[0]{\theabsdir}{\detokenize{_}}{\noexpand\noexpand\noexpand\_}[\Result]
\begin{document}
Before: \theabsdir
After: \Result
\end{document}