# How do I test for a character, irrespective of catcode?

This came up from Labels in TikZ are incorrectly interpreted, but it's also something I've run into with the braids package, and a couple of other things I've done. Also, from looking at the TikZ code this is something that TikZ does a lot but I don't see that it does it very elegantly!

The question is this: how do I test for a particular character in the input stream, irrespective of the category code?

The example from the braids package is fairly simple. The package there works by taking a string of the form a_2 a_3^{-1} and so forth. As with TikZ, it detects the end of the braid by looking for the ; character. But some packages, Babel for example, make that active. Frankly, I don't care if ; is active or not: it's just a placeholder for the end of the braid. So I don't want to have to bother testing all the possible variants of catcodes, I just want to know "is it a semi-colon?". I'm in a similar situation with the _ and ^ characters, you might notice.

So is there a robust (whatever that means) way to test: "Is the token that I have right now a semi-colon?".

-
I would ensure that the category code of the necessary characters is the required one at the start of the environment. This has the disadvantage of not allowing proper typesetting of text; so it depends on what is expected to be inside your environment. –  egreg Sep 21 '11 at 22:09

## 3 Answers

The \if primitive tests purely on the basis of character code, with only the need to watch out for active characters:

\long\def\example#1{%
\if\noexpand#1;%
...


and so on. Alternatively, you could use \pdfstrcmp to do a string-based comparison, and therefore not worry about only grabbing a single token.

\usepackage{pdftexcmds} % For \pdf@strcmp
\makeatletter
\long\def\example#1{%
\ifnum\pdf@strcmp{\unexpanded{#1}}{;}=\z@ % Gives 0 when TRUE
...


(See the recent how to create switch structure comparing strings in latex for more on \pdfstrcmp.)

-
I'm assuming that at the point-of-definition that ; is not active. To be extra-careful, you might use \noexpand; for that (\pdfstrcmp fully-expands it's arguments). –  Joseph Wright Sep 21 '11 at 19:21

Try this:

\documentclass{article}
\usepackage[english]{babel}
\def\isSemicolon#1{%
\ifx;#1
semicolon!
\else
Not a semicolon!
\fi}
\def\isPeriod#1{%
\ifx.#1
Period!
\else
Not a period!
\fi}
\begin{document}% babel makes ":" active here
\isSemicolon;
\isPeriod.
\end{document}


Change the definitions to a language that makes it active or to a colon which is commonly made active and it will work. The trick is the if..else construction.

-
This only works if the catcode of the character is the same when tested as when the test is defined. Move babel after the definitions and you will see that it breaks. –  Bruno Le Floch Sep 22 '11 at 22:23

Another option is to use the xstring package:

Macros of this package take the catcodes of tokens into account. To avoid unexpected behavior (particularly with tests), you should keep in mind that tokens and their catcodes are examined....

Starred macros do not take catcodes into account. They simply convert some arguments into arguments with catcodes 10, 11 and 12, and call the non-starred macros with these modified arguments.

The first example returns false, and the starred version returns true:

 \IfStrEq{\string a\string b}{ab}{true}{false}
\IfStrEq*{\string a\string b}{ab}{true}{false}

-