可扩展地更改字母大小写并在 \csname 内使用,无需包

可扩展地更改字母大小写并在 \csname 内使用,无需包

我想更改单个字符参数的字母大小写,并在 中使用更改后的字母\csname。而且我想在不使用附加包的情况下完成此操作。

例如,我想将字母传递给宏{S},并让它执行宏\csname macros\endcsname,或者将字母传递给它{T},并让它执行宏\csname macrot\endcsname

在我的 MWE 中,我展示了三种方法:

  1. 我没想到\lowercase会成功,因为我记得读到过它直到最后一刻才真正展开。令人惊讶的是,它进入了\edef正常状态,但它一定没有真正展开,因为它破坏了\csname

  2. 我还制定了一种方法,我曾经\numexpr将 32 添加到参数的 ASCII 码中,然后\char对该结果执行。 就像 一样\lowercase,它看起来是可扩展的,但它在 内部也会失败\csname

  3. 我终于成功地完成了这个stringstrings包。写完这个包后,我知道转换是不漂亮... 本质上,它是对 形式的详尽 26 倍检查\if #1Aa\else\if #1Bb\else\if #1Cc\else...\fi\fi\fi,其优点是可以扩展为\edef,因此它可以起作用。

一定有更好的方法。MWE 之所以有效,只是因为我使用了\tmp中方法 3 的低效计算\csname。如果我使用\tmp前两种方法中的任何一种,它就会中断。

\documentclass{article}
\usepackage{stringstrings}
\def\macros{Expandably converted an S to a s}
\def\macrot{Expandably converted a T to a t}
\newcommand\convert[1]{%
  %MAKE THE ARGUMENT LOWER CASE
  1) LOWERCASE\\
  \edef\tmp{\lowercase{#1}}% PRETENDS TO EXPAND, BUT FAILS IN \csname
  Here is the ``expanded'' conversion: \tmp\\
  % OR
  2) ASCII CONVERSION\\
  \edef\tmp{\expandafter\char\numexpr`#1+32\relax}% PRETENDS TO EXPAND, BUT FAILS IN \csname
  Here is the ``expanded'' conversion: \tmp\\
  % OR
  3) STRINGSTRINGS PACKAGE\\
  \caselower[q]{#1}\def\tmp{\thestring}% WORKS BUT USES A PACKAGE
  Here is the ``expanded'' conversion: \tmp\\
  %
  Now use it in a csname: \csname macro\tmp\endcsname%
}
\begin{document}
\convert{S}\par
\convert{T}
\end{document}

在此处输入图片描述


答案摘要:

我已将 egreg 的代码片段添加到此 MWE 中以生成文档(而不仅仅是终端输出),显示到目前为止提出的方法:

\documentclass{article}

%%%%%%%%%%%%%%%%%%%%%%%%%%%% L3 APPROACH

\usepackage{expl3}

\ExplSyntaxOn
\cs_set_eq:NN \Xexplower \tl_expandable_lowercase:n
\ExplSyntaxOff

%%%%%%%%%%%%%%%%%%%%%%%%%%%% EGREG'S LESS HACKY SOLUTION

\def\explowerchar#1{%
  \ifcase\numexpr`#1-`A\relax
   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\else
   #1\fi
}

\def\explower#1{%
  \doexplowerchar#1\relax
}

\def\doexplowerchar#1{%
  \ifx#1\relax
  \else
    \explowerchar{#1}\expandafter\doexplowerchar
  \fi
}

%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEVE'S HACKY PACKAGE APPROACH

\usepackage{stringstrings}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\def\macros{Expandably converted an S to a s}
\def\macrot{Expandably converted a T to a t}

\begin{document}

\textbf{Steve's hacky approach}

\caselower[q]{S}\csname macro\thestring\endcsname\par
\caselower[q]{T}\csname macro\thestring\endcsname

\textbf{egreg's less hacky approach}

\edef\lcstring{\explowerchar{S}}\csname macro\lcstring\endcsname\par
\edef\lcstring{\explower{T}}\csname macro\lcstring\endcsname

\textbf{L3 approach}

\edef\lcstring{\Xexplower{S}}\csname macro\lcstring\endcsname\par
\edef\lcstring{\Xexplower{T}}\csname macro\lcstring\endcsname

\end{document}

在此处输入图片描述

答案1

如果你知道输入仅由类别代码为 11 或 12 的字符组成(也可能是其他字符,但绝对不是控制序列),一个不那么可怕的黑客方法是

\def\explowerchar#1{%
  \ifcase\numexpr`#1-`A\relax
   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\else
   #1\fi
}

\def\explower#1{%
  \doexplowerchar#1\relax
}

\def\doexplowerchar#1{%
  \ifx#1\relax
  \else
    \explowerchar{#1}\expandafter\doexplowerchar
  \fi
}

\edef\lcstring{\explower{AStRiNg?}}
\show\lcstring

终端上输出:

> \lcstring=macro:
->astring?.

当然需要 e-TeX。

\lowercase恐怕没有办法全面地进行扩展。

无需重新发明轮子:

\documentclass{article}
\usepackage{expl3}

