多阶段正则表达式替换

多阶段正则表达式替换

我正在尝试编写一个宏,它可以识别字符串中的某些字符序列,然后将它们映射到不同的集合上。在我考虑的特定情况下,由于有多种表示相同标记的方式,我希望规范化识别的序列。为了简化问题,以下是我能想到的演示问题的最小代码

\documentclass{article}

\usepackage{xstring}
\usepackage{xparse}
\usepackage{expl3}
\usepackage{l3regex}

\makeatletter
\ExplSyntaxOn

\DeclareDocumentCommand\@Normalize { m } {
  \tl_clear_new:N \tl_@str
  \tl_set:Nx \tl_@str {#1}
  \tl_use:N \tl_upper_case:n \tl_@str
}

\tl_const:Nn \tl_Map@A {1}
\tl_const:Nn \tl_Map@B {2}
\tl_const:Nn \tl_Map@C {3}

\NewDocumentCommand\nstring { m } {
  \tl_clear_new:N \tl_@norm 
  \tl_set:Nn \tl_@norm {#1}
  \regex_replace_all:nnN {[A-Ca-c]} {\c{@Normalize}\cB\{\0\cE\}} \tl_@norm
  \regex_replace_all:nnN {[A-Ca-c]} {\u{tl_Map@\0}} \tl_@norm
  \tl_use:N \tl_@norm
}

\ExplSyntaxOff
\makeatother

\begin{document}
\nstring{ABC abc XYZ}
\end{document}

在这个特定实例中,“规范化”是微不足道的——只是大写而已——但它说明了普遍的问题。本质上,它\regex_replace_all并不像我预期的那样工作,也就是说我认为第二次调用会看到第一次调用的结果。然而,事实并非如此!有没有什么方法可以按顺序\regex_replace_all实现所需的结果?

我在尝试编译该文档时遇到的错误如下:

! Undefined control sequence.
<argument> \LaTeX3 error: 
                Erroneous variable \tl_Map@a used!
l.33 \nstring{ABC abc XYZ}

答案1

该代码将适用于您,因为:

  • 它使用 l3 语法(未混合)
  • 在应用一些奇怪的东西之前,它会进行适当的扩展
  • 它定义了一个可扩展的辅助命令

注意:如果设置(覆盖)变量,则无需清除。除此之外,您可能还需要考虑使用与这些常量值不同的类型来存储地图(例如属性列表)。

代码:

\documentclass{article}

\usepackage{xstring}
\usepackage{xparse}
\usepackage{expl3}

\ExplSyntaxOn

\cs_set:Npn \kevin_normalize:n #1
    {
        \text_uppercase:n { #1 }
    }

\tl_const:Nn \c_kevin_mapA_tl { 1 }
\tl_const:Nn \c_kevin_mapB_tl { 2 }
\tl_const:Nn \c_kevin_mapC_tl { 3 }

\NewDocumentCommand\nstring { m } {
%  \tl_clear:N \l_tmpa_tl
    \tl_set:Nn \l_tmpa_tl { #1 }
  \regex_replace_all:nnN {[A-Ca-c]} { \c{kevin_normalize:n}\cB\{\0\cE\}} \l_tmpa_tl
  \tl_set:Nx \l_tmpa_tl { \tl_use:N \l_tmpa_tl }
  \regex_replace_all:nnN {[A-Ca-c]} { \u{c_kevin_map\0_tl} } \l_tmpa_tl
  \tl_use:N \l_tmpa_tl
}

\ExplSyntaxOff

\begin{document}
\nstring{ABC abc XYZ}
\end{document}

替代方案(保存正则表达式,感谢 egreg):

\documentclass{article}

\usepackage{xstring}
\usepackage{xparse}
\usepackage{expl3}

\ExplSyntaxOn

\cs_set:Npn \kevin_normalize:n #1
    {
        \tl_if_exist:cTF {c_kevin_map\str_uppercase:n{#1}_tl}
            { \tl_use:c {c_kevin_map\str_uppercase:n{#1}_tl }}
            { #1 }
    }

\tl_const:Nn \c_kevin_mapA_tl { 1 }
\tl_const:Nn \c_kevin_mapB_tl { 2 }
\tl_const:Nn \c_kevin_mapC_tl { 3 }

\NewDocumentCommand\nstring { m } {
  \tl_set:Nn \l_tmpa_tl { #1 }
  \regex_replace_all:nnN {[A-Ca-c]} { \c{kevin_normalize:n}\cB\{\0\cE\} } \l_tmpa_tl
  \tl_set:Nx \l_tmpa_tl { \tl_use:N \l_tmpa_tl }
  \tl_use:N \l_tmpa_tl
}

\ExplSyntaxOff

\begin{document}
\nstring{ABC abc XYZ xyz}
\end{document}

答案2

没有正则表达式。您可以映射字符串并使用\str_case:nnF(使用执行 f 扩展的变体)进行替换。唯一的问题是处理映射标记列表时忽略的空格。

\documentclass{article}

\usepackage{xparse}
\usepackage{expl3}

\ExplSyntaxOn

\NewDocumentCommand{\nstring}{m}
 {
  \tl_set:Nn \l_tmpa_tl { #1 }
  \tl_replace_all:Nnn \l_tmpa_tl { ~ } { \c_space_tl }
  \tl_map_inline:Nn \l_tmpa_tl
   {
    \str_case:fnF { \str_uppercase:n { ##1 } }
     {
      {A}{1}
      {B}{2}
      {C}{3}
     }
     {##1}
   }
 }
\prg_generate_conditional_variant:Nnn \str_case:nn { f } { T, F, TF }
\ExplSyntaxOff

\begin{document}
\nstring{ABC abc XYZ}
\end{document}

在此处输入图片描述

相关内容