4

In a custom class I defined a custom command to typeset a telephone number,

\newcommand{\phone}[1]{\def\@phone{#1}}

I use this \@phone macro inside my class when I want to replace with the value from the "variable". But there are some cases that I need to use a cleaned up version of the phone string. Normally \phone contains something like (623) 21-23-12-31; however, I want to create a \phoneclean which outputs just the numbers as 62321231231. How to accomplish that?

5 Answers 5

5

enter image description here

You can loop through dropping droppable characters (spaces are lost automatically here)

\documentclass{article}

\def\foo#1{%
\ifx\relax#1\else
\ifx(#1\else\ifx)#1\else\ifx-#1\else#1\fi\fi\fi
\expandafter\foo
\fi}

\begin{document}

\foo(623) 21-23-12-31\relax

\end{document}
7
  • @Lin \ifx(#1\else reads "if the next character is ( do nothing else .... Nov 29, 2023 at 19:23
  • Yes. I noticed later, hence why I have removed the question. But I think is good to keep your answer anyway. Thanks.
    – Lin
    Nov 29, 2023 at 19:23
  • Could \foo be made fully exandable?
    – Mico
    Nov 29, 2023 at 20:06
  • 1
    @Lin \expandafter\foo\@phone\relax Nov 29, 2023 at 20:40
  • 1
    @Mico \foo is expandable, but you just need to expand its argument first see the comment above Nov 29, 2023 at 20:54
7

Here's a LuaLaTeX-based solution, which makes use of Lua's powerful string.gsub function to purge all non-numeric characters from the macro's argument.

enter image description here

% !TEX TS-program = lualatex
\documentclass{article}

\usepackage{luacode} % for '\luaexec' macro
\newcommand\phoneclean[1]{\luaexec{% "%D" matches "any non-numeric character"
   tex.sprint (( string.gsub ( "#1" , "\%D" , "" ) )) }}

\begin{document}
\phoneclean{(623) 21-23-12-31}
\end{document}

Addendum: Because \luaexec is fully expandable, so is \phoneclean. Hence, instead of executing \phoneclean{(623) 21-23-12-31} directly, one could also first define \zzz via \newcommand{\zzz}{(623) 21-23-12-31} and then execute \phoneclean{\zzz}.

2
  • 1
    Another power tool in the lualatex arsenal.
    – rallg
    Nov 30, 2023 at 0:36
  • Remark: To handle a use case such as \def\zzz{\small\{623\} 21-23-12-31} \phoneclean{\zzz}, just change "#1" to \luastringO{#1} inside the \phoneclean macro. (\luastringO is a command provided by the luacode package; it expands its argument once.)
    – Mico
    Nov 30, 2023 at 6:52
4

You can map the input and keep the digits.

\documentclass{article}

\ExplSyntaxOn

\NewExpandableDocumentCommand{\getphonenumber}{m}
 {
  \lin_getphoneno:e { #1 }
 }

\cs_new:Nn \lin_getphoneno:n
 {
  \tl_map_function:nN { #1 } \__lin_getphoneno_check:n
 }
\cs_generate_variant:Nn \lin_getphoneno:n { e }

\cs_new:Nn \__lin_getphoneno_check:n
 {
  \str_case:nn { #1 }
   {
    {0}{0} {1}{1} {2}{2} {3}{3} {4}{4} {5}{5} {6}{6} {7}{7} {8}{8} {9}{9}
   }
 }

\ExplSyntaxOff

\begin{document}

\getphonenumber{(623) 21-23-12-31}

\edef\test{\getphonenumber{(623) 21-23-12-31}}
\texttt{\meaning\test}

\def\phoneno{(623) 21-23-12-31}

\getphonenumber{\phoneno}

\edef\test{\getphonenumber{\phoneno}}
\texttt{\meaning\test}

\end{document}

You see that \getphonenumber also works when the number is stored in a macro.

enter image description here

4

With this token cycle, alphabetical characters, TeX grouping, symbols, macros, and spaces are all screened out.

\documentclass{article}
\usepackage{tokcycle}
\newcommand\phoneclean[1]{%
  \tokencycle
  {\ifnum`##1>`\/ \ifnum`##1<`\:##1\fi\fi}
  {\processtoks{##1}}
  {}
  {}%
  #1\endtokencycle
}
\begin{document}
\phoneclean{(623) 21-23-12-31}

\phoneclean{(623) 21-23-12-31(c)}

\phoneclean{{623} 21-23-12-31}

\phoneclean{\small\{623\} 21-23-12-31}
\end{document}

enter image description here

1
  • 1
    +1 for coming up with \small\{623\} 21-23-12-31 as a use case. This use case prompted me to post a follow-up comment below my own answer.
    – Mico
    Nov 30, 2023 at 6:55
3

The following uses a low level token-loop and check for a digit (only category 12 digits should pass this filter and remain in the output, in particular macros that expand to a valid number are filtered as well). It is fully expandable, expands exactly in two steps and the result doesn't expand any further inside of an \edef. It assumes that \stop is never part of the argument, and that the argument doesn't contain braced groups.

If you give an optional * after \sanitisephone the first token in the input is expanded once (allowing a single macro as input).

There is a bit of trickery involved to get the *-variant using \NewExpandableDocument and still being expandable in exactly two steps (note that this trickery can be considered breaking coding styleguides of the kernel team).

\documentclass{article}

\makeatletter
\providecommand\@secondofthree[3]{#2}
\providecommand\@gobblefour[4]{}

\newcommand\phonenumber{}
\newcommand\phone[1]{\edef\phonenumber{\unexpanded{#1}}}

\newcommand\filternondigit[1]
  {%
    \ifnum\@ne<1\noexpand#1
      \@firstoftwo
    \fi
    \@gobbletwo\unexpanded{#1}%
  }

\newcommand\sanitisephone
  {\unexpanded\expanded\sanitisephone@args}
\NewExpandableDocumentCommand\sanitisephone@args{s +m}
  {{{\IfBooleanT{#1}\expandafter\sanitisephone@#2\stop}}}
\newcommand\sanitisephone@[1]
  {\sanitisephone@ifstop#1\@gobblefour\stop\filternondigit{#1}\sanitisephone@}
\long\def\sanitisephone@ifstop#1\stop{}
\makeatother

\begin{document}
\phone{(623) 21-23-12-31}
\edef\mytmp{\sanitisephone*\phonenumber}\texttt{\meaning\mytmp}

\edef\mytmp{\sanitisephone{(623) 21-23-12-31}}\texttt{\meaning\mytmp}
\end{document}

You must log in to answer this question.

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