\MakeUppercase 是否有纯粹可扩展的变体?

\MakeUppercase 是否有纯粹可扩展的变体?

\MakeUppercase\uppercase使用 TeX 的 uccode,而且它们不是纯粹可扩展的。比如说,

\edef\temp{\MakeUppercase{abc}}

将失败。

有时纯可扩展宏非常有用。例如,它们很强大。我认为我们可以使用字符串替换来实现这样的功能。理想的结果是:

\Uppercase{abc} % expanded to -> ABC

也可能是:

\GetUppercase{abc} % \result is expanded to -> ABC

到目前为止,我能做的是:

% \getuppercase{abc} => \result :-> A
\def\getupperchar#1{%
  \edef\result{\ifcase`#1\relax
     0\or 1\or 2\or 3\or 4\or 5\or 6\or 7\or 8\or 9\or
    10\or11\or12\or13\or14\or15\or16\or17\or18\or19\or
    20\or21\or22\or23\or24\or25\or26\or27\or28\or29\or
    30\or31\or32\or33\or34\or35\or36\or37\or38\or39\or
    40\or41\or42\or43\or44\or45\or46\or47\or48\or49\or
    50\or51\or52\or53\or54\or55\or56\or57\or58\or59\or
    60\or61\or62\or63\or64\or A\or B\or C\or D\or E\or
     F\or G\or H\or I\or J\or K\or L\or M\or N\or O\or
     P\or Q\or R\or S\or T\or U\or V\or W\or X\or Y\or
     Z\or91\or92\or93\or94\or95\or96\or A\or B\or C\or
     D\or E\or F\or G\or H\or I\or J\or K\or L\or M\or
     N\or O\or P\or Q\or R\or S\or T\or U\or V\or W\or
     X\or Y\or Z\or123\or124\or125\or126\or127\or128\or129\or
    \fi}}

这可以用于实现一个简单的变体mfirstuc

% \getfirstupper{abc} => \result :-> Abc
\def\getfirstupper#1{%
  \getupperchar{#1}%
  \edef\result{\result\gobble#1}}
\def\gobble#1{}

\Uppercase但是,我无法通过这种方式实现完整的命令\GetUppercase。我想知道是否有一种聪明的方法来定义这样的命令。

欢迎提供任何信息。顺便说一句,我知道可以使用 LuaTeX,但我正在寻找纯 TeX 解决方案。

答案1

更新答案

因为expl3部分基于我最初提出的方法和布鲁诺的方法我们现在已经开发了一组可扩展的大小写转换函数,可以实现 Unicode 联盟所描述的大小写映射:

  • \str_foldcase:n
  • \text_uppercase:n(n)
  • \text_lowercase:n(n)
  • \text_titlecase:n(n)

需要注意的一点是,它们使用“引擎原生”输入,这意味着 pdfTeX 仅支持 ASCII(8 位范围的上半部分比较棘手)。对于 XeTeX/LuaTeX,则涵盖整个 Unicode 范围。

这个问题的直接答案是使用\text_uppercase:n:它以选择性的方式扩展输入,可以处理诸如和之类的条目,\aa并在包括“-type”方法的扩展上下文中工作f(使用扩展\romannumeral)。在当前的实现中,有一些功能与textcase包,例如选择性跳过输入,跳过数学模式材料,ETC。

有四种类型的函数可以涵盖不同的用例:

  • “删除”大小写以用于非文本上下文。这看起来更像“小写”,并且是一对一映射。由于数据类似于字符串,因此调用该函数,\str_foldcase:n并且不会跳过或扩展任何输入。

  • 大写

  • 小写

  • 制作“标题大小写”(Unicode 描述):它仅覆盖某些文本的第一个“字母”,而不是某些文本每个单词的首字母(后者在英语中通常称为标题大小写)

该代码包括处理上下文依赖的能力(例如我们可以使用多种语言的版本来表示字母,例如希腊语中的 final-sigma(希腊语中的 final-sigma),以及与语言相关的版本,例如\text_lowercase:nn { tr } { I }应用土耳其语规则(此处产生了无点 i)。

在实现层面,所采用的方法是使用两部分策略映射输入,首先确定下一个标记是空格、括号或其他内容(我们称之为N-type)。可以正确获取每种类型,然后使用查找表根据需要更改大小写。

请注意,在 LuaTeX 中使用 Lua 只能提供部分解决方案,原因有二。首先,Lua 不支持 TeX 标记,这意味着跳过数学模式输入等操作需要付出更多努力。其次,LuaTeX 中提供的当前 Lua Unicode 库文档不完善,没有涵盖上下文相关问题、非一一映射等。例如,一个简单的测试用例是

\documentclass{article}
\usepackage{fontspec}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\text_uppercase:n { Fußball }
\ExplSyntaxOff

\directlua{tex.print(unicode.utf8.upper("Fußball"))}
\end{document}

其中 Lua 语言中没有大小写变化。(Lua 库遵循的 Unicode 版本也不清楚。)


原始答案

为了expl3,我写了以下内容作为我能找到的最强大的方法

\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\cs_new:Npn \tl_to_upper_case:n #1
  { \exp_args:Nf \__tl_to_upper_case:n {#1} }
\cs_new:Npn \__tl_to_upper_case:n #1
  { \__tl_to_upper_case:w #1 ~ \q_no_value \q_stop }
\cs_new:Npn \__tl_to_upper_case:w #1 ~ #2 \q_stop
  {
    \quark_if_no_value:nTF {#2}
      { 
        \tl_map_function:nN {#1} \__tl_to_upper_case_aux:N 
        \tl_trim_spaces:n { }
      }
      { \__tl_to_upper_case:w #1 { ~ } #2 \q_stop }
  }
\cs_new:Npn \__tl_to_upper_case_aux:N #1
  {
    \prg_case_str:nnn {#1}
      {
        { a } { \__tl_to_case_aux:nw { A } }
        { b } { \__tl_to_case_aux:nw { B } }
        { c } { \__tl_to_case_aux:nw { C } }
        { d } { \__tl_to_case_aux:nw { D } }
        { e } { \__tl_to_case_aux:nw { E } }
        { f } { \__tl_to_case_aux:nw { F } }
        { g } { \__tl_to_case_aux:nw { G } }
        { h } { \__tl_to_case_aux:nw { H } }
        { i } { \__tl_to_case_aux:nw { I } }
        { j } { \__tl_to_case_aux:nw { J } }
        { k } { \__tl_to_case_aux:nw { K } }
        { l } { \__tl_to_case_aux:nw { L } }
        { m } { \__tl_to_case_aux:nw { M } }
        { n } { \__tl_to_case_aux:nw { N } }
        { o } { \__tl_to_case_aux:nw { O } }
        { p } { \__tl_to_case_aux:nw { P } }
        { q } { \__tl_to_case_aux:nw { Q } }
        { r } { \__tl_to_case_aux:nw { R } }
        { s } { \__tl_to_case_aux:nw { S } }
        { t } { \__tl_to_case_aux:nw { T } }
        { u } { \__tl_to_case_aux:nw { U } }
        { v } { \__tl_to_case_aux:nw { V } }
        { w } { \__tl_to_case_aux:nw { W } }
        { x } { \__tl_to_case_aux:nw { X } }
        { y } { \__tl_to_case_aux:nw { Y } }
        { z } { \__tl_to_case_aux:nw { Z } }
      }
      { \__tl_to_case_aux:nw {#1 } }
  }
\cs_new:Npn \__tl_to_case_aux:nw #1#2 \tl_trim_spaces:n #3
  {
   #2
   \tl_trim_spaces:n { #3 #1 }
  }
\cs_set_eq:NN \MakeExpandableUppercase \tl_to_upper_case:n
\ExplSyntaxOff
\begin{document}
\MakeExpandableUppercase{Hello World}
\edef\test{\MakeExpandableUppercase{Hello World}}
\show\test
\MakeExpandableUppercase{Hello {World}}
\edef\test{\MakeExpandableUppercase{Hello {World}}}
\show\test
\edef\test{Hello\space\space World}
\MakeExpandableUppercase{\test}
\edef\test{\MakeExpandableUppercase{\test}}
\end{document}

在输入末尾删除空格的原因是,在字符串开头无法避免这种情况,所以我觉得你能做的最好的事情就是说“末尾的空格被删除”。空格应该保留在输入中。你可以用同样的方式实现小写函数,如果你进行嵌套

 \MakeExpandableUppercase{\MakeExpandableLowercase{Hello} World}

应该可以正常工作。如上一个示例所示,在进行大小写更改之前,会先扩展材料。这甚至适用于受保护的宏,因为底层扩展使用了\romannumeral。因此,参数需要由纯可扩展材料组成。

(需要注意的是,这当然可以在没有的情况下实现expl3。)


为了完整性,LuaTeX 解决方案可能如下所示

\documentclass{article}
\usepackage{fontspec}
\newcommand*\MakeExpandableUppercase[1]{%
  \scantokens\expandafter{%
    \directlua{
      tex.write(string.upper("\luatexluaescapestring{\unexpanded{#1}}"))
    }%
  \noexpand
  }%
}%
\begin{document}
\MakeExpandableUppercase{hello world \oe}
\end{document}

(我不是 Lua 专家:可能有更有效的方法。)

答案2

编辑3:现在 LaTeX3 中的标记列表模块提供了\text_uppercase:n\text_lowercase:n,它们源于该讨论,但更强大,对控制序列数量的要求也更低。速度也更慢。

编辑 2:在第一个代码吃掉空格并在遇到括号时卡住,第二个代码在超过 600 个左右的标记时会崩溃之后,我花了一些时间编写了一个干净的代码,它仍然可以处理超过 5k 个标记,尽管它变慢了。新代码实际上适合各种概括(参见代码底部附近)。我摆脱了导致“扩展级别过多”的扩展控制,现在代码不再那么棘手了。

(抱歉,代码和解释很长。)现在,经过三个扩展步骤后,\Uppercase{ Hel{l }o\error World }扩展为HEL{L }O\error WORLD,保留空格、括号和宏(不扩展)。

两个想法:

  • 在所有标记后面放置一个后,使用分隔参数(参见\UL_brace_check:nw和)检查括号和空格,以确保参数后面至少有一个括号或空格。\UL_space_check:nw{\q_mark} \q_stop

  • 定义案例更改表。例如,\UL_table_upper_p是一个宏,它扩展为P,并\UL_table_lower_A扩展为a。如果未定义表的相关条目,则不会更改正在读取的标记。请参阅\UL_convert_token:nN。代码的最后一部分是关于设置这些表(“案例表”?)。

我们需要进入括号组并\UL_to_case:nn完全展开,然后才能继续。为此,我们使用\romannumeral-\\0`,以空格结尾,该空格在最后引入。

一些宏值得解释一下。

  • \UL_expand_csname:n{...}\csname abc\endcsname用相应的替换每个构造\abc。我需要在某个地方显式地定义一个相当深的 csname。

  • \expandafter:nw{...}\foo\foo将之前扩大...

  • \expandsome{\foo\expandthis\bar\baz\expandthis\foo{ABC}}将扩展下面的宏\expandthis一次(该宏可以接受任何类型的参数:事实上,我们只是简单地\expandafter这样做)。

代码也可以找到在线的。最后是代码,最后有一些测试和一些注释。

\catcode`\_=11\relax
\catcode`\:=11\relax

% ======================== Generic macros

% A few standard commands to manipulate arguments
\long\gdef\use_none:n#1{}
\long\gdef\use_none:nn#1#2{}
\long\gdef\use_i:nn#1#2{#1}
\long\gdef\use_ii:nn#1#2{#2}
\long\gdef\use_ii_i:nn#1#2{#2#1}
\long\gdef\use_ii_iii_i:nnn#1#2#3{#2#3#1}
\long\gdef\use_i_bbii:nn#1#2{#1{{#2}}}
\long\gdef\use_bii_bi:nn#1#2{{#2}{#1}}

% What expl3 calls "quarks", useful for |\ifx| comparisons.
\gdef\q_stop{\q_stop}
\gdef\q_mark{\q_mark}
\gdef\q_nil{\q_nil}
\long\gdef\use_none_until_q_stop:w#1\q_stop{}

% Two tests 
\long\gdef\UL_if_empty:nTF#1{%
  \expandafter\ifx\expandafter\q_nil\detokenize{#1}\q_nil%
  \expandafter\use_i:nn%
  \else%
  \expandafter\use_ii:nn%
  \fi}

\expandafter\long\expandafter\gdef\expandafter\UL_if_detok_qmark:wTF%
\expandafter#\expandafter1\detokenize{\q_mark}#2\q_stop{% 
  \UL_if_empty:nTF{#1}}

% ======================== Main command: |\UL_to_case:nn|
% Usage:       |\UL_to_case:nn{<table>}{<text>}|
% Expands in:  2 steps.
\long\gdef\UL_to_case:nn{\romannumeral\UL_to_case_aux:nn}
\long\gdef\UL_to_case_aux:nn#1#2{-`\0%
  \UL_brace_check:nw{#1}#2{\q_mark} \q_stop\UL_to_case_end:n{}}%

% Initially, I used |\q_mark{} \q_stop|: the braces and space are there
% to avoid runaway arguments in |\UL_brace_check:nw| and 
% |\UL_space_check:nw|, whose "w" arguments are delimited respectively
% by an open brace, and by a space. I changed to |{\q_mark} \q_stop|:
% then we only do the check for |\q_mark| in the case of a brace group, 
% and not at every step.

% |\UL_to_case_output:n| appends its argument to the argument of
% |\UL_to_case_end:n|.
\long\gdef\UL_to_case_output:n#1#2\UL_to_case_end:n#3{%
                                    #2\UL_to_case_end:n{#3#1}}
\long\gdef\UL_to_case_end:n#1{ #1}
% And |\UL_to_case_end:n| expands to 
% - a space, which stops the expansion of |\romannumeral-`\0|,
% - followed by its argument, which is the result we want.


% First, we check whether the next token is a brace. 
\long\gdef\UL_brace_check:nw#1#2#{%
  \UL_if_empty:nTF{#2}%
  {\UL_brace_yes:nn{#1}}%
  {\UL_space_check:nw{#1}#2}%
}
% If there is a brace, we might have reached {\q_mark}.
\long\gdef\UL_brace_yes:nn#1#2{%
  \expandafter\UL_if_detok_qmark:wTF \detokenize{#2 \q_mark}\q_stop{% 
    \use_none_until_q_stop:w% 
  }{% 
    \csname UL_table_#1_braces\endcsname{#1}{#2}%
    \UL_brace_check:nw{#1}%
  }%
}

% Then check whether the next token is a space.
\long\gdef\UL_space_check:nw#1#2 {%
  \UL_if_empty:nTF{#2}%
  {\UL_convert_token:nn{#1}{ }}%
  {\UL_convert_token:nn{#1}#2 }% we put the space back!
}

\long\gdef\UL_convert_token:nn#1#2{%
  \ifcsname UL_table_#1_\detokenize{#2}\endcsname%
  \expandafter\use_i:nn%
  \else%
  \expandafter\use_ii:nn%
  \fi% 
  {\csname UL_table_#1_\detokenize{#2}\endcsname}%
  {\csname UL_table_#1_default\endcsname{#2}}%
  \UL_brace_check:nw{#1}% Do the next token.
}


% ======================== Casecode tables.
% ============ Generic setup.
% Typical use:
% - |\UL_setup:nnn{u}{a}{A}| to define |a| uppercased as |A|.
% - |\UL_setup_cmd:nnpn{ULnil}{\NoCaseChange}#1{%
%      \UL_to_case_output:n{#1}}|
% Note that for the second, we have to grab all the arguments in one go.
% Also note that the second should not be used until we define the ULec 
% and ULea tables below.
%
% - |\UL_set_eq:nnnn{tableA}{tokenA}{tableB}{tokenB}| sets the entry
% |tokenA| of the table |tableA| to be equal to the entry |tokenB| of the
% table |tokenB|.
% - |\UL_new_table:nn{tableA}{tableB}| creates a new table, |tableA|, 
% which is a copy of |tableB|.

\protected\long\gdef\UL_content_of_table_add:nn#1#2{%
  \long\expandafter\gdef\csname UL_table_#1%
  \expandafter\expandafter\expandafter\endcsname%
  \expandafter\expandafter\expandafter{%
    \csname UL_table_#1\endcsname{#2}}%
}

\protected\long\gdef\UL_setup:nnn#1#2#3{%
  \UL_content_of_table_add:nn{#1}{#2}%
  \expandafter\long\expandafter\gdef%
  \csname UL_table_#1_\detokenize{#2}\endcsname%
  {\UL_to_case_output:n{#3}}%
}

\protected\long\gdef\UL_setup_cmd:nnpn#1#2#3#{%
  \UL_content_of_table_add:nn{#1}{#2}%
  \UL_expand_csname:n{%
    \long\gdef\csname UL_table_#1_\detokenize{#2}\endcsname##1##2{%
      \expandafter:nw{\use_ii_i:nn{##1{##2}}}%
      \csname UL_table_#1_\detokenize{#2}_aux\endcsname}%
  }%
  \use_i_bbii:nn{\expandafter\long\expandafter\gdef%
    \csname UL_table_#1_\detokenize{#2}_aux\endcsname#3}%
}

\protected\long\gdef\UL_set_eq:nnnn#1#2#3#4{%
  \UL_content_of_table_add:nn{#1}{#2}%
  {\expandafter}\expandafter\global\expandafter\let%
  \csname UL_table_#1_\detokenize{#2}\expandafter\endcsname%
  \csname UL_table_#3_\detokenize{#4}\endcsname%
}

\long\gdef\UL_new_table:nn#1#2{%
  \ifcsname UL_table_#1\endcsname%
  \PackageError{ULcase}{Table \detokenize{#1} already defined!}{}%
  \fi%
  \long\expandafter\gdef\csname UL_table_#1\endcsname{}%
  %
  \def\UL_tmpA{#1}%
  \def\UL_tmpB{#2}%
  \expandafter\expandafter\expandafter\UL_new_table_aux:nnn%
  \csname UL_table_#2\endcsname{}%
}
\long\gdef\UL_new_table_aux:nnn#1{%
  \UL_if_empty:nTF{#1}{}{%
    \UL_set_eq:nnnn{\UL_tmpA}{#1}{\UL_tmpB}{#1}%
    \UL_new_table_aux:nnn%
  }%
}%
\long\gdef\UL_new_table:n#1{\UL_new_table:nn{#1}{ULnil}}




% ============ Table ULea, \expandafter:nw
% 
% The |ULea| table puts |\expandafter| after each token (including braces
% and spaces). Allows us to define |\expandafter:nw|, which expands what
% follows its first argument once. 
% 
% |\expandafter:nw| takes 2-steps to act. For a 1-step version, use 
% |\MEA_trigger:f\MEA_expandafter:nw|. 

\long\gdef\UL_table_ULea_default#1{\UL_to_case_output:n{\expandafter#1}}%
\long\gdef\UL_table_ULea_braces#1#2{%
  \expandafter\expandafter\expandafter\UL_to_case_output:n%
  \expandafter\expandafter\expandafter{%
    \expandafter\expandafter\expandafter\expandafter%
    \expandafter\expandafter\expandafter{%
      \UL_to_case:nn{#1}{#2}\expandafter}%
  }%
}
\let\MEA_trigger:f\romannumeral
\def\MEA_expandafter:nw{\UL_to_case_aux:nn{ULea}}
\def\expandafter:nw{\MEA_trigger:f\MEA_expandafter:nw}


% ============ Table |ULec|, |\UL_expand_csname:n|
% The |ULec| table expands only the 
% |\csname ...\endcsname| constructions.
% 
\long\gdef\UL_table_ULec_default{\UL_to_case_output:n}%
\long\gdef\UL_table_ULec_braces#1#2{%
  \expandafter\expandafter\expandafter\UL_to_case_output:n%
  \expandafter\expandafter\expandafter{%
    \expandafter\expandafter\expandafter{\UL_to_case:nn{#1}{#2}}%
  }%
}
\long\expandafter\gdef\csname%
  UL_table_ULec_\detokenize{\csname}\endcsname#1#2{%
  \expandafter:nw{\use_ii_iii_i:nnn{#1{#2}}}%
  \expandafter\UL_to_case_output:n\csname%
}%

\def\UL_expand_csname:n{\MEA_trigger:f\UL_to_case_aux:nn{ULec}}


% ============ Table |ULexpandsome|, |\expandsome|
% The |ULexpandsome| table expands only the tokens following |\expandthis|.
% 
\long\gdef\UL_table_ULexpandsome_default{\UL_to_case_output:n}%
\long\gdef\UL_table_ULexpandsome_braces#1#2{%
  \expandafter\expandafter\expandafter\UL_to_case_output:n%
  \expandafter\expandafter\expandafter{%
    \expandafter\expandafter\expandafter{\UL_to_case:nn{#1}{#2}}%
  }%
}
\long\expandafter\gdef\csname%
  UL_table_ULexpandsome_\detokenize{\expandthis}\endcsname#1#2{%
  \expandafter:nw{#1{#2}}%
  %\expandafter\UL_to_case_output:n\csname%
}%

\def\expandsome{\MEA_trigger:f\UL_to_case_aux:nn{ULexpandsome}}


% ============ The default table, ULnil
\long\gdef\UL_table_ULnil{{default}{braces}{$}}%$
\long\gdef\UL_table_ULnil_default{\UL_to_case_output:n}
\long\gdef\UL_table_ULnil_braces#1#2{%
  \expandafter\expandafter\expandafter\UL_to_case_output:n%
  \expandafter\expandafter\expandafter{%
    \expandafter\expandafter\expandafter{\UL_to_case:nn{#1}{#2}}%
  }%
}
\UL_setup_cmd:nnpn{ULnil}{\NoCaseChange}#1{%
  \UL_to_case_output:n{#1}}


% ============ Working on math mode.
% 
% We add \q_mark so that \UL_dollar_aux:nw can read to the next dollar
% without unbracing the argument, so that ${...}$ --x-> $...$
\long\expandafter\gdef\csname UL_table_ULnil_\detokenize{$}\endcsname#1#2{%$
    \UL_dollar_aux:nw{#1{#2}}\q_mark%
}
% Grab until the next dollar, so #2={\q_mark Math Stuff}. 
% If \use_none:n #2 is empty, then we had only grabbed `\q_mark`, 
% which means there was $$, and we need to redo the same business. 
% Otherwise, we output, after stripping the \q_mark.
\long\gdef\UL_dollar_aux:nw#1#2${%$%
  \expandafter\UL_if_empty:nTF\expandafter{\use_none:n#2}{% eats \q_mark
    \UL_bidollar:nw{#1}\q_mark%
  }{%
    \expandafter\UL_to_case_output:n\expandafter{%
      \expandafter$\use_none:n#2$}#1%
  }%
}
\long\gdef\UL_bidollar:nw#1#2$${%
  \expandafter\UL_to_case_output:n\expandafter{%
    \expandafter$\expandafter$\use_none:n#2$$}#1}



% =========== Lowercase, Uppercase, Caesar
\long\gdef\Lowercase{\UL_to_case:nn{lower}}
\long\gdef\Uppercase{\UL_to_case:nn{upper}}
\long\gdef\CaesarCipher{\UL_to_case:nn{caesar}}

% Setup the uppercase and lowercase tables.
\UL_new_table:n{lower}
\UL_new_table:n{upper}

\protected\long\gdef\UL_setup_lower_upper:n#1{%
  \UL_if_empty:nTF{#1}{}{%
    \UL_setup:nnn{upper}#1%
    \expandafter:nw{\UL_setup:nnn{lower}}\use_bii_bi:nn#1%
    \UL_setup_lower_upper:n%
  }%
}
% should become user-firendly.
\UL_setup_lower_upper:n {{a}{A}} {{b}{B}} {{c}{C}} {{d}{D}} {{e}{E}} 
{{f}{F}} {{g}{G}} {{h}{H}} {{i}{I}} {{j}{J}} {{k}{K}} {{l}{L}} {{m}{M}} 
{{n}{N}} {{o}{O}} {{p}{P}} {{q}{Q}} {{r}{R}} {{s}{S}} {{t}{T}} {{u}{U}} 
{{v}{V}} {{w}{W}} {{x}{X}} {{y}{Y}} {{z}{Z}} {{\ae}{\AE}} {{\oe}{\OE}} 
{}


% Just for fun, we define the Caesar cipher.
\UL_new_table:n{caesar}
\begingroup
  \lccode`\x=1\relax
  \loop
    \lccode`\X=\numexpr\lccode`\x+2\relax
    \lowercase{\UL_setup:nnn{caesar}{x}{X}}%
    \lccode`\x=\numexpr\lccode`\x+1\relax
  \unless\ifnum\lccode`\x>126\relax
  \repeat
\endgroup
\UL_setup:nnn{caesar}{ }{ }




% ====== Various tests
\long\gdef\checkoutput{\ifx\a\b\message{Correct}\else\show\WRONG\fi}

\long\gdef\expandonce#1{% redefines #1 as #1 expanded once.
  \long\xdef#1{\unexpanded\expandafter\expandafter\expandafter{#1}}}
\def\0{\1}\def\1{\2}\def\2{\3}\def\3{\4}\def\4{\5}


% \Uppercase, \Lowercase, \NoCaseChange work (+ nesting)
% Spaces and braces are fine.
\long\gdef\a{\Uppercase{ Hello, { } W\Lowercase{O}r\NoCaseChange{lD}! }}
\expandonce\a\expandonce\a\expandonce\a
\long\gdef\b{ HELLO, { } W\Lowercase{O}RlD! }
\checkoutput

% Another test.
\long\gdef\a{\Lowercase{He l%
    \NoCaseChange{\Uppercase{ Lp\NoCaseChange{ o}}}o }}
\expandonce\a\expandonce\a\expandonce\a
\long\gdef\b{he l\Uppercase{ Lp\NoCaseChange{ o}}o }
\checkoutput
\long\edef\a{\a}
\long\gdef\b{he l LP oo }
\checkoutput

% Math works (both $$ and $). Nesting does not break, 
% although we would wish for better (i.e. "Letter"-> "letter").
\long\gdef\a{\Lowercase{{t}ExT, $$\frac{A}{B}$$ and $(\mbox{Letter $A$})$}}
\expandonce\a\expandonce\a\expandonce\a
\long\gdef\b{{t}ext, $$\frac{A}{B}$$ and $(\mbox{Letter $A$})$}
\checkoutput

\edef\a{\CaesarCipher{a{b}cdef@ ABCX}}
\edef\b{c{d}efghB CDEZ}
\checkoutput


\long\gdef\a{\Uppercase{%
    \0{ a${} {{abd}+cd}$\0{b$${\d $0$}$$ }}%
    \NoCaseChange{ Ac dD\relax\0ii}i cd }%
}
\expandonce\a\expandonce\a\expandonce\a
\long\gdef\b{\0{ A${} {{abd}+cd}$\0{B$${\d $0$}$$ }} %
  Ac dD\relax\0iiI CD }%
\checkoutput



% More on braces, spaces, and expansion (nothing is expanded, 
% as we expect).
\long\gdef\a{\Lowercase{ {} \0 { b{C} {dB\AE~}} \0{\0} }}
\expandonce\a\expandonce\a\expandonce\a
\long\gdef\b{ {} \0 { b{c} {db\ae ~}} \0{\0} }
\checkoutput

% Testing the ULec table (expanding only \csname)
\long\gdef\a{\UL_expand_csname:n{ \hello 
    {\csname Hdsf\endcsname}##1\space \csname el\endcsname{ }lo, my name}}
\expandonce\a\expandonce\a
\long\gdef\b{ \hello {\Hdsf}##1\space \el{ }lo, my name}
\checkoutput


% Custom table.
\UL_new_table:n{mytable}
\UL_setup:nnn{mytable}{h}{Hello}
\long\gdef\a{\UL_to_case:nn{mytable}{h{ h} {}\space \h}}
\expandonce\a\expandonce\a\expandonce\a\expandonce\a
\long\gdef\b{Hello{ Hello} {}\space \h}
\checkoutput


\def\mydo#1#2{(#1)-(#2)}
\long\gdef\a{\expandsome{\0\0{\expandthis\mydo{\0\expandthis\0}\0\0}}}
\expandonce\a\expandonce\a
\long\gdef\b{\0\0{(\0\1)-(\0)\0}}
\checkoutput

\long\gdef\a{\Uppercase{\NoCaseChange{The quick brown fox jumps over the lazy dog.} The quick brown fox jumps over the lazy dog. \NoCaseChange{The quick brown fox jumps over the lazy dog.} The quick brown fox jumps over the lazy dog. \NoCaseChange{The quick brown fox jumps over the lazy dog.} The quick brown fox jumps over the lazy dog. \NoCaseChange{The quick brown fox jumps over the lazy dog.} The quick brown fox jumps over the lazy dog. \NoCaseChange{The quick brown fox jumps over the lazy dog.} The quick brown fox jumps over the lazy dog. \NoCaseChange{The quick brown fox jumps over the lazy dog.} The quick brown fox jumps over the lazy dog. \NoCaseChange{The quick brown fox jumps over the lazy dog.} The quick brown fox jumps over the lazy dog. }}
\begingroup\tracingall\tracingonline=0\relax
\expandonce\a\expandonce\a\expandonce\a
\endgroup
\long\gdef\b{The quick brown fox jumps over the lazy dog. THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG. The quick brown fox jumps over the lazy dog. THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG. The quick brown fox jumps over the lazy dog. THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG. The quick brown fox jumps over the lazy dog. THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG. The quick brown fox jumps over the lazy dog. THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG. The quick brown fox jumps over the lazy dog. THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG. The quick brown fox jumps over the lazy dog. THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG. }
\checkoutput

答案3

我使用以下命令获取首字母大写的完全展开的字符串。我需要它将字符串作为消息的一部分写入 AUX 文件。Dan Luecking 很久以前在 CTT 上发布了它。该命令将\makefirstcap展开的字符串存储在 中\firstcaphold。您可以制作自己的变体。

\documentclass{article}

\def\makefirstcap#1#2\nil{%
    \iffalse{\fi
    \uppercase{\edef\firstcaphold{\iffalse}\fi#1}#2}}

\begin{document}
\makefirstcap test\nil
\show\firstcaphold
\end{document} 

答案4

我从 OP 的代码开始\ifcase,并添加了两行以创建可扩展的\euppercase宏。

\def\euppercaseB#1{\ifcase`#1\relax
     0\or 1\or 2\or 3\or 4\or 5\or 6\or 7\or 8\or 9\or
    10\or11\or12\or13\or14\or15\or16\or17\or18\or19\or
    20\or21\or22\or23\or24\or25\or26\or27\or28\or29\or
    30\or31\or32\or33\or34\or35\or36\or37\or38\or39\or
    40\or41\or42\or43\or44\or45\or46\or47\or48\or49\or
    50\or51\or52\or53\or54\or55\or56\or57\or58\or59\or
    60\or61\or62\or63\or64\or A\or B\or C\or D\or E\or
     F\or G\or H\or I\or J\or K\or L\or M\or N\or O\or
     P\or Q\or R\or S\or T\or U\or V\or W\or X\or Y\or
     Z\or91\or92\or93\or94\or95\or96\or A\or B\or C\or
     D\or E\or F\or G\or H\or I\or J\or K\or L\or M\or
     N\or O\or P\or Q\or R\or S\or T\or U\or V\or W\or
     X\or Y\or Z\or123\or124\or125\or126\or127\or128\or129\or
    \fi
}
\def\euppercase#1{\euppercaseA#1\end}
\def\euppercaseA#1{\ifx#1\end \else\euppercaseB#1\expandafter\euppercaseA\fi}

\message{aha: \euppercase{aha}.} % Prints: aha: AHA.

当然,代码\ifcase需要稍微修改一下。例如.,等应该扩展为等.,而不是等4644

相关内容