8

Can anyone help me debug the code below? As written, it produces an Incomplete \iffalse error on the \xdef line on the final iteration. The problem is somehow related to the \phantom macro, as \def\c{0} works fine. I know there are a couple of conditional tests inside \phantom, but I'm not enough of a TeX wizard to figure out what is colliding with what.

\documentclass{article}
\usepackage{tikz,xstring}

\begin{document}

\def\result{}
\foreach \i in {1,...,6}{
\StrChar{12345}{\i}[\c]
\ifx\c\empty
\def\c{\phantom{0}}
\fi
\xdef\result{\result\c}}


\stop
3
8

\phantom is a fragile command and not safe in an \edef. One way to locally make it safe is:

\documentclass{article}
\usepackage{tikz,xstring}

\begin{document}

\def\result{}
\let\oldphantom\phantom
\let\phantom\relax
\foreach \i in {1,...,6}{
\StrChar{12345}{\i}[\c]
\ifx\c\empty
\def\c{\phantom{0}}
\fi
\xdef\result{\result\c}}
\let\phantom\oldphantom
\show\result

\stop
3
  • You can simply do \let\phantom\relax inside the cycle code, because they are performed in a group. – egreg Dec 14 '14 at 21:02
  • This solution works; thanks. I think I understand it, but I want to make sure. The \let\phantom\relax essentially makes \phantom an unexpandable noop inside the loop. But when \result is expanded at the end \phantom has its real definition back so it works as expected. Is that about right? – Jay Dec 14 '14 at 21:30
  • @Jay yes, exactly – David Carlisle Dec 14 '14 at 21:39
4

You can't have \phantom inside \xdef, because it performs assignments.

There are several strategies for avoiding the problem.

First strategy: use a \protected macro:

\documentclass{article}
\usepackage{xstring,pgffor}

\protected\def\Pzero{\phantom{0}}

\begin{document}

\def\result{}
\foreach \i in {1,...,6}{%
  \StrChar{12345}{\i}[\c]%
  \ifx\c\empty
    \def\c{\Pzero}%
  \fi
  \xdef\result{\result\c}%
}

X\result X

\end{document}

The loop can be, more simply,

\foreach \i in {1,...,6}{%
  \StrChar{12345}{\i}[\c]%
  \xdef\result{\result\ifx\c\empty\Pzero\else\c\fi}%
}

Second strategy: use token registers.

\documentclass{article}
\usepackage{xstring,pgffor}

\newtoks\mytoks

\begin{document}

\def\result{}
\mytoks={}
\foreach \i in {1,...,6}{%
  \StrChar{12345}{\i}[\c]%
  \ifx\c\empty
    \global\mytoks=\expandafter{\the\mytoks\phantom{0}}%
  \else
    \global\mytoks=\expandafter{\the\expandafter\mytoks\c}%
  \fi
}
\edef\result{\the\mytoks}

X\result X

\end{document}

Third strategy: forget xstring and pgffor and prefer expl3.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\padnumber}{mmo}
 {% #1 is the final number of digits
  % #2 the given number
  % #3 is an optional macro to store the result in
  \IfNoValueTF{#3}
   {
    \jay_padnumber:nnn { \tl_use:N \l_jay_partial_tl } { #1 } { #2 }
   }
   {
    \jay_padnumber:nnn { \tl_set_eq:NN #3 \l_jay_partial_tl } { #1 } { #2 }
   }
 }

\tl_new:N \l_jay_partial_tl

\cs_new_protected:Npn \jay_padnumber:nnn #1 #2 #3
 {
  % store the given number
  \tl_set:Nn \l_jay_partial_tl { #3 }
  \int_compare:nT { \tl_count:N \l_jay_partial_tl < #2 }
   {
    % add as many \phantom{0} as needed
    \tl_put_right:Nx \l_jay_partial_tl
     {
      \prg_replicate:nn { #2 - \tl_count:N \l_jay_partial_tl } { \exp_not:N \phantom { 0 } }
     }
   }
  % produce the result or store it
  #1
 }
\ExplSyntaxOff

\begin{document}

X1234567890 % test

X\padnumber{6}{12345}X

X\padnumber{7}{12345}X

X\padnumber{4}{12345}X

\padnumber{8}{12345}[\result]

\texttt{\meaning\result}

\end{document}

We count the items in the argument; if the number of items exceeds the given argument, the number is simply printed (or stored). Otherwise the right number of \phantom{0} is added in one step.

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.