Alphabetic comparison of two strings

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}

-

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).

-
Why not just something like \lowercase{\ifnum\pdf@strmp{#1}{#2}}=\z@ ...? –  Joseph Wright May 30 '12 at 15:54
@JosephWright Of course. :) –  egreg May 30 '12 at 16:02

[kept for 'historical purposes' - can be deleted anytime]

I'm no big LaTeX wizard as egreg for instance. :) So I propose a much simpler solution, to simply use a package out there: 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}


The command is really simple to use: it takes 4 arguments, the first two are the strings to be compared, the 3rd is to be executed when the comparison result is true, otherwise argument number 4 will be executed.

The package provides a lot more functions for fancy string manipulations.

EDIT: New, corrected answer (still using xstring)

After posting my first answer, I was told (rightfully!) that the question was about more than just a simple equality check. Rather it is about comparing whether a string comes before or after another one alphabetically. After scratching my head a bit, I came up with this:

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


The 'relative distance' thing shows actually, 'how far apart' in the defined alphabet the characters are that differ. If the value is negative, the second word would need to move 'up', before the other word to have them in alphabetical order, and the other way around if it is positive.

It is not as elegant as egreg's solution and prints the result into the document, it also might seem a bit crude and may need a bit of tuning, but it's an alternative. :)

-
The OP, however, needs not to check for equality, but for alphabetic order. –  Daniel May 30 '12 at 16:34
@Daniel: I see... I just thought it was a simple comparison - my bad. Then xstring is not the right tool, since you need to do something like < or > operations (which it doesn't provide). I'll delete my post. Thanks anyway. :) –  Count Zero May 30 '12 at 17:17
Well... on second thought I won't! :) There is a solution using xstring. See my edit. ;)` –  Count Zero May 31 '12 at 11:04