7

I am trying to test one char substrings (TeX or LaTeX):

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}

\usepackage{fp,stringstrings}

\newcommand*{\mytest}[1]{%
  \def\firstChar{\substring{#1}{1}{1}}%
  \ifx M\firstChar\textit{M has been recognized}\else\textbf{M has NOT been recognized, firstChar='\firstChar'}\fi
}

\begin{document}\thispagestyle{empty}
Test with Matching: \mytest{Matching}\par
Test with M:  \mytest{M}\par
Test with No: \mytest{No}\par
Test with N:  \mytest{N}\par
\end{document}

Expected result is "M has been recognized" for the first two calls. What I obtain is:enter image description here

How to compare against a constant with ifx ?

2 Answers 2

6

\ifx compares the meaning of the two following tokens without expanding them. So you're testing whether the token M has the same meaning as \firstChar and they don't, even if \def\firstChar{M} has been issued, because M is a character and \firstChar is a macro.

You have to expand \firstChar:

\expandafter\ifx\expandafter M\firstChar

But this wouldn't work with that string extraction, because \firstChar expands to instruction for extracting the first character, not to the first character. A better (and simpler) test is

\makeatletter
\def\extract@first#1#2\@nil{#1}
\newcommand*{\mytest}[1]{%
  \expandafter\ifx\expandafter M\extract@first#1\@empty\@nil
    \textit{M has been recognized}%
  \else
    \textbf{M has NOT been recognized, firstChar=`\extract@first#1\@empty\@nil'}%
  \fi
}
\makeatother

This works also with an empty string. However, this will not work with multibyte UTF-8 characters, unless a UTF-8 savvy engine (XeLaTeX or LuaLaTeX) is used. Working with UTF-8 is much more difficult.

Complete example:

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}

\makeatletter
\def\extract@first#1#2\@nil{#1}
\newcommand*{\mytest}[1]{%
  \expandafter\ifx\expandafter M\extract@first#1\@empty\@nil
    \textit{M has been recognized}%
  \else
    \textbf{M has NOT been recognized, firstChar=`\extract@first#1\@empty\@nil'}%
  \fi
}
\makeatother

\begin{document}\thispagestyle{empty}
Test with Matching: \mytest{Matching}\par
Test with M:  \mytest{M}\par
Test with No: \mytest{No}\par
Test with N:  \mytest{N}\par
Test with empty: \mytest{}\par
\end{document}

enter image description here

3
  • Thanks egreg. Why does \@empty is required please?
    – lalebarde
    May 22, 2014 at 13:11
  • 1
    @lalebarde Just for the case the argument is empty. You can remove it if you're sure the argument is not empty.
    – egreg
    May 22, 2014 at 13:14
  • Thanks. I am trying to experiment what I have learnt here in Heiko Oberdiek first solution to get the \Lettrine{D'}Artagnan capability without the complexity of the second solution that provides it, but I fail. I will post there my failing solution.
    – lalebarde
    May 22, 2014 at 14:32
5

If you wish to stay with stringstrings, the \isnextbyte function is the one you seek. Because \theresult is an \edef'ed string, you don't want to use \ifx for the comparison, but rather \if.

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}

\usepackage{fp,stringstrings}

\newcommand*{\mytest}[1]{%
  \isnextbyte[q]{M}{#1}%
  \if T\theresult\textit{M has been recognized}\else%
    \textbf{M has NOT been recognized. firstChar=\substring{#1}{1}{1}}\fi
}

\begin{document}\thispagestyle{empty}
Test with Matching: \mytest{Matching}\par
Test with M:  \mytest{M}\par
Test with No: \mytest{No}\par
Test with N:  \mytest{N}\par

\isnextbyte{M}{Matching}
\isnextbyte{M}{M}
\isnextbyte{M}{No}
\isnextbyte{N}{M}
\end{document}

enter image description here


However, if you prefer your logic to extract the first character and do the comparison, as given in your MWE, this is how it would be done. Rather than trying to assign the substring to a \def, just run the \substring in quiet [q] mode. This will not print out the substring, which has nonetheless been placed via \edefinto \thestring. At that point, the proper test becomes \if M\thestring...\else...\fi.

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}

\usepackage{fp,stringstrings}

\newcommand*{\mytest}[1]{%
  \substring[q]{#1}{1}{1}%
  \if M\thestring\textit{M has been recognized}\else\textbf{M has NOT been recognized, firstChar='\thestring'}\fi
}

\begin{document}\thispagestyle{empty}
Test with Matching: \mytest{Matching}\par
Test with M:  \mytest{M}\par
Test with No: \mytest{No}\par
Test with N:  \mytest{N}\par
\end{document}

The OP properly points out in the comments that both of these tests can be fooled if the first character of the string is, for example, a \$. For that possibility, one could invoke some syntax from the ifthen package to take care of the test:

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}

\usepackage{fp,stringstrings,ifthen}

\newcommand*{\mytest}[1]{%
 \substring[q]{#1}{1}{1}%
 \ifthenelse{\equal{M}{\thestring}}%
 {\textit{M has been recognized}}%
 {\textbf{M has NOT been recognized, firstChar='\thestring'}}%
}

\begin{document}\thispagestyle{empty}
Test with Matching: \mytest{Matching}\par
Test with M:  \mytest{M}\par
Test with No: \mytest{No}\par
Test with N:  \mytest{N}\par
Test with \$N:  \mytest{\$N}\par
\end{document}

The idea of the package, in placing manipulated strings into \thestring via \edef is so that successive manipulations can be set up on a given text. In this MWE, for example, the phrase "Four score and seven years ago" has the following manipulations sequentially performed: 1) "seven" is changed to "7", 2) all letter-cases are reversed; 3) the first word is rotated to the end of the string; 4) the first character is gobbled.

\documentclass{article}
\usepackage{stringstrings}
\begin{document}
\edef\phrase{Four score and seven years ago}
\convertword[q]{\phrase}{seven}{7}
\changecase[q]{\thestring}
\rotateword[q]{\thestring\ }
\gobblechar[q]{\thestring}
\thestring
\end{document}

enter image description here

8
  • Thanks Steven. From the stringsstrings documentation: \isnextbyte is a byte test and not a character test. This means that, while \isnextbyte operates very eciently, it cannot be used to directly detect multi-byte characters like \$, \^, \{, \}, \_, \dag, \ddag, \AE, \ae, \OE, \oe, etc., so this function is not a good candidate. Besides, I am not stingsstrings dogmatic.
    – lalebarde
    May 22, 2014 at 11:44
  • @lalebarde Believe me, I am not a stringstrings dogmatic either, and I wrote it :^). It leaves much to be desired. May 22, 2014 at 11:57
  • @lalebarde The ifthen syntax would permit you to screen out things like \$ from a true test as follows: \newcommand*{\mytest}[1]{% \substring[q]{#1}{1}{1}% \ifthenelse{\equal{M}{\thestring}}% {\textit{M has been recognized}}% {\textbf{M has NOT been recognized, firstChar='\thestring'}}% } May 22, 2014 at 12:04
  • @lalebarde Likewise, a test for \$ could be accomplished with \newcommand*{\mytest}[1]{\substring[q]{#1}{1}{1}\ifthenelse{\equal{\char36\relax}{\thestring}}{\textit{\$ has been recognized}}{\textbf{\$ has NOT been recognized, firstChar='\thestring'}}} May 22, 2014 at 12:14
  • Great and thanks for the explanations. I prefer this solution, but here, I have unexpected newline between eg Test with Matching: and M has been recognized ?! Do you have the same behaviour?
    – lalebarde
    May 22, 2014 at 12:20

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .