如何用参数替换字符串中的占位符?

如何用参数替换字符串中的占位符?

\enzh我正在使用如下定义的宏用两种语言编写文档:

\documentclass{article}

\newif\ifen
\entrue % comment out to switch to Chinese
\newcommand{\enzh}[2]{\ifen#1\else#2\fi}

\begin{document}

\enzh{English text}{Chinese text}

\end{document}

目前显示“英文文本”,如果\entrue注释掉则显示“中文文本”。此部分工作正常。

但是,两种语言可能存在一些共同的元素。我不想为两种语言重复相同的代码,而是希望用这些共同的元素替换两种语言字符串中的占位符,使用类似于 Python 字符串的语法format。例如:

\documentclass{article}
\usepackage{hyperref}

\newif\ifen
\entrue % comment out to switch to Chinese
\newcommand{\enzh}[2]{\ifen#1\else#2\fi} % how should this command be implemented?

\begin{document}

\enzh{English #1 text #2}{Chinese #2 text #1}{\href{https://www.google.com}{Google}}{b}

\end{document}

应显示“英语谷歌文本b”和“中文b文本”谷歌“对于两种语言模式。理想情况下,可以进行任意多次替换,但就我的目的而言,3 次就足够了。

\enzh为了实现这一点,应该如何执行命令?

答案1

使用 expl3 你可以定义一个\MyReplace带有三个参数的命令,

  1. ⟨英文原文⟩
  2. ⟨中文文本⟩
  3. ⟨英文/中文文本中常用替换 {1}、{2}、{3}、… 的文本短语列表⟩

,它实际上包含括号嵌套的未分隔参数列表。

取决于迭代是否\englishtrue发生\englishfalse在⟨英文文本⟩或⟨中文文本⟩的未限定参数上,其中每个元素处理如下:

  • 如果它的第一个标记是,:则假定它表示一个非常见的文本短语,并且它会被:删除。
  • 如果它的第一个标记不是,:则假定它表示数字 k,并且如果该列表有第 k元素,则传递⟨常用文本短语列表⟩的第 k元素。

起初我考虑过反过来做,即:⟨number⟩用 而不是:⟨non-common text phrase⟩,但随后出现了一个问题,即如何处理非常见文本短语以 为前导的情况:,而不会错误地将其作为 :⟨number⟩ 传递给处理,这可能会导致低级 TeX 错误消息。

\documentclass{article}

\ExplSyntaxOn
\bool_new:N \l_english_bool
\cs_new_protected:Npn \englishtrue {\bool_set_true:N \l_english_bool}
\cs_new_protected:Npn \englishfalse {\bool_set_false:N \l_english_bool}
\cs_new_protected:Npn \enzh #1#2 {\bool_if:nTF \l_english_bool {#1}{#2}}
\cs_new_protected:Npn \MyReplace #1#2#3
  {
    \bool_if:nTF \l_english_bool
    {\tl_map_tokens:nn {#1} }
    {\tl_map_tokens:nn {#2} }
    {\mymodule_get_kth_arg:nn{#3}}
  }
\cs_new:Nx \mymodule_get_kth_arg:nn
  {
    \exp_not:N\tl_if_blank:nF {#2} {
      \exp_not:N\tl_if_head_eq_meaning:nNTF 
         {#2}\token_to_str:N :
         {\exp_not:N\tl_tail:n {#2}}
         {\exp_not:N\tl_item:nn{#1}{#2}}
    }
  }
\ExplSyntaxOff

\begin{document}

\englishtrue

\enzh{English text}{Chinese text}

\MyReplace{ {1}{: }{2}{: English text A }{2}{: English text B }{5}{: English text C }{4}{: English text D }{3} }%
          { {2}{: }{1}{: Chinese Text text A }{1}{: Chinese text B }{3}{: Chinese Text text C }{4}{: Chinese text D }{5} }%
          { 
             {Common Phrase 1} 
             {Common Phrase 2}
             {Common Phrase 3}
             {Common Phrase 4}
             {Common Phrase 5}
          }

\bigskip

\englishfalse

\enzh{English text}{Chinese text}

\MyReplace{ {1}{: }{2}{: English text A }{2}{: English text B }{5}{: English text C }{4}{: English text D }{3} }%
          { {2}{: }{1}{: Chinese Text text A }{1}{: Chinese text B }{3}{: Chinese Text text C }{4}{: Chinese text D }{5} }%
          { 
             {Common Phrase 1}
             {Common Phrase 2}
             {Common Phrase 3}
             {Common Phrase 4}
             {Common Phrase 5}
          }

\end{document}

在此处输入图片描述

在上述示例的 ⟨英文文本⟩ 和 ⟨中文文本⟩ 中,{: }空格都用作非常用文本短语。您也可以将空格用作另一个常用文本短语,然后执行

\MyReplace{ {1}{6}{2}{: English text A }{2}{: English text B }{5}{: English text C }{4}{: English text D }{3} }%
          { {2}{6}{1}{: Chinese Text text A }{1}{: Chinese text B }{3}{: Chinese Text text C }{4}{: Chinese text D }{5} }%
          { 
             {Common Phrase 1}
             {Common Phrase 2}
             {Common Phrase 3}
             {Common Phrase 4}
             {Common Phrase 5}
             { }
          }


#1如果您更喜欢通过, #2, ... , #11... 而不是{1}, {2}, ... , ...来表示常见的文本短语/替换,{11}并且如果可扩展性不是问题,那么您可以使用 l3regex 的正则表达式。

在以下示例中,正则表达式将搜索#以一系列数字0, 1, 2, 3, 4, 5, 6, 7, 8,9结尾,并以一个可选斜杠结尾,/这样您就可以将属于常用文本短语/替换列表元素编号的数字与属于要传递的文本的数字分开 - 因此,如果您希望在文本中紧跟在常用文本短语后面/紧跟在 表示的替换后面出现斜杠#⟨sequence of digits denoting the number of the common text phrase/replacement⟩,则需要输入两个斜杠:
#⟨sequence of digits denoting the number of the common text phrase/replacement⟩//The second one of these slashes goes into the text. The first one is removed as "digit"-separator":

\documentclass{article}

\ExplSyntaxOn
\bool_new:N \l_english_bool
\tl_new:N \l_mymodule_replacementlist_tl
\tl_new:N \l_mymodule_textphrase_tl
\cs_new_protected:Npn \englishtrue {\bool_set_true:N \l_english_bool}
\cs_new_protected:Npn \englishfalse {\bool_set_false:N \l_english_bool}
\cs_new_protected:Npn \enzh #1#2 {\bool_if:nTF \l_english_bool {#1}{#2}}
\cs_new_protected:Npn \MyReplace #1#2#3
  {
    \bool_if:nTF \l_english_bool
    {\tl_set:Nn\l_mymodule_textphrase_tl {#1} }
    {\tl_set:Nn\l_mymodule_textphrase_tl {#2} }
    \tl_set:Nn \l_mymodule_replacementlist_tl {#3}
    % \cA. denotes any active character; \cC. denotes any control sequence token, 
    % so the following prepends \noexpand to tokens that might be expandable:
    \regex_replace_all:nnN {(?:\cA.|\cC.)}{\c{exp\_not\:N}\0}\l_mymodule_textphrase_tl
%    \tl_show:N \l_mymodule_textphrase_tl
    % Replace any #<digit sequence><optional slash> of category 6 that is preceeded by an even amount of hashes 
    % by \tl_\item:Nn \l_mymodule_replacementlist_tl {<digit sequence>}:
    \regex_replace_all:nnN {  ((?:\G|[^\cP.])(?:[\cP.][\cP.])*)  [\cP.]   (\cO[\d]+)\cO\/?  }
                           {\1 \c{tl\_item\:Nn} \c{l\_mymodule\_replacementlist\_tl} \cB\{ \2 \cE\}}
                           \l_mymodule_textphrase_tl
%    \tl_show:N \l_mymodule_textphrase_tl
    % Expand the token list to get the token-list-items and halve doubled hashes:
    \exp_args:NNx \tl_set:Nn \l_mymodule_textphrase_tl {\tl_use:N \l_mymodule_textphrase_tl }
%    \tl_show:N \l_mymodule_textphrase_tl
    \tl_use:N \l_mymodule_textphrase_tl
  }
\ExplSyntaxOff

\begin{document}

\tableofcontents

\englishtrue

\MyReplace{\section{#1 English text #2}}
          {\section{#2 Chinese text #1}}
          {
             {Common 1} % <- this is #1
             {Common 2} % <- this is #2
          }

\enzh{English text}{Chinese text}

\bigskip

\MyReplace{#1 #2 English text A #2//English text B #5 English text C #4 English text D #3/7 is a nice digit.}%
          {#2 #1 Chinese Text text A #1//Chinese text B #3 Chinese Text text C #4 Chinese text D #5/7 is a nice digit.}%
          { 
             {Common Phrase 1} % <- this is #1
             {Common Phrase 2} % <- this is #2
             {Common Phrase 3} % <- this is #3
             {Common Phrase 4} % <- this is #4
             {Common Phrase 5} % <- this is #5
          }

\bigskip

\MyReplace{\def\MACRO##1##2{#1 English text #2}}%
          {\def\MACRO##1##2{#2 Chinese text #1}}%
          { 
             {Common Phrase 1, Argument 1: #1, Argument 2: #2} % <- in the two arguments above this is #1
             {Common Phrase 2, Argument 1: #1, Argument 2: #2} % <- in the two arguments above this is #2
          }%
{\tt\frenchspacing\string\MACRO: \meaning\MACRO}

\englishfalse

\MyReplace{\section{#1 English text #2}}
          {\section{#2 Chinese text #1}}
          {
             {Common 1} % <- this is #1
             {Common 2} % <- this is #2
          }

\enzh{English text}{Chinese text}

\bigskip

\MyReplace{#1 #2 English text A #2//English text B #5 English text C #4 English text D #3/7 is a nice digit.}%
          {#2 #1 Chinese Text text A #1//Chinese text B #3 Chinese Text text C #4 Chinese text D #5/7 is a nice digit.}%
          { 
             {Common Phrase 1} % <- this is #1
             {Common Phrase 2} % <- this is #2
             {Common Phrase 3} % <- this is #3
             {Common Phrase 4} % <- this is #4
             {Common Phrase 5} % <- this is #5
          }

\bigskip

\MyReplace{\def\MACRO##1##2{#1 English text #2}}%
          {\def\MACRO##1##2{#2 Chinese text #1}}%
          { 
             {Common Phrase 1, Argument 1: #1, Argument 2: #2} % <- in the two arguments above this is #1
             {Common Phrase 2, Argument 1: #1, Argument 2: #2} % <- in the two arguments above this is #2
          }%
{\tt\frenchspacing\string\MACRO: \meaning\MACRO}

\end{document}

在此处输入图片描述

如果您按照这种方式执行操作并将其用作\MyReplace宏的 ⟨定义⟩ 的替换文本/⟨平衡文本⟩的组成部分,则#表示常用文本短语/替换的数量的哈希值需要加倍,就像在 ⟨定义⟩ 内嵌套 ⟨定义⟩ 时表示参数的哈希值需要加倍一样。

答案2

您可以简单地定义\enzhi单个参数、\enzhii两个参数等等。

\newif\ifen
\entrue

\def\enzh#1#2{\ifen#1\else#2\fi}
\def\enzhi#1#2{\def\tmp##1{\ifen#1\else#2\fi}\tmp}
\def\enzhii#1#2{\def\tmp##1##2{\ifen#1\else#2\fi}\tmp}

\enzhii{English #1 text #2}{Chinese #2 text #1}{\href{https://www.google.com}{Google}}{b}

答案3

主要问题是要有一致的语法,所以我建议\enzh有两个尾随可选参数。如果存在,则两个强制参数将被解释为模板(您可以在其中使用#1#2)。

\documentclass{article}
\usepackage{hyperref}

\ExplSyntaxOn

\NewDocumentCommand{\enzh}{mmoO{#3}}
 {
  \IfNoValueTF{#3}
   {% no optional arguments
    \legacy_if:nTF { en } { #1 } { #2 }
   }
   {% with optional arguments
    \tian_enzh:nnnn { #1 } { #2 } { #3 } { #4 }
   }
 }

\cs_new_protected:Nn \tian_enzh:nnnn
 {
  \group_begin:
  \legacy_if:nTF { en }
   {% en is true, use the first argument as template
    \cs_set:Nn \__tian_enzh_do:nn { #1 }
   }
   {% en is false, use the second argument as template
    \cs_set:Nn \__tian_enzh_do:nn { #2 }
   }
  % do the job
  \__tian_enzh_do:nn { #3 } { #4 }
  \group_end:
 }

\cs_new:Nn \__tian_enzh_do:nn {} % initialize the auxiliary function

\ExplSyntaxOff

\newif\ifen

\begin{document}

\section{Chinese}

\enzh{English text}{Chinese text}

\enzh{English #1 text #2}{Chinese #2 text #1}[\href{https://www.google.com}{Google}][b]

\section{English}
\entrue

\enzh{English text}{Chinese text}

\enzh{English #1 text #2}{Chinese #2 text #1}[\href{https://www.google.com}{Google}][b]

\end{document}

在此处输入图片描述

如果只有一个可选参数,则第二个参数将被视为与出现的参数相同。

相关内容