3

I would like to match the pattern "start with >>" in a string and replace this with a command; and if multiple >> appear, it should match and replace multiple times.

For example, given the string aaa>>bbb>>ccc, I would like to convert it into aaa\somecommand{bbb}\somecommand{ccc}. My idea is to use \regex_replace_all:nnN, but I cannot seem to find the correct regex pattern: for example, >>(.*) would only match once for this string. How to properly achieve this?

Below is a MWE:

\documentclass{article}
\begin{document}

\NewDocumentCommand \somecommand { m }
  {
    \begin{center} #1 \end{center}
  }

\ExplSyntaxOn
\NewDocumentCommand \replace { m }
  {
    \tl_set:Nn \l_tmpa_tl { #1 }
    \regex_replace_all:nnN
      { >> ((?:.(?:?!>>))*.) } % needs to be changed
      { \c{somecommand}\{\1\} }
      \l_tmpa_tl
    \tl_use:N \l_tmpa_tl
  }
\ExplSyntaxOff

\replace{aaa>>bbb>>ccc}

% expected: aaa\somecommand{bbb}\somecommand{ccc}

\end{document}
1
  • The thing about \regex_replace_all:nnN is that you only need to match your replacement once, not multiple times. But you need to make sure that >> can't be part of your regular expression. If no other > are part of it you could simply use >>([^>]*).
    – Skillmon
    Commented Aug 28, 2023 at 14:28

2 Answers 2

2

No need to use regexes, unless you need to preserve spaces around >>.

\documentclass{article}
\begin{document}

\ExplSyntaxOn

\NewDocumentCommand \somecommand { m }
  {
    \jinwen_somecommand:n { #1 }
  }

\NewDocumentCommand \replace { m }
  {
    \jinwen_replace:n { #1 }
  }

\tl_new:N \l__jinwen_replace_head_tl
\seq_new:N \l__jinwen_replace_tail_seq

\cs_new:Nn \jinwen_somecommand:n { \,---\, #1 \,---\, }

\cs_new_protected:Nn \jinwen_replace:n
  {
    \seq_set_split:Nnn \l__jinwen_replace_tail_seq { >> } { #1 }
    \seq_pop_left:NN \l__jinwen_replace_tail_seq \l__jinwen_replace_head_tl
    \tl_use:N \l__jinwen_replace_head_tl
    \seq_map_function:NN \l__jinwen_replace_tail_seq \jinwen_somecommand:n
  }
\ExplSyntaxOff

X\replace{}X

\replace{aaa}

\replace{aaa>>bbb}

\replace{aaa>>bbb>>ccc}

\end{document}

enter image description here

2
  • Thank you for this! Please allow me to ask a follow-up question: is it possible to define the replace function as \regex_replace so as to read in a token list, process it, and then store the result into the same token list?
    – Jinwen
    Commented Aug 28, 2023 at 14:54
  • @Jinwen Your examples are often too reduced. Please, state the real problem.
    – egreg
    Commented Aug 28, 2023 at 15:02
1

Using just LaTeX2e, with the listofitems package. Note that the output of the command is stored in the token list \mytoks.

\documentclass{article}
\usepackage{listofitems}
\newtoks\mytoks
\newcommand\addtotoks[2]{\global#1\expandafter{\the#1#2}}
\newcommand\xaddtotoks[2]{\expandafter\addtotoks\expandafter
  #1\expandafter{#2}}
\NewDocumentCommand \somecommand { m }
  {
    \begin{center} #1 \end{center}
  }
\newcommand\replace[1]{%
  \setsepchar{>>}%
  \mytoks{}%
  \readlist\mylist{#1}%
  \foreachitem\z\in\mylist[]{%
    \ifnum\zcnt=1\xaddtotoks\mytoks{\z}\else
      \xaddtotoks\mytoks{\expandafter{\z}}\fi%
    \ifnum\zcnt<\mylistlen \addtotoks\mytoks{\somecommand}\fi%
  }%
  \the\mytoks
}
\begin{document}
\replace{Initial Text>>Next Text Group>>The Final Text Group}
\end{document}

enter image description here

2
  • \xaddtotoks is unnecessarily complicated, your \expandafter chain assumes #1 is just a single token, so why not define it \newcommand\xaddtotoks[2]{\expandafter\addtotoks\expandafter#1\expandafter{#2}} instead?
    – Skillmon
    Commented Aug 29, 2023 at 4:07
  • @Skillmon Yes, you are right. I will change it. Commented Aug 29, 2023 at 15:26

You must log in to answer this question.

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