3

I'm looking for a luatex solution doing something like this.

\split{a;b;c;d;e}

This command should produce a tabular like this one.

\begin{tabular}{|*{5}{c|}}
\hline
a & b & c & d & e \\
\hline
\end{tabular}

I'm trying with such a code, but I have many problems with compiling and debuging is not very easy.

\documentclass[10pt,oneside]{article}
\usepackage{luatextra}

\newcommand{\split}[1]{%
\directlua{
    lst = string.split("#1",";")
    max = \luaescapestring{##lst}
    tex.print(format.string([[\string\begin{tabular}{|*{\%i}{c|}]],max))
    tex.print([[\string\hline]])
    for i,x in ipairs(lst) do
         if i == max then
             tex.print(format.string([[\%s \string\string\]],x)
         else
             tex.print(format.string([[\%s \&]],x)
         end
    end
    tex.print([[\string\hline]])
    tex.print([[\string\end{tabular}]])}}

\begin{document}
\split{a;b;c;d;e}
\end{document}

Edit 1 : The following code is working for me.

\documentclass[10pt,oneside]{article}
\usepackage{luatextra}

\begin{luacode*}
  function split(input)
    local lst = string.split(input,";")
    local max = #lst
    tex.print(string.format([[\begin{tabular}{|*{%i}{c|}}]],max))
    tex.print([[\hline]])
    for i, x in ipairs(lst) do
       if i == max then
         tex.print(string.format([[%s \\]],x))
       else
         tex.print(string.format([[%s & ]],x))
       end
    end
    tex.print([[\hline\end{tabular}]])
  end
\end{luacode*}

\newcommand{\split}[1]{\directlua{split("#1")}}

\begin{document}
\split{a;b;c;d;e}
\end{document}
  • Is this essentially a learning exercise? That is, you are just trying to learn Lua rather than solve the problem you specifically mention? Otherwise, why the insistence on Lua? – cfr Apr 16 '16 at 0:36
  • Yes, this is an exercice and i'm trying to learn lua. I need to understand some things about directlua and special characters. – MB4E Apr 16 '16 at 0:47
3

Lua's string does not have a split function by default. See Split Join for numerous design and implementation examples.

Writing Lua in TeX makes escaping of special characters an issue. Package luacode provides several methods and environments for writing Lua code. Its documentation provides an overview, how which character in each method are escaped.

Since it's an exercise, the following example does not use luatextra or luacode and used the primitive \directlua directly.

The special characters:

  • The percent sign % is the comment character in TeX, therefore the example defines \% to expand to the percent character.

  • \# is defined as escape for the hash character.

  • \\ is defined as escape for the backslash. Caution: Some methods of luacode use the input \\ for the double double backslash and use \string\ for the single backslash except for environment luacode*, where the backslash is written in verbatim manner.

Since TeX would replaces line ends by spaces, the code in \directlua would be one huge line for Lua and errors are always reported in the first line. Therefore the end of line behavior is changed inside a group to make a new line character (LF/0x10) instead. Because TeX reads a complete line before executing the contents of the line, the percent comment after the final \endgroup disables the read end line character. Otherwise the new line character would be seen by TeX as normal character, causing the error Missing\begin{document}`, if used in the preamble or the end of line would go as character (slot 10 of the current font) in the typeset output.

Full example:

% arara: lualatex
\documentclass[10pt,oneside]{article}

% Function string:split
%
% Source: http://lua-users.org/wiki/SplitJoin (GavinKistner)
%
% Written for 5.0; could be made slightly cleaner with 5.1
% Splits a string based on a separator string or pattern;
% returns an array of pieces of the string.
% (May optionally supply a table as the third parameter
% which will be filled with the results.)
\begingroup
  \makeatletter
  \let\%\@percentchar
  \let\\\@backslashchar
  \edef\#{\string#}%
  \catcode10=12 %
  \endlinechar=10 %
\directlua{%
  function string:split( inSplitPattern, outResults )
    if not outResults then
      outResults = { }
    end
    local theStart = 1
    local theSplitStart, theSplitEnd = string.find( self, inSplitPattern, theStart )
    while theSplitStart do
      table.insert( outResults, string.sub( self, theStart, theSplitStart-1 ) )
      theStart = theSplitEnd + 1
      theSplitStart, theSplitEnd = string.find( self, inSplitPattern, theStart )
    end
    table.insert( outResults, string.sub( self, theStart ) )
    return outResults
  end
}\directlua{%
  function semicolon_split(input)
    local lst = input:split(";")
    local max = \#lst
    tex.print(string.format([[\\begin{tabular}{|*{\%i}{c|}}]],max))
    tex.print([[\\hline ]])
    for i, x in ipairs(lst) do
       if i == max then
         tex.print(string.format([[\%s\\\\]],x))
       else
         tex.print(string.format([[\%s&]],x))
       end
    end
    tex.print([[\\hline\\end{tabular}]])
  end
}\endgroup%

\newcommand{\split}[1]{%
  \directlua{semicolon_split("\luatexluaescapestring{#1}")}%
}

\begin{document}
\split{a;b;c;d;e}
\end{document}

Result

For comparison, the same with environment luacode. There, % and # can be used directly or used as \% and \#. The backslash is available via the longer \string\, more correctly, \ is not a valid token, thus \string is applied to the next command starting with \. The macro \\ generates the double backslash.

% arara: lualatex
\documentclass[10pt,oneside]{article}
\usepackage{luacode}

% Function string:split
%
% Source: http://lua-users.org/wiki/SplitJoin (GavinKistner)
%
% Written for 5.0; could be made slightly cleaner with 5.1
% Splits a string based on a separator string or pattern;
% returns an array of pieces of the string.
% (May optionally supply a table as the third parameter
% which will be filled with the results.)
\begin{luacode}
  function string:split( inSplitPattern, outResults )
    if not outResults then
      outResults = { }
    end
    local theStart = 1
    local theSplitStart, theSplitEnd = string.find( self, inSplitPattern,
    theStart )
    while theSplitStart do
      table.insert( outResults, string.sub( self, theStart, theSplitStart-1 ) )
      theStart = theSplitEnd + 1
      theSplitStart, theSplitEnd = string.find( self, inSplitPattern, theStart )
    end
    table.insert( outResults, string.sub( self, theStart ) )
    return outResults
  end
\end{luacode}
\begin{luacode}
  function semicolon_split(input)
    local lst = input:split(";")
    local max = #lst
    tex.print(string.format([[\string\begin{tabular}{|*{%i}{c|}}]],max))
    tex.print([[\\hline ]])
    for i, x in ipairs(lst) do
       if i == max then
         tex.print(string.format([[%s\\]],x))
       else
         tex.print(string.format([[%s&]],x))
       end
    end
    tex.print([[\string\hline\string\end{tabular}]])
  end
\end{luacode}

\newcommand{\split}[1]{%
  \luadirect{semicolon_split("\luatexluaescapestring{#1}")}%
}

\begin{document}
\split{a;b;c;d;e}
\end{document}

Environment luacode* is probably the easiest to use, if TeX macros, TeX parameters, ... are not needed:

\begin{luacode*}
  function semicolon_split(input)
    local lst = input:split(";")
    local max = #lst
    tex.print(string.format([[\begin{tabular}{|*{%i}{c|}}]],max))
    tex.print([[\hline ]])
    for i, x in ipairs(lst) do
       if i == max then
         tex.print(string.format([[%s\\]],x))
       else
         tex.print(string.format([[%s&]],x))
       end
    end
    tex.print([[\hline\end{tabular}]])
  end
\end{luacode*}
  • +1. A version of the answer, which loads the luacode package and uses a luacode or luacode* environment to house the Lua code, might be instructive as well. – Mico Apr 16 '16 at 6:30
  • @Mico Version with package luacode added. – Heiko Oberdiek Apr 16 '16 at 6:47
  • Is tex.print(string.format([[%s\\]],x)) needed in the luacode and luacode* environments? I would write, tex.print(x.."\\\\"). (x is a string by construction.) – Mico Apr 16 '16 at 6:58
  • @Mico Then the percent sign would be gone ;-) Yes, string concatenation would simplify the code. – Heiko Oberdiek Apr 16 '16 at 7:04
  • Thank you for this detailed answer. Are you sure there is no string.split() function ? Because the code I added in "Edit 1" is working. – MB4E Apr 16 '16 at 7:41
3

LuaTeX has string.explode() to make a table of strings from one string with a separator, suitable for your case. (See the documentation of LuaTeX for that).

But your question is much simpler to answer:

\documentclass{article}
\usepackage{luacode}

\begin{luacode}
split=function(inputstr)
    local str,cnt = string.gsub(inputstr,";"," & ")
    tex.sprint("\\begin{tabular}{|*{",cnt + 1,"}{c|}}")
    tex.sprint("\\hline", str, "\\\\\\hline\\end{tabular}")
end
\end{luacode}

\newcommand{\split}[1]{\luaexec{split(\luastring{#1})}}

\begin{document}
\split{a;b;c;d;e}
\end{document}

You can substitute the ; with & in your string.

Result:

tablewithcharacters


Edit1: replaced the long "\lualatexescapestring{..}" with the much simpler \luastring{..} defined in the luacode package. Thanks to Mico for that hint. Edit2: make function more generic

  • @Mico where is \luastring{} defined? Is it “new”? – I see, I should read the fine luacode manual. Thanks for the pointer! – topskip Apr 16 '16 at 13:48
  • Thanks for this new answer. That's good but the number of tabular cols isn't necessary 5. – MB4E Apr 16 '16 at 15:16
  • @MB78 That's easy, I'll edit my post in a second – topskip Apr 16 '16 at 16:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.