我试图了解为什么我可以让 LaTeX 忽略文本中的空格,但是当我在宏中执行同样的事情时,它却无法忽略这些空格。
我使用 \catcode32=9 来忽略 ASCII 空格字符。(我也尝试过 \ignorespaces,但效果更差。)以下 MWE 进行了说明(至少这是我能得到的最简化的,尽管我留下了几个 \typeout 来帮助直观地了解正在发生的事情,以及一堆注释)。
\documentclass{report}
%==========gll=========
%Introduce 2-line text-and-gloss:
\gdef\gll#1
{\bgroup
\catcode`\^^M=12
\onesent#1
}
{\catcode`\^^M=12 \endlinechar=-1 % 12 = other
% Within this block (down to the comment "restore \catcode`\^^M"),
% newline (^^M) is treated as a matchable char for TeX's regex matching.
%==========getwords=========
%Recursively tokenize the input line, outputting each token in an \hbox
% Args:
% #1=1st token of line, e.g. {\textbf{AAA}}
% #2=remainder of line, i.e. remaining tokens; empty on final pass
\gdef\getwords#1 #2^^M
{\typeout{1=#1}%Debug:
\typeout{2=#2}%Debug:
\hbox{\catcode32=9X#1X}
\def\more{#2}
\ifx\more\empty\let\more=\donewords
\else\let\more=\getwords
\fi
\more#2^^M
}
%==========donewords=========
%End the recursion
\gdef\donewords#1^^M{}%
%==========twosent=========
%Introduce a space char before the newline
\gdef\onesent#1^^M%
{\typeout{onesent,1=#1}
\getwords#1 ^^M%
\egroup % matches \bgroup in \gloss
}
} % restore \catcode`\^^M
\begin{document}
Use \textbackslash{}catcode to ignore spaces:
I{\hbox{\catcode32=9\textbf{ AAA } \textbf{ \textit{ aaa }}}}I
===============
Try same thing inside \textbackslash{}gll; first line has no space in
source text, while second has leading, intenal and trailing spaces; Xs
added on left and right for clarity:
\gll \textbf{AAA} \textbf{ AA A }
\end{document}
当我在文本中执行此操作时,结果看起来像 XAAAX,即 \catcode32=9 按照我的预期工作。但是当我在宏中执行此操作时,结果看起来像 X AA AX,即 catcode 什么也不做。
动机:上述代码是 Covington 宏中用于注释行间文本的简化版本。空格会阻止输出中的正确对齐。当然,一种解决方案是消除源代码中的杂散空格,但由于各种原因,很难执行此操作 :-)。我以为编程解决方案肯定很容易。错了。
实际上,我确实有一个程序化的解决方案,它依赖于使用
\spaceskip 1sp
(如本网站另一个问答中所建议的)代替 \catcode 方法。这有效,但 ragged2e 包通过重新定义它而破坏了它
\spaceskip 0pt
每次设置字体时 - 我们都会重新设置行间内的字体(例如,将某些内容加粗)。
在 2016 TeXLive 版本中使用 pdfLatex 进行了测试(早期版本使用 xelatex 进行了测试)。
答案1
Catcode 在 LaTeX 中的工作方式相当混乱。您遇到的问题是,如果您定义一个宏:
\def\test#1{
\catcode` =9\relax
% now do some stuff
}
当你调用时,参数会被解析,并且在扩展时以及在评估任何代码之前,\test{argument}
catcode 会被分配给参数的标记。\test
\test
\catcode` =9\relax
\test
在 catcodes 已经分配给参数中的标记之后,就会发生inside 。
这是在宏中使用某些环境时出现的问题的根源。例如,tikz-cd 将其变成&
活动字符,因此如果您说在扩展时将解析参数\footnote{\begin{tikzcd}A & B\end{tikzcd}}
,并为其分配 catcode“对齐字符”,然后当将 catcode 更改为“活动”时,就太晚了。\footnote
\footnote
&
\begin{tikzcd}
&
有几种可能的解决方案。不太可靠的解决方案可能适合您的情况,即定义一个不带参数的包装器命令,启动一个组并更改 catcode,然后调用带参数的内部命令。此内部命令的主体可以立即结束该组,因为当我们到达那里时,参数已经分配了它们的 catcode,因此 catcode 无论如何都无关紧要:
\gdef\gll{
\bgroup
\catcode`\^^M=12\relax
\catcode`\ =9\relax % This relax is really important
\onesent
}
\gdef\onesent#1^^M{%
\egroup
% Do stuff
}
请注意关键的\relax
。另一个问题是,如果没有它,这种方法就会失败。这是因为当 TeX 寻找数值时,它会不断扫描、扩展宏,直到看到一个它绝对不能合并到数字中的标记。因此,如果没有 ,\relax
它会扩展\getwords@
以检查它是否将数字作为第一个要合并到 catcode 中的字符。但是,这种扩展会导致参数的解析,并且发生在 catcode 分配实际发生之前。
另一种选择是使用\scantokens
:
\gdef\getwords#1 #2^^M{%
\bgroup
\catcode`\ =9\relax
\scantokens{\hbox{X#1X}}%
\egroup
% Do some other stuff
}
\scantokens
\getwords
是一个 eTeX 原语,它对其参数进行去标记化,并使用当前的 catcode 对其进行重新标记,从而修复了您的问题。这对于人们在另一个命令的参数中包装的问题非常有效。