省略宏内的空格

省略宏内的空格

我试图了解为什么我可以让 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 对其进行重新标记,从而修复了您的问题。这对于人们在另一个命令的参数中包装的问题非常有效。

相关内容