我尝试比较两个字符串(按字母顺序排列),但发现使用该\pdfstrcmp
命令存在一些问题:1- 因为它需要 pdftex;2- 因为它区分大小写。
第一个问题不太重要,因为我可以使用另一个编译器。
但是第二个问题对我来说很复杂。在\pdfstrcmd
命令中,所有小写字符与大写字符的比较结果相同。
因此我尝试使用字符比较\ifnum\uccode#1\uccode#2 (...)
,但答复是"! Improper alphabetic constant"
有谁知道解决这个问题的方法吗?
我的代码:
\documentclass{article}
\newcounter{auxCountGetCharAt}
\makeatletter
\def\funcGetCharAt#1#2\relax{\stepcounter{auxCountGetCharAt}\edef\@tempb{\the\value{auxCountGetCharAt}}\ifx\@tempa\@tempb#1\else{\ifx\relax#2\relax\else\funcGetCharAt#2\relax\fi}\fi}
\def\getCharAt#1#2{%
\edef\@tempa{#2}%
\setcounter{auxCountGetCharAt}{0}%
\funcGetCharAt#1\relax%
}
\newcommand\compareChars[2]{\ifnum\uccode`#1>\uccode`#2 1\else{\ifnum\uccode`#1=\uccode`#2 0\else -1\fi}\fi}
\newcommand\compareStrings[2]{%
\def\@tempc{\getCharAt{#1}{1}}%
\def\@tempd{\getCharAt{#2}{1}}%
\compareChars{\@tempc}{\@tempd}%
}
\begin{document}
\compareStrings{abcdefg}{bcdefg}
\compareStrings{Abcdefg}{bcdefg}
\compareStrings{Bbcdefg}{acdefg}
\compareStrings{bbcdefg}{acdefg}
\compareStrings{bcdefg}{bcdeag}
\end{document}
答案1
如果字符串仅由 ASCII 字符组成,则可以在比较之前将它们规范化为小写:
\documentclass{article}
\usepackage{pdftexcmds}
\makeatletter
\def\compareStrings#1#2{%
\lowercase{\edef\@tempa{\pdf@strcmp{#1}{#2}}}%
\typeout{#1 -- #2: \@tempa}%
}
\makeatother
\compareStrings{abcdefg}{bcdefg}
\compareStrings{Abcdefg}{bcdefg}
\compareStrings{Bbcdefg}{acdefg}
\compareStrings{bbcdefg}{acdefg}
\compareStrings{bcdefg}{bcdeag}
\compareStrings{ABcDeF}{aBCdef}
该pdftexcmds
包定义了\pdf@strcmp
如何使用 pdfLaTeX、XeLaTeX 和 LuaLaTeX 执行正确的操作。在所有情况下,结果都是
abcdefg -- bcdefg: -1
Abcdefg -- bcdefg: -1
Bbcdefg -- acdefg: 1
bbcdefg -- acdefg: 1
bcdefg -- bcdeag: 1
ABcDeF -- aBCdef: 0
还允许\edef
将宏传递给\compareStrings
,只要它们的扩展名由纯 ASCII 字符组成(XeLaTeX 和 LuaLaTeX 没有实际限制,但 pdfLaTeX 中的 UTF-8 字符不起作用)。
如果您需要针对不同情况执行某些操作,而不是生成 -1、0 或 1,您可以添加常规测试:
\makeatletter
\newcommand\compareStringsDo[5]{%
\lowercase{\ifcase\pdf@strcmp{#1}{#2}}%
#4\or
#5\else
#3\fi
}
\makeatother
因此,如果它们相同,则按字母顺序排列时将执行代码\compareStringsDo{<str1>}{<str2>}{<before>}{<equal>}{<after>}
,否则(在规范化为小写之后)。<before>
<str1>
<str2>
<equal>
<after>
该expl3
版本的巨大优势在于完全可扩展。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\compareStrings}{mm}
{
\str_if_eq:eeTF { \str_lower_case:f { #1 } } { \str_lower_case:f { #2 } }
{ \equalStrings{#1}{#2} }
{ \differentStrings{#1}{#2} }
}
\ExplSyntaxOff
\newcommand{\equalStrings}[2]{\typeout{Strings #1 and #2 are equal}}
\newcommand{\differentStrings}[2]{\typeout{Strings #1 and #2 are different}}
\newcommand{\test}{AbCdEF}
\compareStrings{abcdefg}{bcdefg}
\compareStrings{Abcdefg}{bcdefg}
\compareStrings{Bbcdefg}{acdefg}
\compareStrings{bbcdefg}{acdefg}
\compareStrings{bcdefg}{bcdeag}
\compareStrings{ABcDeF}{aBCdef}
\compareStrings{ABcDeF}{\test}
\stop
控制台输出:
Strings abcdefg and bcdefg are different
Strings Abcdefg and bcdefg are different
Strings Bbcdefg and acdefg are different
Strings bbcdefg and acdefg are different
Strings bcdefg and bcdeag are different
Strings ABcDeF and aBCdef are equal
Strings ABcDeF and AbCdEF are equal
答案2
古老、简单、不是真正答案的答案
[出于“历史目的”保留 - 可随时删除]
我不像LaTeX
egreg 那样是个大巫师。:)
所以我提出了一个更简单的解决方案,只需使用现有的包即可:xstring
:
\documentclass{article}
\usepackage{xstring}
\begin{document}
\IfStrEq{abcdefg}{bcdefg}{true}{false}
\IfStrEq{Abcdefg}{bcdefg}{true}{false}
\IfStrEq{Bbcdefg}{acdefg}{true}{false}
\IfStrEq{bbcdefg}{acdefg}{true}{false}
\IfStrEq{bcdefg}{bcdeag}{true}{false}
\IfStrEq{bcdeag}{bcdeag}{true}{false}
\end{document}
该命令使用起来非常简单:它需要 4 个参数,前两个是需要比较的字符串,当比较结果为真时执行第三个参数,否则将执行参数编号 4。
该包提供了更多用于特殊字符串操作的函数。
编辑:新的、更正的答案(仍在使用xstring
)
在发布我的第一个答案后,我被告知(这是理所当然的!)这个问题不仅仅是一个简单的相等性检查。而是比较一个字符串是否前或者后另一个按字母顺序经过一番思考,我想到了这个:
\documentclass{article}
\usepackage{xstring}
\usepackage[nomessages]{fp}
\newcommand{\compAlph}[2]{%
\StrCompare{#1}{#2}[\posdiff]
Difference between input strings detected at position:
\posdiff\smallskip
\StrChar{#1}{\posdiff}[\myfirstchar]
Character in the 1st string that is different:
\myfirstchar\smallskip
\StrChar{#2}{\posdiff}[\mysecondchar]
Character in the 2nd string that is different:
\mysecondchar\smallskip
\StrPosition{abcdefghijklmnopqrstuvwxyz}{\myfirstchar}[\posfirst]
\StrPosition{abcdefghijklmnopqrstuvwxyz}{\mysecondchar}[\possecond]
\FPeval{\posrel}{clip(\possecond-\posfirst)}
`Relative distance' between characters:
\posrel
}
\begin{document}
\compAlph{zyzzyzzyryx}{bcdeag}
\bigskip
\compAlph{abcdefg}{bcdefg}
\bigskip
\compAlph{bbcdefg}{acdefg}
\bigskip
\compAlph{bbcdefg}{acdefg}
\bigskip
\compAlph{bcdefg}{bcdeag}
\bigskip
\compAlph{bcdeag}{bcdeag}
\end{document}
“相对距离”实际上表示在定义的字母表中,字符之间的“距离”。如果该值为负数,则第二个单词需要“向上”移动,在另一个单词之前,以使它们按字母顺序排列,如果该值为正数,则反之亦然。
它不像 egreg 的解决方案那样优雅,并将结果打印到文档中,它也可能看起来有点粗糙,可能需要一些调整,但它是一种替代方案。:)