10

How can I create a macro

\split{ABC}

that outputs

enter image description here

where the formatting of the characters are made with another macro, i.e.

\formatchar{A}

where

\def\formatchar#1{{\color{red}#1}}

for any string length?

I tried to modify the code in Split a character string n by n

\documentclass{article}
\usepackage{xstring}

\def\split#1#2{%
    \StrSplit{#2}{1}\tempa\tempb
    x%
    \tempa\let\tempa\empty
    \unless\ifx\tempb\empty\def\tempa{|\split{1}\tempb}\fi
    y%
    \tempa
}

\begin{document}
\split{1}{ABCD}
\end{document}

And I get x and y before and after (since I failed limiting with a command and }) but I cannot get it to format properly. Also I would like to avoid the {1} in call, and use

\split{ABCD}

only. It is always 1-charater splits.

Any help is much appreciated!

5 Answers 5

8

If your input is not guaranteed to be “ASCII-only” an approach with \seq_set_split:Nnn may not work.

I changed the name to \splitchars because \split is taken up by amsmath.

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{xcolor}

\NewDocumentCommand{\formatchar}{m}{%
  \textcolor{red!90!yellow}{#1}% <--- or whatever you like
}

\ExplSyntaxOn

\NewDocumentCommand{\splitchars}{O{|}m}
 {% #1 = optional separator, default |
  % #2 = text to split
  \mflxvii_split:nn { #1 } { #2 }
 }

\seq_new:N \l__mflxvii_split_items_seq

\cs_new_protected:Nn \mflxvii_split:nn
 {
  \seq_clear:N \l__mflxvii_split_items_seq
  \text_map_inline:nn { #2 }
   {
    \seq_put_right:Nn \l__mflxvii_split_items_seq { \formatchar{##1} }
   }
  \seq_use:Nn \l__mflxvii_split_items_seq { #1 }
 }

\ExplSyntaxOff

\begin{document}

\splitchars{ABC}

\splitchars[--]{ABC}

\splitchars{ábç}

\splitchars{ÅÄÖ}

\end{document}

enter image description here

In order to get a list-like output, here's an extension that allows a different separator between the last two items.

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{xcolor}

\NewDocumentCommand{\formatchar}{m}{%
  \textcolor{red!90!yellow}{#1}% <--- or whatever you like
}

\ExplSyntaxOn

\NewDocumentCommand{\splitchars}{O{|}O{#1}m}
 {% #1 = optional separator, default |
  % #2 = optional separator between last two
  % #3 = text to split
  \mflxvii_split:nnn { #1 } { #2 } { #3 }
 }

\seq_new:N \l__mflxvii_split_items_seq

\cs_new_protected:Nn \mflxvii_split:nnn
 {
  \seq_clear:N \l__mflxvii_split_items_seq
  \text_map_inline:nn { #3 }
   {
    \seq_put_right:Nn \l__mflxvii_split_items_seq { \formatchar{##1} }
   }
  \seq_use:Nnnn \l__mflxvii_split_items_seq { #2 } { #1 } { #2 }
 }

\ExplSyntaxOff

\begin{document}

\splitchars{ABC}

\splitchars[--]{ABC}

\splitchars{ábç}

\splitchars{ÅÄÖ}

\splitchars[, ][ and ]{ÅÄÖ}

\splitchars[, ][ and ]{ÅÄ}

\end{document}

enter image description here

8
  • Thank you. Is there some way to get in contact with you for a commission? I have not found any 'dm'/'pm' function on this site.
    – mf67
    Commented Dec 21, 2023 at 11:27
  • It did not work, and gives error LaTeX cmd Error: Command '\split' already defined. I'm running pdfLaTeX, TeXLive 2022
    – mf67
    Commented Dec 21, 2023 at 21:45
  • In addition it cannot handle ÅÄÖ. Can this be corrected?
    – mf67
    Commented Dec 21, 2023 at 21:52
  • 1
    @mf67 I changed the name, because \split is preempted by amsmath. But you see that it deals correctly with ÅÄÖ. Are you using UTF-8? I tried with TeX Live 2022 and it works the same.
    – egreg
    Commented Dec 21, 2023 at 22:04
  • 1
    @mf67 Sure. Added in the last edit.
    – egreg
    Commented Dec 30, 2023 at 10:05
6

Here's a LuaLaTeX-based solution, which makes use of Lua's gsub and sub built-in functions. Actually, the code uses versions of the gsub and sub functions that can handle utf8-encoded characters directly.

The main user-level macro is called \splitstring rather than \split, because the amsmath package defines an environment called split along with macros called \split and \endsplit.

Observe that (a) the argument of \splitstring can contain whitespace and (b) formatchar is implemented as a Lua function rather than as a LaTeX macro.

enter image description here

% !TEX TS-program = lualatex
\documentclass{article}
\usepackage{xcolor}  % for '\textcolor' macro

% Lua-side code
\directlua{
  function formatchar ( c )
     return ( '\\textcolor{red}{\\textbf{' .. c .. '}}|' )
  end
  function split ( s )
     s = unicode.utf8.gsub ( s , '.' , formatchar )
     return ( unicode.utf8.sub ( s , 1 , -2 ) ) % delete final '|'
  end
}
% LaTeX-side code
\newcommand\splitstring[1]{\directlua{tex.sprint(split('#1'))}}

\begin{document}
\obeylines % just for this example
\splitstring{ABC}
\splitstring{123abc}
\splitstring{Hello World}
\end{document}
3
  • Thank you for your solution. I never learnt Lua but understand it is very powerful. Can you recommend a good starter book for Lua with LaTeX?
    – mf67
    Commented Dec 21, 2023 at 9:50
  • 1
    @mf67 - I will assume you're pretty good at LaTeX. I'm not sure there are a great many books out there that would fit your stated need. I'd recommend studying An Introduction to LuaTeX (Part 1): What is it—and what makes it so different? and An Introduction to LuaTeX (Part 2): Understanding \directlua. There are lots of books on learning Lua.
    – Mico
    Commented Dec 21, 2023 at 22:51
  • No, I'm not very good at all. I try to understand, but mostly copy and try to adjust what others have done earlier to fit my needs. I'm not very successful most of the time… Thank you for the links.
    – mf67
    Commented Dec 21, 2023 at 23:13
5

Here is my solution attempt using ExplSyntax:

\documentclass{article}
\usepackage{xcolor}

\ExplSyntaxOn
\seq_new:N \l__my_split_seq
\NewDocumentCommand { \split } { m } {  \__my_split:n { #1 }  }
\cs_new_protected:Npn \__my_split:n #1
{
  \seq_set_split:Nnn \l__my_split_seq {} { #1 }
  \seq_set_map:NNn \l__my_split_seq \l__my_split_seq
  {  \__my_split_fn:n { ##1 }  }
  % \seq_log:N \l__my_split_seq
  \seq_use:Nn \l__my_split_seq { | }
}
\cs_new_protected:Npn \__my_split_fn:n #1 {   {  \color { red } #1  }   }
\ExplSyntaxOff

\begin{document}

\split{ABCD}

\split{1234}

\end{document}

enter image description here

1
  • Thank you, very kind of you. I don't know Expl, but it seems good. Why is one line commented? Is it important?
    – mf67
    Commented Dec 21, 2023 at 9:38
4

Nobody actually tried to use the OPs code with xstring, which is arguably the easiest :) Here a recursive definition of \split, using \StrSplit and \IfStrEq. It works also for accented letters.

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{xstring}
\usepackage{xcolor}

\def\formatchar#1{{\color{red}#1}}

\def\split#1{%
    \StrSplit{#1}{1}{\tempa}{\tempb}% split into first character \tempa and the rest \tempb
    \formatchar{\tempa}%            % print first character in red
    \IfStrEq{\tempb}{}{}{%          % if rest is empty (first {}) do nothing (second {}), which will stop the recursive loop.
    |%                              % else: print a bar, and
    \split{\tempb}%                 % call the function again for the rest,
    }%                              % which will split the first character of the rest etc.
}

\begin{document}
\split{ABCDÅÄÖ}
\end{document}

enter image description here

3
  • Nice idea: +1 . // The stop condition is critical to avoid infinite loops. It's in your code, off course, if you know what to look for. Do you have any hints for future readers on this subject? Or can you augment your code with some comments? Thank you
    – MS-SPO
    Commented Jan 18 at 7:35
  • 1
    @MS-SPO I added comments, I hope it is more clear now.
    – Marijn
    Commented Jan 18 at 7:47
  • Thank you: great
    – MS-SPO
    Commented Jan 18 at 7:48
3

Since there are no LaTeX2e solutions offered, I give one here. I use a token cycle to grab characters one-by-one from the argument. This solution assumes the argument has no macros and no groups. I omitted those, because no guidance was given on how they should be handled, though they can be handled, as needed.

If unicode characters need to be handled, compilation must be performed under xelatex or lualatex.

\documentclass{article}
\usepackage{tokcycle,xcolor}
\newcommand\formatchar[1]{\textcolor{red}{#1}}
\def\split#1{%
  \tokencycle{\formatchar{##1}\splitchar}
  {}{}{##1\splitchar}#1\endtokencycle}
\newcommand\splitchar{\tcpeek\z\ifx\empty\z\else$\mid$\fi}
\begin{document}
\split{ABCD}

\split{Å ÄÖ}
\end{document}

enter image description here

2
  • 1
    +1. I don't think that $\mid$ is quite right, though. I'd suggest either | or \textbar.
    – Mico
    Commented Dec 22, 2023 at 3:24
  • 1
    @Mico Maybe so, semantically; however, there appears to be no visual distinction between $\mid$ and \textbar. Commented Dec 22, 2023 at 3:33

You must log in to answer this question.

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