How can I split the string Hello World!
- stored into a macro \def\mystring{Hello Word!}
- into a comma-separated list of characters (including spaces) which can be used as argument of a \foreach
loop (\foreach \char in {\myCSlist}
) in order to loop over each character (as with \foreach \char in {H,e,l,l,o,\space,W,o,r,l,d,!})
?
EDIT 1 (Mar 21): Why I want that?
At first, I didn't explain why I want the solution generates a comma-separated list of characters (e.g. \myCSlist
) which can be used as argument of a \foreach
. It's because I want to create a Tikz \node
for each character using a pic
. Something like this (from here):
\newcommand{\hsp}{.5}
\tikzset{symbols/.pic={%
\foreach \s[count=\n from 0] in {\myCSlist}{%
\pgfmathsetmacro{\myangle}{360*rnd}%
\node[rotate=\myangle] at (\hsp*\n,0){\s};%
}}}%
EDIT 2 (Mar 21): Answers review (regarding to my specifications)
Please feel free to edit this part!
The following comparison is made considering the use of LuaLaTeX engine for compilation (so no need of fontenc
as pointedd out by Mico in comment).
Answer | \foreach requirement |
Nb Packages* | Bonus | Particularity |
---|---|---|---|---|
StevenB.Segletes's (listofitems ) |
Yes | 2 | handles macro in string input, UTF-8, compatible with [tikz]{standalone} class |
|
StevenB.Segletes's (tokcycle only) |
Yes | 1 | UTF-8, compatible with [tikz]{standalone} class |
|
Mico's | ~Yes | 1 | UTF-8, compatible with [tikz]{standalone} class |
LuaLaTeX code |
SandyG's | Yes | 1 | UTF-8, compatible with [tikz]{standalone} class |
|
egreg's \stringforeach |
Yes | 2 | UTF-8 | expl3 |
egreg's \processtokenlist |
No? | 2 | UTF-8 | expl3 |
wipet's \replspaces |
No? | 0 | UTF-8 ,compatible with [tikz]{standalone} class |
TeX code |
wipet's \myforeach |
Yes | 0 | UTF-8 ,compatible with [tikz]{standalone} class |
TeX code |
Legend:
- "
\foreach
requirement" = Is the solution pemits to use the output list into a\foreach
or something with equivalent features? - "No?" = I was not able to make it works myself.
- "~Yes" = Not direct because of the use of "special" language (ex.
LuaLaTeX
orexpl3
). - "*" =
pgffor
not counted because I will use Tikz anyway.
EDIT 3 (Mar 22): Implementation of the Tikz nodes with pic
\tikzset{%
symbols/.pic={\markletters{Hello World!}%
(\detokenize\expandafter{\mystring})%
\readlist\mylist\mystring
\foreachitem\z\in\mylist{%
\pgfmathsetmacro{\myangle}{360*rnd}%
\pgfmathsetmacro{\mystep}{\zcnt-1}% start from 0 (\zcnt macro is provided by listofitems package)
\node[rotate=\myangle] at (\hsp*\mystep,0){\z};%
}}}%
Not working if the string Hello World!
is stored into a macro (e.g. \def\myFirststring{Hello World!}
) but it seems to be feasible (see StevenB.Segletes's comments).
\newcounter{cnt}
\setcounter{cnt}{-1}
\newcommand\markletters[1]{%
\tokencycle{\stepcounter{cnt}%
\pgfmathsetmacro{\myangle}{360*rnd}%
\pgfmathsetmacro{\mystep}{\thecnt}%
\node[rotate=\myangle] at (\hsp*\mystep,0){##1};}{}{}{\stepcounter{cnt}%
\node[rotate=\myangle] at (\hsp*\mystep,0){##1};}#1\endtokencycle%
\setcounter{cnt}{-1}}%
\tikzset{symbols/.pic={\markletters{Hello World!}}}%
Not working if the string Hello World!
is stored into a macro (e.g. \def\myFirststring{Hello World!}
) but it seems to be feasible (see StevenB.Segletes's comments).
WARNING: this method is not compatible with \pic[red] {symbols};
(i.e. the text is not colored in red...).
\luaexec{
function DoString ( s )
for i = 1,string.utflength(s) do
tex.sprint ( '\\pgfmathsetmacro{\\mystep}{'.. i .. '-1}% start from 0 ('.. i .. ' acces the loop integer)
\\pgfmathsetmacro{\\myangle}{360*rnd}%
\\node[rotate=\\myangle] at (\\mystep*10 pt,0) {' .. unicode.utf8.sub ( s , i , i ) .. '};%
' )
end
end
}
\tikzset{symbols/.pic={\DoString{\mystring}}}%
\newcommand{\markletters}[1]{\StrLen{#1}[\strlen]\foreach \chr[count=\n from 0] in {1,...,\strlen}{\pgfmathsetmacro{\myangle}{360*rnd}%
\node[rotate=\myangle] at (\hsp*\n,0){\StrChar{#1}{\chr}};}}%
\tikzset{symbols/.pic={\markletters{\mystring}}}%
\tikzset{symbols/.pic={\stringforeach \char [count=\n from 0] in \mystring {\pgfmathsetmacro{\myangle}{360*rnd}\node[rotate=\myangle] at (\hsp*\n,0){\char};}}}%
\newcounter{cnt}%
\setcounter{cnt}{-1}%
\def\body#1{\stepcounter{cnt}\pgfmathsetmacro{\myangle}{360*rnd}\pgfmathsetmacro{\mystep}{\thecnt}\pgfmathsetmacro{\myangle}{360*rnd}\node[rotate=\myangle] at (\hsp*\mystep,0){#1};}%
\tikzset{symbols/.pic={\replspaces\mystring
\myforeach\mystring\setcounter{cnt}{-1}%
}}%
All these methods give the following result:
If the following code is used (e.g. given with SandyG's solution):
\documentclass{standalone}
\usepackage{tikz}
\usepackage{xstring}
\newcommand{\markletters}[1]{\StrLen{#1}[\strlen]\foreach \chr[count=\n] in {1,...,\strlen}{\pgfmathsetmacro{\myangle}{360*rnd}%
\node[rotate=\myangle] at (\hsp*\n,0){\StrChar{#1}{\chr}};}}%
\def\mystring{Hello World!}%
\newcommand{\hsp}{.5}%
\tikzset{symbols/.pic={\markletters{\mystring}}}%
\begin{document}
\begin{tikzpicture}
\pic {symbols};
\end{tikzpicture}
\end{document}
EDIT 4 (Mar 23): My final choice (fully opinion-based ;)
I could use all the solutions with a "Yes" (or "~Yes") in the \foreach
column of the above table, but I have to choose only one...
It as to be noted that the only solution which creates a macro which can be used as an \foreach
argument is SandyG's solution (this feature was explicity mentionned in my question).
The egreg's \stringforeach
, StevenB.Segletes's (listofitems
) (with the \foreachitem
provided by listofitems
package) and wipet's \myforeach
solutions achieves equivalent functionality but:
- SandyG's solution supports UTF-8 (egreg's
\stringforeach
solution doesn't yet). - SandyG's solution is compatible with
[tikz]{standalone}
document class (strange things happen with egreg's\stringforeach
solution). - SandyG's solution handle the use of a string input strored into a macro:
\def\mystring{Hello Word!}
(I was not able achieve this feature with StevenB.Segletes's (listofitems
) but it seems to be feasible, see StevenB.Segletes's comments) - SandyG's solution uses a very short code that is easily understood by a beginner (which is not the case with
expl3
orTeX
).
My choice is therefore towards the solution of SandyG.
First attempt
I tried with the \markletters
macro of egreg (using xparse and expl3) but the results seems to be not suitable for looping with foreach
.
I changed the result print - of the \markletters
macro - from (##1)
to ##1,
, in order to obtain a comma-separated list, but this also results in a unwanted comma ,
at the end of the comma-separated list.
Here a M(Non)WE:
\documentclass{article}
\usepackage{tikz}% For the \foreach loop
\usepackage{xparse}
% egreg's \markletters macro : https://tex.stackexchange.com/a/359204/262081
\ExplSyntaxOn
\NewDocumentCommand{\markletters}{om}
{
\IfNoValueTF{#1}
{
\kessels_markletters:nn { #2 } { \tl_use:N \l_kessels_marked_letters_tl }
}
{
\kessels_markletters:nn { #2 } { \tl_set_eq:NN #1 \l_kessels_marked_letters_tl }
}
}
\tl_new:N \l_kessels_unmarked_letters_tl
\tl_new:N \l_kessels_marked_letters_tl
\cs_new_protected:Nn \kessels_markletters:nn
{
\tl_set:Nn \l_kessels_unmarked_letters_tl { #1 }
\tl_replace_all:Nnn \l_kessels_unmarked_letters_tl { ~ } { \textvisiblespace }
\tl_clear:N \l_kessels_marked_letters_tl
\tl_map_inline:Nn \l_kessels_unmarked_letters_tl
{
\tl_put_right:Nn \l_kessels_marked_letters_tl { ##1, }
}
#2
}
\ExplSyntaxOff
\def\mystring{Hello World!}
\begin{document}
\markletters[\foo]{\mystring}%
\foreach \char in {\foo}{%
<\char>%
}%
\end{document}
This gives the following result:
But I would prefer this result (note the presence of the space between 'Hello' and 'World'):
lua
to do this, instead of all this weird\tl_new:N #
unreadable stuff :)