如何吞噬一个角色(如果存在)

如何吞噬一个角色(如果存在)

我想做以下事情:

给定一个(复杂的)字符串/宏\String,如果它以 * 开头,我就将其删除,否则我什么也不做。

到目前为止我尝试过

\def\gobbleast *{}%

如果以 开头,则\gobbleast \String工作正常,否则会出现错误。\String*

答案1

如果还需要涵盖边缘情况,例如需要考虑花括号,或者字符串本身可能包含不平衡的\if.../ \or/ \else/\fi我有一个令人恐惧的答案,其中\if..- \or- \else-\fi构造不会出现在任何地方,但使用分隔参数和参数空性测试来实现分支。;->

由于整个过程不是很快,因此它更像是一种教学手段,用来了解 TeX 如何收集属于宏参数的标记,以及如何通过扩展等方式操纵标记扩展的时间\expandafter顺序\romannumeral

\documentclass[a4paper, landscape]{article}

% ---- Layout ----------------------------------------------------------------------
%      The code for changing the layout is not of importance to you.
%      It is messy and only suits the need of having the result of compiling this
%      example fit on one page
\pagestyle{plain}
\csname @ifundefined\endcsname{pagewidth}{}{\pagewidth=\paperwidth}%
\csname @ifundefined\endcsname{pdfpagewidth}{}{\pdfpagewidth=\paperwidth}%
\csname @ifundefined\endcsname{pageheight}{}{\pageheight=\paperheight}%
\csname @ifundefined\endcsname{pdfpageheight}{}{\pdfpageheight=\paperheight}%
\textwidth=\paperwidth
\advance\textwidth-3cm
\evensidemargin=\dimexpr-1in+1.5cm\relax
\oddsidemargin=\dimexpr-1in+1.5cm\relax
\marginparsep=2mm
\marginparwidth=\dimexpr1.5cm-2\marginparsep\relax
\textheight=\paperheight
\advance\textheight-3cm
\topmargin=\dimexpr-1in+1.5cm\relax
\headheight=0pt
\headsep=0pt
{\normalfont
 \setbox\csname @tempboxa\endcsname\hbox{0123456789}%
 \global\footskip=\dimexpr .75cm -.5\dp\csname @tempboxa\endcsname
                               +.5\ht\csname @tempboxa\endcsname\relax
}%
\parindent=0pt
\flushbottom
% ---- END OF LAYOUT ---------------------------------------------------------------


