使用字符代码将字符附加到标记列表

使用字符代码将字符附加到标记列表

有没有办法从字符代码表示中获取实际的字符标记?具体来说,我希望有一个宏\prepend#1#2,它接受一个标记列表#1,并将与字符代码相对应的字符标记添加#2到其中。

以下是预期结果的简短演示:

\newtoks\test
\test={bc}
\prepend\test{97}
\showthe\test % should print abc

答案1

您可以使用\char_generate:nn { <charcode> } { <catcode> }

\input expl3-generic
\ExplSyntaxOn
\cs_new_eq:NN \toks_use:N \tex_the:D
\cs_new_protected:Npn \prepend #1 #2
  {
    \if:w \exp_not:N #1 #1
      \use:x { #1 = { \char_generate:nn {#2} { 12 } \toks_use:N #1 } }
    \else:
      \tl_put_left:Nx #1 { \char_generate:nn {#2} { 12 } }
    \fi:
  }
\ExplSyntaxOff

\newtoks\test
\test={bc}
\prepend\test{97}
\showthe\test % should print abc

\def\test{bc}
\prepend\test{97}
\show\test % should print abc

\bye

终端输出将是:

> abc.
l.16 \showthe\test
                   % should print abc
? 
> \test=macro:
->abc.
l.21 \show\test
                % should print abc again
?

如何\char_generate:nn生成字符取决于所使用的引擎。在 LuaTeX 中,它使用tex.cprint(<catcode>, utf8_char(<charcode>)),方式与 Henri 的答案类似,但具有可能的<catcode>设置。在 XeTeX 中,它使用\Ucharcat <charcode> <catcode>

expl3在(pdftexε-pTeX和)支持的其他引擎中,ε-upTeX没有办法真正产生仅扩展上下文中的字符( 的一个关键特性\char_generate:nn),因此expl3使用与 egreg 的答案相同的方法预先生成这些字符,然后\char_generate:nn在被要求时使用这些字符。

正如 egreg 的回答中所说,您无法生成某些 catcode 的字符(即 0、5、9、14 和 15),因为它们不会产生标记(当 TeX 扫描输入时它们会消失,因此它们在宏扩展级别不存在)。此外,expl3为了在引擎之间保持一致性,该实现不允许生成空格字符,因为 Lua 版本不允许这样做。但是,由于您想要 Knuth TeX 版本,因此也允许使用空格字符。


expl3以下代码是针对 代码的改编,\char_generate:nn经过修改后可在 Knuth TeX 中使用。代码基本相同,只是由于缺少 而需要一些额外的复杂功能\unexpanded,这允许您在宏中使用单个参数标记,并允许您轻松地将内容附加到宏中而无需 toks 寄存器。除此之外,它们都是一样的。

代码首先定义一个临时的 toks 寄存器,其中包含空字符 ( ^^@) 和可能的不同的 catcode,以 分隔\or

\or ^^@% 1
\or ^^@% 2
\or ^^@% 3
\or ^^@% 4
\or    % 5 Invalid
\or ^^@^^@% 6 Has to be doubled for a later `\def`
\or ^^@% 7
\or ^^@% 8
\or    % 9 Invalid
\or ^^@% 10
\or ^^@% 11
\or ^^@% 12
\or ^^@% 13

然后它循环遍历所有字符代码并将空字符256设置为,然后使用egreg的答案中的技巧:\lccode#1\lowercase

    \begingroup
      \lccode0=#1
      \lccode32=#1
      \edef\x{\endgroup
      \gdef\expandafter\noexpand
        \csname c__char_\romannumeral#1_tl\endcsname{\the\tmptoks}}%
      \lowercase\expandafter{\x}

对于字符代码 97,结果是:

\gdef\c__char_xcvii_tl{\or a\or a\or a\or a\or \or aa\or a\or a\or \or a\or a\or a\or a}

然后给定一个字符代码,<charcode>您可以使用访问该标记列表\csname c__char_\romannumeral<charcode>_tl\endcsname,然后使用`\ifcase\fi 您获得所请求的字符。

\chargenerate宏首先检查(在 中\generateaux)参数是否在有效范围内(catcode 在 1 到 13 之间,5 和 9 除外,charcode 在 0 到 255 之间,尽管使用 Knuth TeX 可能需要将其更改为 127),然后\generateauxi使用参数进行调用,然后使用\ifcase上面的测试(带有一些用于扩展控制的位和片段)留下请求的字符。

运行下面的代码,tex我得到:

在此处输入图片描述

% Auxiliaries
\long\def\gobbletoqstop#1\qstop{}
\long\def\firstofone#1{#1}
\chardef\expend=0
% Expandable error message
\begingroup
\long\xdef\expandableerror#1{%
  \noexpand\expandafter\noexpand\expandafter\noexpand\expandafter
    \noexpand\gobbletoqstop\noexpand\firstofone
      {\csname Error! \endcsname#1}\noexpand\qstop}
\endgroup
% Append stuff to a toks register
\def\toksputright#1{%
  \begingroup
    \def\toksputtoks{#1}%
    \afterassignment\toksputrightaux
    \toks0=}
\def\toksputrightaux{%
    \edef\x{\endgroup
      \toksputtoks={\the\toksputtoks\the\toks0}}%
  \x}
% Set up constant token lists
\newtoks\tmptoks
\begingroup
  \tmptoks{ \noexpand\or}%
  \catcode0=1
  \toksputright\tmptoks{^^@\iffalse}}%
  \catcode0=2
  \toksputright\tmptoks{{\fi\noexpand\or^^@}%
  \begingroup
    \def\noop{}%
    \edef\x{\expandafter\noop\the\tmptoks}%
  \expandafter\endgroup
  \expandafter\tmptoks\expandafter{\x}%
  \catcode0=3  \toksputright\tmptoks{\or^^@}%
  \catcode0=4  \toksputright\tmptoks{\or^^@}%
  \catcode0=5  \toksputright\tmptoks{\or}%
  \catcode0=6  \toksputright\tmptoks{\or^^@^^@}%
  \catcode0=7  \toksputright\tmptoks{\or^^@}%
  \catcode0=8  \toksputright\tmptoks{\or^^@}%
  \catcode0=9  \toksputright\tmptoks{\or}%
  \catcode0=10 \toksputright\tmptoks\expandafter{\firstofone{\or}^^@}%
  \catcode0=11 \toksputright\tmptoks{\or ^^@}%
  \catcode0=12 \toksputright\tmptoks{\or^^@}%
  \catcode0=13 \toksputright\tmptoks{\or^^@}%
  \def\chartmp#1;{%
    \begingroup
      \lccode0=#1
      \lccode32=#1
      \edef\x{\endgroup
      \gdef\expandafter\noexpand
        \csname c__chargen_\romannumeral#1_tl\endcsname{\the\tmptoks}}%
      \lowercase\expandafter{\x}}%
  \let^^L\relax
  \catcode`^^L=12
  \count0=0
  \loop
    \expandafter\chartmp\number\count0;
    \advance\count0 by 1
    \ifnum\count0<256 \repeat
\endgroup
% Main definition
\def\chargenerate#1#2{%
  \romannumeral\expandafter\generateaux
    \number#1\expandafter;\number#2;}
% Check for invalid input
\def\generateaux#1;#2;{%
  \ifnum0%
      \ifnum#1=0  1\fi
      \ifnum#2=10 1\fi
      =11
    \expandableerror{Cannot generate null char as a space.}%
  \else
    \ifodd0%
        \ifnum#2< 1 1\fi
        \ifnum#2= 5 1\fi
        \ifnum#2= 9 1\fi
        \ifnum#2>13 1\fi\space
      \expandableerror{Invalid catcode for char generation.}%
    \else
      \ifodd0%
          \ifnum#1<  0 1\fi
          \ifnum#1>"FF 1\fi\space
        \expandableerror{Charcode requested out of engine range.}%
      \else
        \generateauxi{#1}{#2}%
      \fi
    \fi
  \fi
  \expend}
% Actual char generation
\def\generateauxi#1#2#3\expend{%
  #3%
  \iffalse{\fi
  \expandafter\expandafter
  \expandafter\expend
  \expandafter\expandafter
  \ifcase#2%
    \csname c__chargen_\romannumeral#1_tl\endcsname
  \or}
  \fi}

% Testing
\def\empty{}
\begingroup
  \lccode`\~=`a
  \lowercase{\endgroup
  \gdef ~{\ active character a}%
}
\def\test#1{%
  \edef\x{%
    \ifnum#1=2 {\iffalse}\fi\space\noexpand\meaning\fi % add { if a is a }
    \chargenerate{97}{#1}%
    \ifnum#1=6 \chargenerate{97}{#1}\fi% add another # if a is a #
    \ifnum#1=1 \iffalse{\fi\space\noexpand\meaning}\fi % if a is a {, add a }
  }%
  \ifx\x\empty
    #1: ERROR
  \else
    #1: \expandafter\meaning\x
  \fi\par}

\tt\scrollmode
\count2=0
\loop
\test{\the\count2 }%
\advance\count2 by 1
\ifnum\count2<16
\repeat

\bye

答案2

\lowercase是一种很好的方法,使用任何 TeX 都可以。

\def\prepend#1#2{% toks, charcode
 \begingroup
  \lccode`9=#2\relax
  \lowercase{%
    \edef\0{\endgroup 
       #1={9\the#1}}%
  \0}}

假定 toks 寄存器不是\0

答案3

\newtoks\test

\def\prepend#1#2{%
  \ifcase\catcode#2\relax
    % 0, do nothing
    \or
    % 1, do nothing
    \or
    % 2, do nothing
    \or
    \prependaux#1{#2}{$}% 3
    \or
    \prependaux#1{#2}{&}% 4
    \or
    % 5, do nothing
    \or
    \prependaux#1{#2}{##}% 6
    \or
    \prependaux#1{#2}{^}% 7
    \or
    \prependaux#1{#2}{_}% 8
    \or
    % 9, do nothing
    \or
    \prependaux#1{#2}{ }% 10
    \or
    \prependaux#1{#2}{a}% 11
    \or
    \prependaux#1{#2}{?}% 12
    \or
    \prependaux#1{#2}{~}% 13
    % 14 or 15, do nothing
  \fi
}
\def\prependaux#1#2#3{%
  \begingroup\lccode`#3=#2\relax
  \lowercase{\endgroup\toks0={#3}}%
  #1\expandafter{\the\toks\expandafter0\the#1}%
}

\test={bc}
\prepend\test{97}

\message{\number`?}

\catcode`?=3

\prepend\test{63}

\the\test$

\prepend\test{`\#}

\showthe\test

\bye

您不能添加类别代码为 0、1、2、5、9、14 或 15 的字符。

如您所见,我添加了一个“奇怪”类别代码 3 字符,并且代码\the\test$打印了一个数学公式。

限制:#1不能\toks0

答案4

您可以使用LuaTeX和string.char函数将ASCII码转换为相应的字符。

\newtoks\test
\test={bc}
\tokspre\test\expandafter{\directlua{tex.sprint(string.char(97))}}
\showthe\test
\bye

相关内容