[Tex/LaTex] Alphabetic comparison of two strings

comparisonstrings

I tried to compare two strings (alphabetically), but I found some problems using the \pdfstrcmp command: 1- because it needs pdftex; 2- because it is case sensitive.

The first problem is not so relevant, because I can use another compiler.

However the second problem is complicated to me. In \pdfstrcmd command all lowercase chars in comparison with an uppercase char has the same answer.

So I tried to use the comparison of chars using \ifnum\uccode#1\uccode#2 (...), but the reply was "! Improper alphabetic constant"

Is there anyone who knows a solution to this problem?

My code:

\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}

Best Answer

If the strings are made only of ASCII characters, you can normalize them to lowercase before comparing:

\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}

The pdftexcmds package defines \pdf@strcmp to do the correct thing with pdfLaTeX, XeLaTeX and LuaLaTeX. In all cases the result is

abcdefg -- bcdefg: -1
Abcdefg -- bcdefg: -1
Bbcdefg -- acdefg: 1
bbcdefg -- acdefg: 1
bcdefg -- bcdeag: 1
ABcDeF -- aBCdef: 0

The \edef allows also to pass macros to \compareStrings, as long as their expansion consists of plain ASCII characters (there's no real restriction for XeLaTeX and LuaLaTeX, but UTF-8 characters in pdfLaTeX wouldn't work).

If you need to execute something for the different cases, instead of producing –1, 0 or 1, you can add the usual test:

\makeatletter
\newcommand\compareStringsDo[5]{%
  \lowercase{\ifcase\pdf@strcmp{#1}{#2}}%
    #4\or
    #5\else
    #3\fi
}
\makeatother

So with \compareStringsDo{<str1>}{<str2>}{<before>}{<equal>}{<after>} the <before> code will be executed when <str1> comes before <str2> in the alphabetical order, <equal> if they are the same and <after> otherwise (after normalization to lowercase).

An expl3 version that has the big advantage of being fully expandable.

\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

The console output:

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
Related Question