我想更改单个字符参数的字母大小写,并在 中使用更改后的字母\csname
。而且我想在不使用附加包的情况下完成此操作。
例如,我想将字母传递给宏{S}
,并让它执行宏\csname macros\endcsname
,或者将字母传递给它{T}
,并让它执行宏\csname macrot\endcsname
。
在我的 MWE 中,我展示了三种方法:
我没想到
\lowercase
会成功,因为我记得读到过它直到最后一刻才真正展开。令人惊讶的是,它进入了\edef
正常状态,但它一定没有真正展开,因为它破坏了\csname
。我还制定了一种方法,我曾经
\numexpr
将 32 添加到参数的 ASCII 码中,然后\char
对该结果执行。 就像 一样\lowercase
,它看起来是可扩展的,但它在 内部也会失败\csname
。我终于成功地完成了这个
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
”。
然而,它仍然有局限性,其中最大的两个是:
我无法对
\def
已编辑的信息进行操作。因此,我可以说\macrofylc{T}
并让它执行\csname macrot\endcsname
,但我不能说\def\x{T}\macrofylc{\x}
。它可以处理参数中的宏,但前提是宏以未被激活的字母命名。因此,我可以
\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}