从字符序列中删除反斜杠

从字符序列中删除反斜杠

对于索引,我想编写一个宏\macroname,从宏名称中删除前导反斜杠,但保留环境名称不变。即

\macroname{\relax}   -->  relax
\macroname{itemize}  -->  itemize

这里的-->应该读作“扩展为”。

据我了解,以下方法应该可行:

\newcommand\removebs[1]{\if#1\char92\else#1\fi}
\newcommand\macroname[1]{%
  \expandafter\removebs\detokenize{#1}}

然而其结果是

\macroname{\relax}   -->  \relax   % literally
\macroname{itemize}  -->  itemize

实际上,我偶然发现,如果我在宏定义期间将反斜杠的 catcode 更改为“other” \removebs,它就会起作用:

{
  \catcode`\|=0
  |catcode`|\=12
  |global|def|removebs#1{|if#1\|else#1|fi}
}

\newcommand\macroname[1]{%
  \expandafter\removebs\detokenize{#1}}

这是为什么呢?TeX 按主题分类我希望\ifPhilipp Lehman 所做的比较就是所谓的“类别代码不可知论”。

感谢您的回答!

答案1

Joseph 给出了一个可行的解决方案。我想解释一下你的代码出了什么问题。

第一次尝试

\newcommand\removebs[1]{\if#1\char92\else#1\fi}
\newcommand\macroname[1]{%
  \expandafter\removebs\detokenize{#1}}

\macroname{\relax}你一起得到

\expandafter\removebs\detokenize{\relax}

然后(使用来分隔标记,并<space>使用 do 来表示空格标记)

\removebs • \ • r • e • l • a • x • <space>

变成

\if • \ • \char • 9 • 2 • \else • \ • \fi • r • e • l • a • x • <space>

并且比较的是类别代码 12 反斜杠和标记\char,这当然会导致错误。

代码\char92是指令打印当前字体中的第 92 个字符。

可以通过检查真实类别代码 12 反斜杠来更正代码:

\makeatletter
\newcommand{\removebs}[1]{\if#1\@backslashchar\else#1\fi}
\makeatother

\detokenize但after产生的空间\relax将会保留。

第二次尝试

{
  \catcode`\|=0
  |catcode`|\=12
  |global|def|removebs#1{|if#1\|else#1|fi}
}

这是有效的,因为它仅实现了针对类别代码 12 反斜杠的检查,但这不是必需的,因为该标记已\@backslashchar在 LaTeX 内核中可用。

另一种方法,无需全局定义和类别更改,可以是

\begingroup\lccode`\|=`\\
\lowercase{\endgroup\def\removebs#1{\if#1|\else#1\fi}}

其中唯一转换为其小写等效项的标记是|(反斜杠)。

建议代码

% \makeatletter
% \newcommand{\removebs}[1]{\if#1\@backslashchar\else#1\fi}
% \makeatother
\begingroup\lccode`\|=`\\
\lowercase{\endgroup\def\removebs#1{\if#1|\else#1\fi}}
\newcommand{\macroname}[1]{\expandafter\removebs\string#1}

\macroname{itemize}因为您想要的只是获取参数字母,所以第一个字母是否具有类别代码 12并不重要。

答案2

TeX 使用 token,您需要知道的是,诸如 这样的控制序列\foo不是\if作为一系列字符进行测试,而是作为单个“单元”进行测试。有一些原语可以将控制序列重新转换为单个 token:\detokenize对一组 token 执行此操作,而\string对单个 token 执行此操作。后者在所有 TeX 版本中也可用(\detokenize需要 e-TeX)。

如您所见,对输入进行去标记化允许您与类别代码 'other' 进行比较\。另一种方法是测试是否是控制序列,使用将所有(未扩展的)控制序列视为相同的#1事实。然后,您可以使用转换为多个标记,并删除第一个字符:\ifcat\string\@gobble

\documentclass{article}
\makeatletter
\newcommand{\removeabs}[1]{%
  \ifcat\relax\noexpand#1%
    \expandafter\expandafter\expandafter\@gobble\expandafter\string
  \fi
  #1%
}
\makeatother
\begin{document}
\removeabs{foo}
\removeabs{\bar}
\end{document}

只要\escapechar是可打印的并且不是空格,以上内容都可以。使用 LaTeX3 中的一些代码“翻译”回原语,您可以设置所有转义字符的健壮性:

\documentclass{article}
\makeatletter
\newcommand{\removeabs}[1]{%
  \ifcat\relax\noexpand#1%
    \expandafter\removeabs@aux@i
  \fi
  #1%
}
\newcommand*{\removeabs@aux@i}{%
  \romannumeral
    \if\string\ \removeabs@aux@ii\fi
    \expandafter\removeabs@aux@iii\string
}
\newcommand{\removeabs@aux@ii}{}
\long\def\removeabs@aux@ii#1\removeabs@aux@iii{%
  -\number\fi\expandafter\z@
}
\newcommand{\removeabs@aux@iii}[1]{\z@}
\makeatother
\begin{document}
\removeabs{foo}
\removeabs{\bar}
\end{document}

(请参阅 LaTeX3 文档中的实现,\cs_to_str:N了解这里发生的情况!)

答案3

我的看法:

\def\cwofcs#1#2 \endcs{\ifnum\the\catcode`#1=0#2\else#1#2\fi}
\def\nameof#1{\expandafter\cwofcs\detokenize{#1}\endcs}

测试:

\nameof\relax       % -->  relax
\nameof\itemize     % -->  itemize

相关内容