我试图了解TAB
键盘字符在逐字内的行为方式。我使用v
的参数定义了一个新命令xparse
,但是,我得到了错误和一个相当奇怪的输出。软件包文档没有提到这个特定字符(如果……我知道它不应该被使用,但是……)。
这是我的示例文件,具有真实tab
字符(感谢@Ulrich 提供的提示):
\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand{\myxverb}{v}{#1}
\pagestyle{empty}
\begin{document}
%This is \verb*|verb with tbars and two keyboard TABs \t \t in argument| text, OK.\par
This is \verb*|verb with tbars and two keyboard TABs in argument| text, OK.\par
%This is \myxverb|myxverb with tbars and two keyboard TABs \t \t in argument| text, NOT OK.\par
This is \myxverb|myxverb with tbars and two keyboard TABs in argument| text, NOT OK.\par
%This is \myxverb{myxverb with brace and two keyboard TABs \t \t in argument} text, NOT OK.
This is \myxverb{myxverb with brace and two keyboard TABs in argument} text, NOT OK.
\end{document}
输出图像:
使用TeXLive 2019(更新).log
生成的文件 的一部分:pdflatex
! LaTeX3 Error: The verbatim command '\myxverb' cannot be used inside an
(LaTeX3) argument.
For immediate help type H <return>.
...
l.10 ...|myxverb with tbars and two keyboard TABs
in argument| text, NOT OK...
?
! LaTeX3 Error: The verbatim command '\myxverb' cannot be used inside an
(LaTeX3) argument.
For immediate help type H <return>.
...
l.13 ...{myxverb with brace and two keyboard TABs
in argument} text, NOT OK.
?
! Too many }'s.
l.13 ... brace and two keyboard TABs in argument}
text, NOT OK.
?
有什么办法可以避免这个问题吗?
问候
答案1
TeX 中如何处理水平制表符(LaTeX 中也是如此):
水平制表符在 ASCII 编码和许多其他字符编码中具有代码点编号 9。
在 TeX 中,您可以使用^^
-notation 将水平制表符也写为^^I
。(“I” 是拉丁字母表中的第九个字母。)当然,在类别代码制度发生变化的情况下,这是不可能的,其中 不是^
类别代码 7(上标)。当命令的参数从 .tex-input-file 中读取并标记时,就会出现这种情况\verb
。
通常,水平制表符在 TeX 中的类别代码为 10(空格)。
在 TeX 中,任何类别代码为 10(空格)的输入字符都将被标记为显式空格标记,即字符代码为 32 和类别为 10(空格)的显式字符标记。
因此,如果 .tex 输入文件中通常类别代码制度下的 TeX 遇到水平制表符,它将在读取设备处于状态 M(行中间)的情况下在标记流中插入一个显式空格标记,即字符代码为 32 且类别为 10(空格)的显式字符标记。
在 LaTeX 中使用 -command 时\verb
,类别代码机制会在 LaTeX 从 .tex-input-file 读取并标记该\verb
-command 的参数之前发生变化。内部这是通过命令完成的\dospecials
。该命令扩展为元素具有模式的列表
\do⟨control-symbol-token whose name is formed by the
character whose category-code is to be changed⟩
最小示例
\show\dospecials\stop
揭示了:
> \dospecials=macro:
->\do \ \do \\\do \{\do \}\do \$\do \&\do \#\do \^\do \_\do \%\do \~.
使用 LaTeX 2e 内核的\verb
-command 和verbatim
-environment,命令\do
依次(通过)在执行时\let
等于。\@makeother
\dospecials
因此您可以看到\dospecials
,例如,更改了空格的类别代码(\do \
),但并没有更改水平制表符的类别代码。(应该是这样的\do \^^I
)。
这意味着:
\verb
当/的参数\verb*
被标记化时(即从 .tex 输入文件中读取以决定将哪些标记插入到标记流中),水平制表符的类别代码不会改变,因此仍然是 10(空格)。因此,即使在逐字分类代码制度中,水平制表符也会产生明确的空格标记= 类别 10(空格)和字符代码 32 的显式字符标记。在水平模式下,空格标记产生水平粘合。
(这是用 大大缩短的故事\verb
。)
但为什么与解析的逐字论证故事?
我调查了xparse.dtx(2019-05-28)并搜索短语“动词”。
我发现了两段我觉得很有趣的段落:
% \begin{macro}{\@@_grab_v_aux_test:N} % \begin{macro} % { % \@@_grab_v_aux_loop:N, % \@@_grab_v_aux_loop:NN, % \@@_grab_v_aux_loop_end: % } % Check that the opening delimiter is a character, setup category codes, % then start reading tokens one by one, keeping the delimiter as an argument. % If the verbatim was not nested, we will be grabbing one character % at each step. Unfortunately, it can happen that what follows the % verbatim argument is already tokenized. Thus, we check at each step % that the next token is indeed a \enquote{nice} % character, \emph{i.e.}, is not a character with % category code $1$ (begin-group), $2$ (end-group) % or $6$ (macro parameter), nor the space character, % with category code~$10$ and character code~$32$, % nor a control sequence.
在这里你可以了解到xparse 读取逐字参数的机制审查这些论点,并抛出错误消息研究表明该参数包含例如明确的空间标记。
% \begin{macro}{\@@_grab_v_aux_catcodes:} % \begin{macro}{\@@_grab_v_aux_abort:n} % In a standalone format, the list of special characters is kept % as a sequence, \cs{c_@@_special_chars_seq}, and we use % \tn{dospecials} in package mode.
在这里你可以了解到解析的逐字读音论证机制也用于\dospecials
调整类别代码制度。
您已经知道,\dospecials
水平制表符的类别代码保持不变,这意味着该字符仍然具有类别代码 10(空格),这意味着它的标记化将(如果阅读设备处于状态 M(行中间))产生一个明确的空格标记,即类别 10(空格)和字符代码 32 的明确字符标记。
概要:
水平制表符被标记为空格标记。在 xparse 逐字读取参数的机制检查期间,这些空格标记会产生错误消息。
关于这个还能做什么?
LaTeX 2e 内核逐字读取参数的机制和解析逐字读取参数的机制保持水平制表符的类别代码不变。
因此,使用这两种机制,以某种方式更改水平制表符的类别代码应该不会出现问题,因为对该字符进行标记不会产生空格标记,而是产生例如显式活动水平制表符标记。
如果这样做,那么在处理逐字参数时,必须定义活动的水平制表符标记来提供您想要的任何标记,例如,用于可视化水平制表符。
在下面的例子中,我尽力使水平制表符产生水平制表键符号,即由 html 实体表示的符号 ⭾ ⭾
,该符号印在许多键盘的制表键上。
我为传递该符号创建的宏是\mytabkeysymbol
。使用此宏,我使用\scalebox
和\resizebox
我图形-package 以获得一个尺寸不超过W
当前字体中字母尺寸的盒子。
这种获取符号的方法远非完美。但对于拥有宏\mytabkeysymbol
并展示可以完成自己的 catcode 欺骗来说,这已经足够了。
\documentclass{article}
\oddsidemargin=1cm
\addtolength\oddsidemargin{-1in}%
\addtolength\oddsidemargin{-\hoffset}%
\evensidemargin=\oddsidemargin
\textwidth=\paperwidth
\addtolength\textwidth{-2cm}%
\parindent=0ex
\usepackage{xparse}
%..........................................................
% This is just some code for producing a horizontal-tab-
% key-symbol:
\usepackage{graphicx}
\newbox\myscratchboxa
\newbox\myscratchboxb
\newlength\myscratchlength
\newcommand\mytabkeysymbol{%
\begingroup\setbox\myscratchboxa=\hbox{W}%
\hbox to\wd\myscratchboxa{\hfill%
\resizebox{.8\wd\myscratchboxa}{\ht\myscratchboxa}{%
\leavevmode
\hbox{%
\scalebox{1}[.5]{%
\setbox\myscratchboxa=\hbox{XM}%
\setbox\myscratchboxb=\hbox{$\leftarrow$}%
\ht\myscratchboxb=1.5\ht\myscratchboxb
\myscratchlength2\ht\myscratchboxa
\advance\myscratchlength-2\ht\myscratchboxb
\myscratchlength=.5\myscratchlength
\advance\myscratchlength\ht\myscratchboxb
%
\lower-\myscratchlength
\hbox{%
\vrule\box\myscratchboxb
}%
}%
}%
\llap{%
\hbox{%
\scalebox{1}[.5]{%
\setbox\myscratchboxa=\hbox{XM}%
\setbox\myscratchboxb=\hbox{$\rightarrow$}%
\ht\myscratchboxb=1.5\ht\myscratchboxb
\myscratchlength2\ht\myscratchboxa
\advance\myscratchlength-2\ht\myscratchboxb
\myscratchlength=.5\myscratchlength
%
\lower-\myscratchlength
\hbox{%
\box\myscratchboxb\vrule
}%
}%
}%
}%
}\hfill}\endgroup
}%
% End of code for producing a horizontal-tab-key-symbol.
%..........................................................
\makeatletter
\newcommand\MakeHorizontalTabActive{\catcode`\^^I=13\relax}%
\begingroup
\MakeHorizontalTabActive
\@firstofone{%
\endgroup
\newcommand\MakeActiveHorizontalTabDeliverTabKeySymbol{%
\let^^I=\mytabkeysymbol
}%
\newcommand\MakeActiveHorizontalTabDeliverInvisiblespace{%
\let^^I=\@xobeysp
}%
}%
%..........................................................
% Don't indent this snippet of code!
\begingroup
\MakeHorizontalTabActive
\catcode`\ =12\relax%
\@firstofone{%
\endgroup%
\newcommand\MakeActiveHorizontalTabActLikeSpace{%
\def^^I{%
\ifnum\the\catcode`\ =13\space\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi%
{\@xobeysp}{ }%
}%
}%
\newcommand\MakeActiveHorizontalTabDeliverVisiblespace{\let^^I= }%
}%
%..........................................................
\NewDocumentCommand{\myxverb}{m}{%
\begingroup
\MakeHorizontalTabActive
\myinnerxverb{#1}%
}%
\NewDocumentCommand{\myinnerxverb}{ m v }{%
\endgroup
{\verbatim@font\frenchspacing#1#2}%
}%
\makeatother
\pagestyle{empty}
\begin{document}
text \verb*|This is verb* with tbars and two keyboard TABs \t \t in argument.| text
text {%
\MakeHorizontalTabActive
\MakeActiveHorizontalTabActLikeSpace
\verb*|This is verb* with tbars and two keyboard TABs in argument after changing keyboard TAB's catcode.|%
} text
text {%
\MakeHorizontalTabActive
\MakeActiveHorizontalTabDeliverTabKeySymbol
\verb*|This is verb* with tbars and two keyboard TABs in argument after changing keyboard TAB's catcode.|%
} text
text {%
\MakeHorizontalTabActive
\MakeActiveHorizontalTabActLikeSpace
\verb|This is verb with tbars and two keyboard TABs in argument after changing keyboard TAB's catcode.|%
} text
text {%
\MakeHorizontalTabActive
\MakeActiveHorizontalTabDeliverTabKeySymbol
\verb|This is verb with tbars and two keyboard TABs in argument after changing keyboard TAB's catcode.|%
} text
text
\myxverb{\MakeActiveHorizontalTabDeliverInvisiblespace}|This is myxverb with tbars and two keyboard TABs in argument.|
text
text
\myxverb{\MakeActiveHorizontalTabDeliverVisiblespace}|This is myxverb with tbars and two keyboard TABs in argument.|
text
text
\myxverb{\MakeActiveHorizontalTabDeliverTabKeySymbol}|This is myxverb with tbars and two keyboard TABs in argument.|
text
\end{document}
顺便一提:
似乎您可以阻止“论坛”/ StackExchage 平台将水平制表符转换为空格序列,如下所示:
不要使用-notation 来表示内联代码,也不要将代码块缩进四个空格,而是使用相应的`inline code`
html 标签<code>inline code</code>
<pre><code>block of code
block of code
block of code</code></pre>
。如果这样做,您可以/必须对解析 html 代码时具有特殊含义的许多字符使用 html 实体等。
例如,您必须写“>
为了获得”>
和“<
为了获得” <
。
一个好处是:您可以编写 html 实体	
来在代码中获取不会被转换的水平制表符。
至少在我的系统上似乎有效:当我从以下代码块复制粘贴到我最喜欢的文本编辑器中时,我会在和之间得到五个连续的水平制表A
符B
:
A B
我将此代码块写为<pre><code>A					B</code></pre>
。
答案2
附注:如果您想修补补丁xparse
以处理 TAB 字符,您可以修补\__xparse_grab_v_aux_catcodes:
宏。
%! TEX program = lualatex
\documentclass{article}
\usepackage{etoolbox} % ← need this package
\begin{document}
\NewDocumentCommand{\aaa}{+v}{#1}
\ExplSyntaxOn
% ======== begin main code ========
\patchcmd {\__xparse_grab_v_aux_catcodes:} {
\cs_set_eq:NN \do \char_set_catcode_other:N
\dospecials
} {
\cs_set_eq:NN \do \char_set_catcode_other:N
\do \^^I
\dospecials
} {} {}
% ======== end main code ========
% example use
\aaa+123456x%^^
1234
123+
\ExplSyntaxOff
\end{document}
显然,这不能保证在未来的 expl3/xparse 版本中能够正常工作。