\makeatletter    
%%=============================================================================
%% Paraphernalia:
%%    \UD@firstoftwo, \UD@secondoftwo, \UD@Exchange, \UD@stopromannumeral,
%%    \UD@CheckWhetherNull,  \UD@CheckWhetherLeadingTokens
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@Exchange[2]{#2#1}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
  \expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%=============================================================================
%% Check whether argument's leading tokens form a specific 
%% token-sequence that does not contain explicit character tokens of 
%% category 1 or 2 or 6:
%%=============================================================================
%% \UD@CheckWhetherLeadingTokens{<argument which is to be checked>}%
%%                              {<a <token sequence> without explicit 
%%                                character tokens of category 1 or 2
%%                                or 6>}%
%%                              {<internal token-check-macro>}%
%%                              {<tokens to be delivered in case
%%                                <argument which is to be checked> has
%%                                <token sequence> as leading tokens>}%
%%                              {<tokens to be delivered in case 
%%                                <argument which is to be checked>
%%                                does not have <token sequence> as
%%                                leading tokens>}%
\newcommand\UD@CheckWhetherLeadingTokens[3]{%
  \romannumeral\UD@CheckWhetherNull{#1}%
  {\expandafter\UD@stopromannumeral\UD@secondoftwo}%
  {%
    % Let's nest things into \UD@firstoftwo{...}{} to make sure they are nested in braces
    % and thus do not disturb when the test is carried out within \halign/\valign:
    \expandafter\UD@firstoftwo\expandafter{%
      \expandafter\expandafter\expandafter\UD@stopromannumeral
      \romannumeral
      \expandafter\UD@secondoftwo\string{\expandafter\UD@@CheckWhetherLeadingTokens#3{\relax}#1#2}{}}{}%
  }%
}%
\newcommand\UD@@CheckWhetherLeadingTokens[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  {\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
  {\expandafter\expandafter\expandafter\UD@stopromannumeral
   \expandafter\expandafter\expandafter}%
  \expandafter\UD@secondoftwo\expandafter{\string}%
}%
%%-----------------------------------------------------------------------------
%% \UD@internaltokencheckdefiner{<internal token-check-macro>}%
%%                              {<token-sequence-gobble-macro>}%
%%                              {<token sequence>}%
%% Defines <internal token-check-macro> to snap everything 
%% until reaching <token sequence>-sequence and spit that out
%% nested in braces.
%%
%% Defines <token-sequence-gobble-macro> to remove <token sequence> which in the
%% token-stream must definitely follow the token <token-sequence-gobble-macro>.
%%-----------------------------------------------------------------------------
\newcommand\UD@internaltokencheckdefiner[3]{%
  \@ifdefinable#1{\long\def#1##1#3{{##1}}}%
  \@ifdefinable#2{\def#2#3{}}%
}%
%%=============================================================================
%% Checking for a leading *:
%%=============================================================================
\UD@internaltokencheckdefiner{\UD@SnapToStar}{\UD@GobbleStar}{*}%
%
% Now you can check for a leading * via
%
% \UD@CheckWhetherLeadingTokens{<argument to check>}{*}{\UD@SnapToStar}%
%                              {<tokens in case <argument to check> has a leading *>}%
%                              {<tokens in case <argument to check> does not have a leading *>}%
%
%%=============================================================================
%% Remove a leading * if present - due to \romannumeral-expansion the
%% result is delivered by triggering two expansion-steps:
%%=============================================================================
\newcommand\RemoveLeadingStar[1]{%
  \romannumeral
  \UD@CheckWhetherLeadingTokens{#1}{*}{\UD@SnapToStar}%
  {\expandafter\UD@stopromannumeral\UD@GobbleStar}{\UD@stopromannumeral}#1%
}%
%%=============================================================================
%% Remove a leading * from the result of "hitting" the 1st token of the
%% argument with \expandafter once - due to \romannumeral-expansion the
%% result is delivered by triggering two expansion-steps::
%%=============================================================================
\newcommand\ExpandAndRemoveLeadingStar[1]{%
  \romannumeral\expandafter\expandafter\expandafter\UD@secondoftwo
  \expandafter\RemoveLeadingStar\expandafter{#1}%
}%
%------------------------------------------------------------------------------

\makeatother

\begin{document}

% Testsuite:

\noindent Defining \verb|\test| from the result of \verb|\RemoveLeadingStar{No leading star}|:
\medskip

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \RemoveLeadingStar{No leading star}%
}%

\noindent
\texttt{\string\test=\meaning\test}

\medskip\hrule\medskip

\noindent Defining \verb|\test| from the result of \verb|\RemoveLeadingStar{*Some leading star}|:
\medskip

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \RemoveLeadingStar{*Some leading star}%
}%

\noindent
\texttt{\string\test=\meaning\test}

\medskip\hrule\medskip

\noindent Defining \verb|\test| from the result of \verb|\RemoveLeadingStar{{*}Some leading star in braces}|:
\medskip

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \RemoveLeadingStar{{*}Some leading star in braces}%
}%

\noindent
\texttt{\string\test=\meaning\test}

\medskip\hrule\medskip

\noindent Defining \verb|\test| from the result of \verb|\RemoveLeadingStar{{*Some} leading star in braces}|:
\medskip

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \RemoveLeadingStar{{*Some} leading star in braces}%
}%

\noindent
\texttt{\string\test=\meaning\test}

\medskip\hrule\medskip

\noindent Defining \verb|\test| from the result of \verb|\RemoveLeadingStar{*Some leading star and imbalanced \fi}|:
\medskip

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \RemoveLeadingStar{*Some leading star and imbalanced \fi}%
}%

\noindent
\texttt{\string\test=\meaning\test}

\medskip\hrule\medskip

\noindent Defining \verb|\test| from the result of \verb|\RemoveLeadingStar{No leading star and imbalanced \fi}|:
\medskip

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \RemoveLeadingStar{No leading star and imbalanced \fi}%
}%

\noindent
\texttt{\string\test=\meaning\test}

\medskip\hrule\medskip

\medskip\hrule\medskip

\noindent {\bfseries\selectfont Defining \verb|\test| from the result of \verb|\ExpandAndRemoveLeadingStar\String|}
\medskip

\noindent... while \verb|\String| is defined to expand to \verb|No leading star|:%
\def\String{No leading star}
\medskip

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \ExpandAndRemoveLeadingStar\String
}%

\noindent
\texttt{\string\test=\meaning\test}

\medskip\hrule\medskip

\noindent... while \verb|\String| is defined to expand to \verb|*Some leading star|:%
\def\String{*Some leading star}
\medskip

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \ExpandAndRemoveLeadingStar\String
}%

\noindent
\texttt{\string\test=\meaning\test}

\medskip\hrule\medskip

\noindent... while \verb|\String| is defined to expand to \verb|{*}Some leading star in braces|:%
\def\String{{*}Some leading star in braces}
\medskip

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \ExpandAndRemoveLeadingStar\String
}%

\noindent
\texttt{\string\test=\meaning\test}

\medskip\hrule\medskip

\noindent... while \verb|\String| is defined to expand to \verb|{*Some} leading star in braces|:%
\def\String{{*Some} leading star in braces}
\medskip

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \ExpandAndRemoveLeadingStar\String
}%

\noindent
\texttt{\string\test=\meaning\test}

\medskip\hrule\medskip

\noindent... while \verb|\String| is defined to expand to \verb|*Some leading star and imbalanced \fi|:%
\def\String{*Some leading star and imbalanced \fi}
\medskip

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \ExpandAndRemoveLeadingStar\String
}%

\noindent
\texttt{\string\test=\meaning\test}

\medskip\hrule\medskip

\noindent... while \verb|\String| is defined to expand to \verb|No leading star and imbalanced \fi|:%
\def\String{No leading star and imbalanced \fi}
\medskip

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \ExpandAndRemoveLeadingStar\String
}%

\noindent
\texttt{\string\test=\meaning\test}

\end{document}

在此处输入图片描述

答案2

\documentclass{article}
\newcommand\gobbleast[1]{\expandafter\gobblehelp#1\relax}
\def\gobblehelp#1#2\relax{\ifx*#1\else#1\fi#2}
\begin{document}
\def\String{abc}
\gobbleast\String

\def\String{*ABC}
\gobbleast\String
\end{document}

在此处输入图片描述

相关内容