\ExplSyntaxOn
\cs_set_eq:NN \explower \text_lowercase:n
\ExplSyntaxOff

\edef\lcstring{\explower{A StRiNg wiTh SpaC\'Es?}}
\show\lcstring

终端输出:

> \lcstring=macro:
->astring with spac\'es?.

答案2

我知道您要求使用无包解决方案,但以下是我们可能在 中采用的解决此问题的方法expl3。这里的“设计简介”是应该进行大小写折叠(由 Unicode 人员定义)以保留“无大小写”数据。显然,这只对 LuaTeX/XeTeX 完全可行:pdfTeX 的“后备”是将使用的数据限制为仅 ASCII 字母。请注意,此处的代码来自 Bruno:我尝试了一种更简单的方法,但下面的方法更优雅,速度更快!


字符串大小写折叠的概念现已可用,expl3\str_foldcase:n上面的“设计简介”所述。对于构造 csnames,其中大小写改变可能需要,还有和\str_uppercase:n\str_lowercase:n注意这些是独立于文本大小写转换,也可在 中使用,并且以扩展的方式使用expl3


原始代码(添加到之前expl3):请注意,当前实现从UnicodeData.txt相关源文件动态创建存储的数据。

该方法的“业务端”是设置(使用expl3语法):

\cs_new:Npn \str_fold_case:n #1
  {
    \exp_after:wN \__str_fold_auxi:w \tl_to_str:n {#1}
    { ~ \c_empty_tl } \__str_fold_end:w ? ~
  }
\cs_new:Npn \__str_fold_auxi:w #1 ~
  {
    \__str_fold_auxii:N #1 { ~ \c_space_tl }
    \__str_fold_auxi:w
  }
\cs_new:Npn \__str_fold_auxii:N #1
  {
    \exp_after:wN \__str_fold_auxiii:NNNNNNNN
    \int_use:N \__int_eval:w 1000000 + `#1 \__int_eval_end: #1
  }
\cs_new:Npn \__str_fold_auxiii:NNNNNNNN #1#2#3#4#5#6#7#8
  {
    \exp_args:NNv \str_case_x:nnF #8
      { c__str_case_#6_X_#7_tl }
      { #8 \exp_after:wN \use_none:n #8 }
    \__str_fold_auxii:N
  }
\cs_new:Npn \__str_fold_end:w ? #1 \__str_fold_auxi:w { }

因此,它最终依赖于\pdfstrcmp(隐藏在 内\str_case_x:nnF)来进行字符串比较。以上内容还需要数据:这可以从 Unicode 大小写折叠文件中自动生成,目前显示为:

\tl_const:cn {c__str_case_0_X_0_tl} {ÈèĬĭƐɛǴǵϨϩҰұԔԕḔḕṸṹỜờᾤ{ὤι}ⒸⓒⰤⱔⲈⲉꙨꙩ}
\tl_const:cn {c__str_case_0_X_1_tl} {ÉéƑƒჍⴭᾥ{ὥι}ⒹⓓⰥⱕⳭⳮ}
\tl_const:cn {c__str_case_0_X_2_tl} {ÊêĮįǶƕΆάϪϫҲҳԖԗḖḗṺṻỞởᾦ{ὦι}ⒺⓔⰦⱖⲊⲋꙪꙫꜲꜳ}
\tl_const:cn {c__str_case_0_X_3_tl} {ËëƓɠǷƿᾧ{ὧι}ⒻⓕⰧⱗ}
\tl_const:cn {c__str_case_0_X_4_tl} {Ììİ{i̇}ƔɣǸǹΈέϬϭҴҵԘԙḘḙṼṽỠỡᾨ{ὠι}ⒼⓖⰨⱘⲌⲍꙬꙭꜴꜵ}
\tl_const:cn {c__str_case_0_X_5_tl} {ÍíΉήᾩ{ὡι}ⒽⓗⰩⱙ}
\tl_const:cn {c__str_case_0_X_6_tl} {ÎîIJijƖɩǺǻΊίϮϯҶҷԚԛḚḛṾṿỢợᾪ{ὢι}ⒾⓘⰪⱚⲎⲏⳲⳳꜶꜷ}
\tl_const:cn {c__str_case_0_X_7_tl} {ÏïƗɨᾫ{ὣι}ⒿⓙⰫⱛ}
\tl_const:cn {c__str_case_0_X_8_tl} {ÐðĴĵƘƙǼǽΌόϰκҸҹԜԝḜḝẀẁỤụὈὀᾬ{ὤι}ⓀⓚⰬⱜⲐⲑꜸꜹ}
\tl_const:cn {c__str_case_0_X_9_tl} {ÑñϱρὉὁᾭ{ὥι}ⓁⓛⰭⱝ}
\tl_const:cn {c__str_case_1_X_0_tl} {ÒòĶķǾǿΎύҺһԞԟḞḟẂẃỦủὊὂᾮ{ὦι}ⓂⓜⰮⱞⲒⲓꜺꜻ}
\tl_const:cn {c__str_case_1_X_1_tl} {ÓóΏώὋὃᾯ{ὧι}Ⓝⓝ}
\tl_const:cn {c__str_case_1_X_2_tl} {ÔôƜɯȀȁΐ{ΐ}ϴθҼҽԠԡḠḡẄẅỨứὌὄⓄⓞⲔⲕꜼꜽꞠꞡ}
\tl_const:cn {c__str_case_1_X_3_tl} {ÕõĹĺƝɲΑαϵεὍὅⓅⓟAa}
\tl_const:cn {c__str_case_1_X_4_tl} {ÖöȂȃΒβҾҿԢԣḢḣẆẇỪừᾲ{ὰι}ⓆⓠⲖⲗꜾꜿꞢꞣBb}
\tl_const:cn {c__str_case_1_X_5_tl} {ĻļƟɵΓγϷϸև{եւ}ᾳ{αι}ⓇⓡCc}
\tl_const:cn {c__str_case_1_X_6_tl} {ØøƠơȄȅΔδӀӏԤԥḤḥẈẉỬửὐ{ὐ}ᾴ{άι}ⓈⓢⲘⲙꝀꝁꞤꞥDd}
\tl_const:cn {c__str_case_1_X_7_tl} {ÙùĽľΕεϹϲӁӂⓉⓣEe}
\tl_const:cn {c__str_case_1_X_8_tl} {ÚúƢƣȆȇΖζϺϻԦԧḦḧẊẋỮữὒ{ὒ}ᾶ{ᾶ}ⓊⓤⲚⲛꝂꝃꞦꞧFf}
\tl_const:cn {c__str_case_1_X_9_tl} {ÛûĿŀΗηӃӄᾷ{ᾶι}ⓋⓥGg}
\tl_const:cn {c__str_case_2_X_0_tl} {ÜüƤƥȈȉΘθѠѡḨḩẌẍỰựὔ{ὔ}ᾸᾰⓌⓦⲜⲝꝄꝅꞨꞩHh}
\tl_const:cn {c__str_case_2_X_1_tl} {ÝýŁłΙιϽͻӅӆᾹᾱⓍⓧIi}
\tl_const:cn {c__str_case_2_X_2_tl} {ÞþƦʀȊȋΚκϾͼѢѣḪḫẎẏỲỳὖ{ὖ}ᾺὰⓎⓨⲞⲟꝆꝇꞪɦJj}
\tl_const:cn {c__str_case_2_X_3_tl} {ß{ss}ŃńƧƨΛλϿͽӇӈΆάⓏⓩKk}
\tl_const:cn {c__str_case_2_X_4_tl} {ȌȍΜμЀѐѤѥḬḭẐẑỴỵᾼ{αι}ⲠⲡꚀꚁꝈꝉLl}
\tl_const:cn {c__str_case_2_X_5_tl} {ŅņƩʃΝνЁёӉӊὙὑMm}
\tl_const:cn {c__str_case_2_X_6_tl} {ȎȏΞξЂђѦѧḮḯẒẓỶỷιιⲢⲣꚂꚃꝊꝋNn}
\tl_const:cn {c__str_case_2_X_7_tl} {ŇňΟοЃѓӋӌὛὓOo}
\tl_const:cn {c__str_case_2_X_8_tl} {ƬƭȐȑΠπЄєѨѩḰḱẔẕỸỹⲤⲥꚄꚅꝌꝍPp}
\tl_const:cn {c__str_case_2_X_9_tl} {ʼn{ʼn}ΡρЅѕӍӎԱաὝὕQq}
\tl_const:cn {c__str_case_3_X_0_tl} {ŊŋƮʈȒȓІіѪѫԲբḲḳẖ{ẖ}Ỻỻῂ{ὴι}ⲦⲧꚆꚇꝎꝏRr}
\tl_const:cn {c__str_case_3_X_1_tl} {ƯưΣσЇїԳգẗ{ẗ}Ὗὗῃ{ηι}Ss}
\tl_const:cn {c__str_case_3_X_2_tl} {ŌōȔȕΤτЈјѬѭӐӑԴդḴḵẘ{ẘ}Ỽỽῄ{ήι}ⲨⲩꚈꚉꝐꝑTt}
\tl_const:cn {c__str_case_3_X_3_tl} {ƱʊΥυЉљԵեẙ{ẙ}Uu}
\tl_const:cn {c__str_case_3_X_4_tl} {ŎŏƲʋȖȗΦφЊњѮѯӒӓԶզḶḷẚ{aʾ}Ỿỿῆ{ῆ}ⲪⲫꚊꚋꝒꝓVv}
\tl_const:cn {c__str_case_3_X_5_tl} {ƳƴΧχЋћԷէẛṡῇ{ῆι}Ww}
\tl_const:cn {c__str_case_3_X_6_tl} {ŐőȘșΨψЌќѰѱӔӕԸըḸḹῈὲⲬⲭꚌꚍꝔꝕXx}
\tl_const:cn {c__str_case_3_X_7_tl} {ƵƶͅιΩωЍѝԹթΈέYy}
\tl_const:cn {c__str_case_3_X_8_tl} {ŒœȚțΪϊЎўѲѳӖӗԺժḺḻẞ{ss}ῊὴⲮⲯꚎꚏꝖꝗZz}
\tl_const:cn {c__str_case_3_X_9_tl} {ƷʒΫϋЏџԻիΉή}
\tl_const:cn {c__str_case_4_X_0_tl} {ŔŕƸƹȜȝАаѴѵӘәԼլḼḽẠạὨὠῌ{ηι}ⲰⲱꚐꚑꝘꝙ}
\tl_const:cn {c__str_case_4_X_1_tl} {БбԽխὩὡ}
\tl_const:cn {c__str_case_4_X_2_tl} {ŖŗȞȟВвѶѷӚӛԾծḾḿẢảὪὢⲲⲳꚒꚓꝚꝛ}
\tl_const:cn {c__str_case_4_X_3_tl} {ГгԿկὫὣ}
\tl_const:cn {c__str_case_4_X_4_tl} {ŘřƼƽȠƞΰ{ΰ}ДдѸѹӜӝՀհṀṁẤấἈἀὬὤⅠⅰⲴⲵꚔꚕꝜꝝ}
\tl_const:cn {c__str_case_4_X_5_tl} {ЕеՁձἉἁὭὥⅡⅱ}
\tl_const:cn {c__str_case_4_X_6_tl} {ŚśȢȣЖжѺѻӞӟՂղṂṃẦầἊἂὮὦῒ{ῒ}ⅢⅲⲶⲷꚖꚗꝞꝟ}
\tl_const:cn {c__str_case_4_X_7_tl} {ЗзՃճἋἃὯὧΐ{ΐ}Ⅳⅳ}
\tl_const:cn {c__str_case_4_X_8_tl} {ŜŝȤȥИиѼѽӠӡՄմṄṅẨẩἌἄⅤⅴⲸⲹꝠꝡ}
\tl_const:cn {c__str_case_4_X_9_tl} {ЙйՅյἍἅⅥⅵ}
\tl_const:cn {c__str_case_5_X_0_tl} {ŞşȦȧКкѾѿӢӣՆնṆṇẪẫἎἆῖ{ῖ}ⅦⅶⲺⲻꝢꝣ}
\tl_const:cn {c__str_case_5_X_1_tl} {ЛлՇշἏἇῗ{ῗ}Ⅷⅷ}
\tl_const:cn {c__str_case_5_X_2_tl} {ŠšDŽdžȨȩМмҀҁӤӥՈոṈṉẬậῘῐⅨⅸⲼⲽꝤꝥ}
\tl_const:cn {c__str_case_5_X_3_tl} {DždžНнՉչῙῑⅩⅹ}
\tl_const:cn {c__str_case_5_X_4_tl} {ŢţȪȫОоӦӧՊպṊṋẮắῚὶⅪⅺⲾⲿꝦꝧ}
\tl_const:cn {c__str_case_5_X_5_tl} {LJljПпՋջΊίⅫⅻ}
\tl_const:cn {c__str_case_5_X_6_tl} {ĀāŤťLjljȬȭРрӨөՌռႠⴀṌṍẰằⅬⅼⳀⳁꝨꝩff{ff}}
\tl_const:cn {c__str_case_5_X_7_tl} {СсՍսႡⴁⅭⅽfi{fi}}
\tl_const:cn {c__str_case_5_X_8_tl} {ĂăŦŧNJnjȮȯТтӪӫՎվႢⴂṎṏẲẳⅮⅾⳂⳃꝪꝫfl{fl}}
\tl_const:cn {c__str_case_5_X_9_tl} {NjnjУуՏտႣⴃⅯⅿffi{ffi}}
\tl_const:cn {c__str_case_6_X_0_tl} {ĄąŨũȰȱФфӬӭՐրႤⴄṐṑẴẵἘἐⱠⱡⳄⳅꙀꙁꝬꝭffl{ffl}

答案3

由于我在提出这个问题 9 个月后一直在研究这个问题,并开发出了一种不同的方法,所以我将在这里发布它。它通过激活大写或小写字符,然后在其他字母大小写中重新定义它们来实现结果。

它实现了问题的目的,即“我想将字母传递给宏{S},并让它执行宏\csname macros\endcsname,或者将字母传递给它{T},并让它执行宏\csname macrot\endcsname”。

然而,它仍然有局限性,其中最大的两个是:

  1. 我无法对\def已编辑的信息进行操作。因此,我可以说\macrofylc{T}并让它执行\csname macrot\endcsname,但我不能说\def\x{T}\macrofylc{\x}

  2. 它可以处理参数中的宏,但前提是宏以未被激活的字母命名。因此,我可以\expresslc{This is An \textit{ITALIC} test}得到“这是一个斜体测试”,但为了将其变为大写,我需要以下解决方案:\let\TEXTIT\textit\expressuc{This is An \TEXTIT{ITALIC} test}要获得“THIS IS AN斜体测试”

请注意,由于我们讨论的不是正则表达式,因此限制 #2 并不比 LaTeX2e 中的当前情况糟糕很多。不过,我非常想弥补限制 #1,但目前还不知道如何弥补。

以下是 MWE:

\documentclass{article}
\def\setuc{%
\8101\9`A=\8102\9\8101\9`B=\8102\9\8101\9`C=\8102\9\8101\9`D=\8102\9%
\8101\9`E=\8102\9\8101\9`F=\8102\9\8101\9`G=\8102\9\8101\9`H=\8102\9%
\8101\9`I=\8102\9\8101\9`J=\8102\9\8101\9`K=\8102\9\8101\9`L=\8102\9%
\8101\9`M=\8102\9\8101\9`N=\8102\9\8101\9`O=\8102\9\8101\9`P=\8102\9%
\8101\9`Q=\8102\9\8101\9`R=\8102\9\8101\9`S=\8102\9\8101\9`T=\8102\9%
\8101\9`U=\8102\9\8101\9`V=\8102\9\8101\9`W=\8102\9\8101\9`X=\8102\9%
\8101\9`Y=\8102\9\8101\9`Z=\8102\9%
}
\def\setlc{%
\8101\9`a=\8102\9\8101\9`b=\8102\9\8101\9`c=\8102\9\8101\9`d=\8102\9%
\8101\9`e=\8102\9\8101\9`f=\8102\9\8101\9`g=\8102\9\8101\9`h=\8102\9%
\8101\9`i=\8102\9\8101\9`j=\8102\9\8101\9`k=\8102\9\8101\9`l=\8102\9%
\8101\9`m=\8102\9\8101\9`n=\8102\9\8101\9`o=\8102\9\8101\9`p=\8102\9%
\8101\9`q=\8102\9\8101\9`r=\8102\9\8101\9`s=\8102\9\8101\9`t=\8102\9%
\8101\9`u=\8102\9\8101\9`v=\8102\9\8101\9`w=\8102\9\8101\9`x=\8102\9%
\8101\9`y=\8102\9\8101\9`z=\8102\9%
}
\def\resetuc{%
\8101\9`A=11\8101\9`B=11\8101\9`C=11\8101\9`D=11%
\8101\9`E=11\8101\9`F=11\8101\9`G=11\8101\9`H=11%
\8101\9`I=11\8101\9`J=11\8101\9`K=11\8101\9`L=11%
\8101\9`M=11\8101\9`N=11\8101\9`O=11\8101\9`P=11%
\8101\9`Q=11\8101\9`R=11\8101\9`S=11\8101\9`T=11%
\8101\9`U=11\8101\9`V=11\8101\9`W=11\8101\9`X=11%
\8101\9`Y=11\8101\9`Z=11%
}
\def\resetlc{%
\8101\9`a=11\8101\9`b=11\8101\9`c=11\8101\9`d=11%
\8101\9`e=11\8101\9`f=11\8101\9`g=11\8101\9`h=11%
\8101\9`i=11\8101\9`j=11\8101\9`k=11\8101\9`l=11%
\8101\9`m=11\8101\9`n=11\8101\9`o=11\8101\9`p=11%
\8101\9`q=11\8101\9`r=11\8101\9`s=11\8101\9`t=11%
\8101\9`u=11\8101\9`v=11\8101\9`w=11\8101\9`x=11%
\8101\9`y=11\8101\9`z=11%
}
\let\8\csname
\let\9\endcsname
\expandafter\let\csname101\endcsname\catcode
\expandafter\let\csname102\endcsname\active
\expandafter\let\csname103\endcsname\gdef
\expandafter\let\csname104\endcsname\resetuc
\expandafter\let\csname105\endcsname\resetlc
%%%%%%%%%%%%%%%%%%%%%%%%%
\def\macrofylc{\setuc\macrotransform}
\def\macrofyuc{\setlc\macrotransform}
\def\expresslc{\setuc\transform}
\def\expressuc{\setlc\transform}
\def\deflc{\setuc\deftransform}
\def\defuc{\setlc\deftransform}
%%%%%%%%%%%%%%%%%%%%%%%%%
\def\macrotransform#1{\csname macro#1\endcsname\resetuc\resetlc}
\def\transform#1{#1\resetuc\resetlc}
\def\deftransform#1{\def\thestring{#1}\resetuc\resetlc}
%%%%%%%%%%%%%%%%%%%%%%%%%
\setuc
% REDEFINE uc TO LOWERCASE
\8103\9A{a}\8103\9B{b}\8103\9C{c}\8103\9D{d}\8103\9E{e}\8103\9F{f}%
\8103\9G{g}\8103\9H{h}\8103\9I{i}\8103\9J{j}\8103\9K{k}\8103\9L{l}%
\8103\9M{m}\8103\9N{n}\8103\9O{o}\8103\9P{p}\8103\9Q{q}\8103\9R{r}%
\8103\9S{s}\8103\9T{t}\8103\9U{u}\8103\9V{v}\8103\9W{w}\8103\9X{x}%
\8103\9Y{y}\8103\9Z{z}%
\8104\9% \resetuc
%%%%%%%%%%%%%%%%%%%%%%%%%
\setlc
% REDEFINE LOWERCASE TO uc
\8103\9a{A}\8103\9b{B}\8103\9c{C}\8103\9d{D}\8103\9e{E}\8103\9f{F}%
\8103\9g{G}\8103\9h{H}\8103\9i{I}\8103\9j{J}\8103\9k{K}\8103\9l{L}%
\8103\9m{M}\8103\9n{N}\8103\9o{O}\8103\9p{P}\8103\9q{Q}\8103\9r{R}%
\8103\9s{S}\8103\9t{T}\8103\9u{U}\8103\9v{V}\8103\9w{W}\8103\9x{X}%
\8103\9y{Y}\8103\9z{Z}%
\8105\9% \resetlc
%%%%%%%%%%%%%%%%%%%%%%%%%
% THESE WILL TELL ME IF I HAVE SUCCEEDED OR NOT
\def\macrot{You have executed macrot}
\def\macroT{You have executed macroT}
\def\macrost{You have executed macrost}
\def\macroST{You have executed macroST}
\begin{document}
\textbf{Express directly: lc, then uc}\par
\expresslc{This IS a \textbf{Bold} \textit{Italic} test}\par
\let\TEXTBF\textbf\let\TEXTIT\textit% THIS IS A CHEAT
\expressuc{This IS a \TEXTBF{Bold} \TEXTIT{Italic} test that cheats.}\par

\textbf{Macrofy lc:}\par
\macrofylc{T}\par
\macrofylc{t}\par
\macrofylc{ST}\par
\macrofylc{St}\par
\macrofylc{sT}\par
\textbf{Macrofy uc:}\par
\macrofyuc{T}\par
\macrofyuc{t}\par
\macrofyuc{st}\par
\macrofyuc{sT}\par
\macrofyuc{St}\par

\textbf{Place in def:}\par
\deflc{This IS a \textbf{Bold} CASE}
Here is thestring stored by deflc: \thestring\par
\textit{UPPERCASE and lowercase are restored.}\par
\end{document}

在此处输入图片描述

答案4

我所能做的就是提供一个例程\UD@ExpandableLowercase,该例程在两个扩展步骤内将与拉丁字母表的 26 个字母相对应的 26 个 catcode-11(字母)字符标记转换为小写。

(!!!请注意,让 TeX 读取和标记由拉丁字母组成的输入通常会产生 catcode-11(字母)字符标记,而扩展\string、、\meaning\jobmane类似基元会产生 catcode-12(其他)字符标记!!!
作为规则的例外,使用\string、、\meaning\jobmane类似基元空格将是 catcode-10(空格)字符代码-32 空格标记。)

该例程不需要 eTeX 或类似的扩展。

作为(可能不喜欢的)副作用,该例程确实将匹配的 catcode-1-character-tokens 和 catcode-2-character-tokens 对替换为匹配的花括号 catcode-1-brace-tokens 和花括号 catcode-2-brace-tokens 对,即{}

我认为这对于纯 TeX 和 LaTeX2e 来说应该不是问题,因为在这些格式中,通常类别代码为 1 的唯一字符是左花括号{,而类别代码为 2 的唯一字符是右花括号}


在此处输入图片描述


\documentclass{article}

\makeatletter
%%-----------------------------------------------------------------------------
%% Paraphernalia:
%%.............................................................................
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@Exchange[2]{#2#1}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@removespace{}\UD@firstoftwo{\def\UD@removespace}{} {}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that
%%                       argument which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that
%%                       argument which is to be checked is not empty>}%
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
  \UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is a catcode-1-character
%%.............................................................................
%% \UD@CheckWhetherBrace{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has leading
%%                        catcode-1-token>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has no leading
%%                        catcode-1-token>}%
\newcommand\UD@CheckWhetherBrace[1]{%
  \romannumeral0\expandafter\UD@secondoftwo\expandafter{\expandafter{%
  \string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
  \UD@firstoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with a space-token
%%.............................................................................
%% \UD@CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked>'s 1st token is a
%%                               space-token>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked>'s 1st token is not
%%                               a space-token>}%
\newcommand\UD@CheckWhetherLeadingSpace[1]{%
  \romannumeral0\UD@CheckWhetherNull{#1}%
  {\expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo}%
  {\expandafter\UD@secondoftwo\string{\UD@CheckWhetherLeadingSpaceB.#1 }{}}%
}%
\newcommand\UD@CheckWhetherLeadingSpaceB{}%
\long\def\UD@CheckWhetherLeadingSpaceB#1 {%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@secondoftwo#1{}}%
  {\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
  {\UD@Exchange{ }{\expandafter\expandafter\expandafter\expandafter
   \expandafter\expandafter\expandafter}\expandafter\expandafter
   \expandafter}\expandafter\UD@secondoftwo\expandafter{\string}%
}%
%%-----------------------------------------------------------------------------
%% \romannumeral\UD@Expandtimes{<number K>}<token sequence>
%%   -> K times the leading token of <token sequence> will be "hit" by
%%      \expandafter .
%%.............................................................................
\newcommand\UD@innerdfork{}\def\UD@innerdfork#1d#2#3dd{#2}%
\newcommand*\UD@dfork[1]{\UD@innerdfork#1{\UD@firstoftwo}d{\UD@secondoftwo}dd}%
\newcommand*\UD@Expandtimes[1]{%
  0\expandafter\UD@innerExp
   \expandafter{\expandafter}\romannumeral\number\number#1 000d%
}
\newcommand*\UD@innerExp[2]{%
  \UD@dfork{#2}{#1 }{\UD@innerExp{#1#1\expandafter}}%
}%
%%-----------------------------------------------------------------------------
%% Fully expandable for-loop:
%%.............................................................................
%% \UD@DoWithEachElementOfArgumentList{{<number K>}<action>}%
%%                                   {{<number L>}{<number M>}<action if element is nested in braces>}%
%%                                   {{<number N>}<action if list empty>}%
%%                                   {<e_k><e_(k+1)>{<e_(k+2)>}..<e_n>}%<-This is the list of elements/undelimited arguments.
%%
%%  yields (after _two_ expansion-steps) :
%%
%%  IF <number L> < 1 THEN:
%%
%%  result of <number K> \expandafter-hits on <action>{<e_k>}%
%%  result of <number K> \expandafter-hits on <action>{<e_(k+1)>}%
%%  result of <number M> \expandafter-hits on <action if element is nested in braces>{<e_(k+2)>}%
%%  ...
%%  result of <number K> \expandafter-hits on <action>{<e_n>}%
%%  result of <number N> \expandafter-hits on <action if list empty>%
%%
%%  IF <number L> >= 1 THEN:
%%
%%  result of <number K> \expandafter-hits on <action>{<e_k>}%
%%  result of <number K> \expandafter-hits on <action>{<e_(k+1)>}%
%%  {result of <number M> \expandafter-hits on <action if element is nested in braces>{<e_(k+1)>}}%
%%  ...
%%  result of <number K> \expandafter-hits on <action>{<e_n>}%
%%  result of <number N> \expandafter-hits on <action if list empty>%
%%
%% The <action if element is nested in braces>-argument can be used for running the
%% \UD@DoWithEachElementOfArgumentList-loop on the single components of elements
%% that consist of more than one token and therefore are nested in curly braces.
%% If <number M> >= 1, braces hereby will be preserved, otherwise they will be removed.
%%
%% <number K>, <number L>, <number N> denote the desired level of expansion.
%%
\newcommand\UD@KeepOnlyFirstBeforeSeLDoM{}%
\long\def\UD@KeepOnlyFirstBeforeSeLDoM#1#2\UD@SeLDoM{{#1}}%
\newcommand\UD@DoWithEachElementOfArgumentList{%
  \romannumeral0\UD@MoveElementFromList{}{ }{}%
}%
%% \UD@MoveElementFromList[7]
%%  Argument 1: {<indicator whether \UD@MoveElementFromList is in the
%%              loop for extracting the first element of the list or is
%%              ready to process the first element of the list and add
%%              the processing-result to <tokens collected so far> >}%
%%              If in the loop for extracting the first element, then
%%              Argument 1 / #1 is not empty.
%%  Argument 2: {<tokens collected so far>}%
%%  Argument 3: {<indicator whether first element of list starts with
%%               catcode-1-opening brace>}. 
%%               If so, argument 3/ #3=".", else argument 3/ #3 empty.
%%  Argument 4: {{<number K>}<action>}%
%%  Argument 5: {{<number L>}{<number M>}<action if element is nested 
%%               in braces>}%
%%  Argument 6: {{<number N>}<action if list empty>}%
%%  Argument 7: {<e_k><e_(k+1)>{<e_(k+2)>}..<e_n>}%<- this holds the
%%              list of elements/undelimited arguments
\newcommand\UD@MoveElementFromList[7]{%
  \UD@CheckWhetherNull{#1}{%
    \UD@CheckWhetherNull{#7}{%
      \expandafter\UD@Exchange\expandafter{\romannumeral\UD@Expandtimes#6}{#2}%
    }{%
      \UD@CheckWhetherLeadingSpace{#7}{%
        \expandafter\UD@PassFirstToSecond
        \expandafter{\UD@removespace#7}{%
          \expandafter\expandafter\expandafter\UD@PassFirstToSecond
          \expandafter\expandafter\expandafter{%
            \expandafter\UD@Exchange
            \expandafter{\romannumeral\UD@Expandtimes#4{ }}{#2}%
          }{\UD@MoveElementFromList{}}{}{#4}{#5}{#6}%
        }%
      }{%
        \UD@CheckWhetherBrace{#7}{%
          \expandafter\expandafter\expandafter\UD@MoveElementFromList
          \expandafter\UD@PassFirstToSecond
          \expandafter{\UD@firstoftwo{}#7}%
          {{#7\UD@SeLDoM}{#2}{.}{#4}{#5}{#6}}%
        }{%
          \expandafter\expandafter\expandafter\UD@MoveElementFromList
          \expandafter\UD@PassFirstToSecond
          \expandafter{\UD@firstoftwo{}#7}%
          {{#7\UD@SeLDoM}{#2}{}{#4}{#5}{#6}}%
        }%
      }%
    }%
  }{%
    \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
    {%
      \UD@CheckWhetherNull{#3}{%
        \expandafter\expandafter\expandafter\UD@PassFirstToSecond
        \expandafter\expandafter\expandafter{%
          \expandafter\UD@Exchange
          \expandafter{\romannumeral\UD@Expandtimes#4#1}{#2}}%
       }{%
        \expandafter\expandafter\expandafter\UD@PassFirstToSecond
        \expandafter\expandafter\expandafter{%
          \csname UD@\ifnum1>\expandafter\UD@firstoftwo
                             \UD@KeepOnlyFirstBeforeSeLDoM#5\UD@SeLDoM{} %
                            Exchange%
                     \else PassFirstToSecond\fi\expandafter\endcsname
          \expandafter{\romannumeral\expandafter\UD@Expandtimes
                                    \UD@firstoftwo{}#5#1}{#2}}%
      }%
      {\UD@MoveElementFromList{}}{}%
    }%
    {\expandafter\UD@MoveElementFromList
     \expandafter{\UD@KeepOnlyFirstBeforeSeLDoM#1}{#2}{#3}%
    }{#4}{#5}{#6}{#7}%
  }%
}%
%%
%%-----------------------------------------------------------------------------
%% Fork depending on some characters:
%%.............................................................................
\newcommand\UD@@ForkCharacters{}%
\long\def\UD@@ForkCharacters#1ABCDEFGHIJKLMNOPQRSTUVWXYZ#2#3AAA{#2}%
%
\newcommand\UD@ForkCharacter[1]{%
  \UD@@ForkCharacters
  #1BCDEFGHIJKLMNOPQRSTUVWXYZ{a}%
  A#1CDEFGHIJKLMNOPQRSTUVWXYZ{b}%
  AB#1DEFGHIJKLMNOPQRSTUVWXYZ{c}%
  ABC#1EFGHIJKLMNOPQRSTUVWXYZ{d}%
  ABCD#1FGHIJKLMNOPQRSTUVWXYZ{e}%
  ABCDE#1GHIJKLMNOPQRSTUVWXYZ{f}%
  ABCDEF#1HIJKLMNOPQRSTUVWXYZ{g}%
  ABCDEFG#1IJKLMNOPQRSTUVWXYZ{h}%
  ABCDEFGH#1JKLMNOPQRSTUVWXYZ{i}%
  ABCDEFGHI#1KLMNOPQRSTUVWXYZ{j}%
  ABCDEFGHIJ#1LMNOPQRSTUVWXYZ{k}%
  ABCDEFGHIJK#1MNOPQRSTUVWXYZ{l}%
  ABCDEFGHIJKL#1NOPQRSTUVWXYZ{m}%
  ABCDEFGHIJKLM#1OPQRSTUVWXYZ{n}%
  ABCDEFGHIJKLMN#1PQRSTUVWXYZ{o}%
  ABCDEFGHIJKLMNO#1QRSTUVWXYZ{p}%
  ABCDEFGHIJKLMNOP#1RSTUVWXYZ{q}%
  ABCDEFGHIJKLMNOPQ#1STUVWXYZ{r}%
  ABCDEFGHIJKLMNOPQR#1TUVWXYZ{s}%
  ABCDEFGHIJKLMNOPQRS#1UVWXYZ{t}%
  ABCDEFGHIJKLMNOPQRST#1VWXYZ{u}%
  ABCDEFGHIJKLMNOPQRSTU#1WXYZ{v}%
  ABCDEFGHIJKLMNOPQRSTUV#1XYZ{w}%
  ABCDEFGHIJKLMNOPQRSTUVW#1YZ{x}%
  ABCDEFGHIJKLMNOPQRSTUVWX#1Z{y}%
  ABCDEFGHIJKLMNOPQRSTUVWXY#1{z}%
  ABCDEFGHIJKLMNOPQRSTUVWXYZ{#1}%
  AAA%
}%
%%-----------------------------------------------------------------------------
%% Apply the \UD@DoWithEachElementOfArgumentList-for-loop to \UD@ForkCharacter
%%.............................................................................
\newcommand\UD@ExpandableLowercase{%
  \romannumeral
  \expandafter\UD@secondoftwo
  \UD@DoWithEachElementOfArgumentList{{2}\UD@ForkCharacter}%
                                     {{1}{2}\UD@ExpandableLowercase}%
                                     {{0}}%
}%

\begin{document}
\ttfamily\footnotesize

In order to show that \string\UD@ExpandableLowercase{} delivers the 
result after two expansion steps have TeX hit the sequence\\[\medskipamount]%
\string\UD@ExpandableLowercase\string{ABC DEF \string{GHI%
\string{J K LM\string{NOP QR\string}\string#1STUV\string}WXYZ\string}%
ABCDEFGHIJKLMNOPQRSTUVWXYZ\string#1\string}\\[\medskipamount]%
by two \string\expandafter-chains reaching into the definition-text
of the macro \string\test:%

\medskip

\expandafter\expandafter
\expandafter            \def
\expandafter\expandafter
\expandafter            \test
\expandafter\expandafter
\expandafter            #%
\expandafter\expandafter
\expandafter            1%
\expandafter\expandafter
\expandafter           {%
  \UD@ExpandableLowercase{ABC DEF {GHI{J K LM{NOP QR}#1STUV}WXYZ}ABCDEFGHIJKLMNOPQRSTUVWXYZ#1}%
}%

\noindent[\string\test=\meaning\test]

\end{document}

相关内容