输入 \_ 时,StrSubstitute 缺失数字被视为 0

输入 \_ 时,StrSubstitute 缺失数字被视为 0

我无法追踪一个错误,它看起来像是缺少输入错误,但发生在一个奇怪的地方。我试图获取一个带有下划线的去标记字符串(实际上是我实际文档中的一个宏),并将去标记的下划线替换为“取消的”下划线(即:)\_。我将结果传递给\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}

在此处输入图片描述


脚注

  1. 在的时候会进行各种测试,\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}

相关内容