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 ?

lalebarde
  • 769

2 Answers2

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

egreg
  • 1,121,712
  • Thanks egreg. Why does \@empty is required please? – lalebarde May 22 '14 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 '14 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 '14 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

  • 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 '14 at 11:44
  • @lalebarde Believe me, I am not a stringstrings dogmatic either, and I wrote it :^). It leaves much to be desired. – Steven B. Segletes May 22 '14 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'}}% } – Steven B. Segletes May 22 '14 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'}}} – Steven B. Segletes May 22 '14 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 '14 at 12:20
  • @lalebarde I do not have the extra newline. I'll edit my answer to post the full MWE. – Steven B. Segletes May 22 '14 at 12:26
  • I mean the solution with \substring[q]{#1}{1}{1}% \if M\thestring\textit{M has been recognized}\else\textbf{M has NOT been recognized, firstChar='\thestring'}\fi – lalebarde May 22 '14 at 12:31
  • @lalebarde I get no stray newlines with my MWE. My version is 2013/04/24 v1.22. – Steven B. Segletes May 22 '14 at 12:35