How do I perform an expandable string comparison?
You can use expl3
; the \strcompare
macro is just a user level version of the expandable internal function \str_if_eq:nnTF
.
\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\cs_new_eq:NN \strcompare \str_if_eq:nnTF
\ExplSyntaxOff
\begin{document}
start\par
\strcompare{abc}{def}{OOPS}{OK}\par
\strcompare{abc}{abcd}{OOPS}{OK}\par
\strcompare{abcd}{abc}{OOPS}{OK}\par
\strcompare{a}{}{OOPS}{OK}\par
\strcompare{}{a}{OOPS}{OK}\par
end
\medskip
start\par
\strcompare{abc}{abc}{OK}{OOPS}\par
\strcompare{}{}{OK}{OOPS}\par
end
\end{document}
I've read that with \usepackage{pdftexcmds}
, you can use \pdf@strcmp
but haven't checked if it works in this case.
Alternatively, the code below defines \expandable@if@equal{str1}{str2}{if-case}{else-case}
. As @JavierBezos
noted in the comments, spaces are not taken into account (so "a b"
= "ab"
), and a version which takes them into account would be harder to achieve (if at all possible?). It can be used as follows:
\makeatletter
start%
\expandable@if@equal{abc}{def}{OOPS}{OK}%
\expandable@if@equal{abc}{abcd}{OOPS}{OK}%
\expandable@if@equal{abcd}{abc}{OOPS}{OK}%
\expandable@if@equal{a}{}{OOPS}{OK}%
\expandable@if@equal{}{a}{OOPS}{OK}%
end
start%
\expandable@if@equal{abc}{abc}{OK}{OOPS}%
\expandable@if@equal{}{}{OK}{OOPS}%
end
\makeatother
Which prints (notice that no extraneous space gets inserted):
startOKOKOKOKOKend
startOKOKend
Here's the definition of \expandable@if@equal
:
\makeatletter
% #1 #2 #3 #4 \sepend
% Params: {if-case}{else-case} \sepr \sepr \sepend
% Or: {if-case}{else-case} B right-…(may be *nothing*)\sepr\sepr \sepend
\def\@expandable@if@equal@empty#1#2#3#4\sepend{%
\ifx#3\sepr% #3right… = Empty string, a.k.a. "\sepr"
% First case in the signature above (#3=\sepr, #4=sepr)
#1% The strings are both empty, and therefore equal. Execute #1.
\else%
% Second case in the signature above (#3=one char, #4=right…\sepr\sepr)
#2% The right string is longer (there's at least one more character), so they are different. Execute #2.
\fi%
}%
% #1 #2 #3 #4 \sepl #5 #6 \sepr #7 \sepend
% Params: {if-case}{else-case}{left-first}{left-…} \sepl \sepr *nothing* \sepr *nothing* \sepend
% Or: {if-case}{else-case}{left-first}{left-…} \sepl B right-…(may be *nothing*) \sepr \sepr \sepend
\def\@expandable@if@equal@somechar#1#2#3#4\sepl#5#6\sepr#7\sepend{%
\ifx#5\sepr% #5#6 = Empty string, a.k.a. "\sepr"
% First case in the signature above (#5=\sepr, #6=*nothing*, #7=*nothing*)
#2% But the left string was not empty, so we run else-case
\else%
% Second case in the signature above (#5=one char, #6=right…, #7=\sepr)
\ifx#3#5% First character of left == first character of right
\@expandable@if@equal{#1}{#2}#4\sepl\sepl#6\sepr\sepr\sepend% Recursion on the rest
\else%
#2% The strings are different at this character, execute #2.
\fi%
\fi%
}%
% Two possibilities:
% #1 #2 #3 #4 \sepl #5 \sepend
% \@expandable@if@equal {if-case}{else-case} \sepl *nothing* \sepl <rest> \sepend
% \@expandable@if@equal {if-case}{else-case} A left-…(may be *nothing*) \sepl \sepl<rest> \sepend
\def\@expandable@if@equal#1#2#3#4\sepl#5\sepend{%
\ifx#3\sepl% #3#4 = Empty string, a.k.a. "\sepl"
% First case in the signature above (#3=\sepl, #4=*nothing*, #5=<rest>)
\@expandable@if@equal@empty{#1}{#2}#5\sepend%
\else%
% First case in the signature above (#3=one char, #4=left…, #5=\sepl<rest>)
\@expandable@if@equal@somechar{#1}{#2}{#3}{#4}#5\sepend%
\fi%
}
\def\expandable@if@equal#1#2#3#4{%
% \@expandable@if@equal#1\sepl#4%#2\sepl#4
%
% \@expandable@if@equal{#4}\sepl\sepl%
\@expandable@if@equal{#3}{#4}#1\sepl\sepl#2\sepr\sepr\sepend%
}%
\makeatother