Here's a way with expl3
and Heiko Oberdiek's catchfile
:
\documentclass{article}
\usepackage[T1]{fontenc} % for the underscore
\usepackage{catchfile}
\usepackage{xparse}
\ExplSyntaxOn
\cs_set_eq:NN \egreg_catchfiledef:nnn \CatchFileDef
\cs_generate_variant:Nn \egreg_catchfiledef:nnn { c }
\cs_generate_variant:Nn \seq_set_split:Nnn { Nnv }
\NewDocumentCommand{\storefile}{ m m } % #1 = symbolic name, #2 = file name
{
\egreg_catchfiledef:cnn { l_egreg_ #1 _list_tl } { #2 } { \char_set_catcode_other:N \_ }
}
\NewDocumentCommand{\printtable}{ m m } % #1 = number of columns, #2 = symbolic name
{
\egreg_printtable:nn { #1 } { #2 }
}
\tl_new:N \l_egreg_temp_table_tl
\seq_new:N \l_egreg_temp_list_seq
\int_new:N \l_egreg_col_count_int
\cs_new_protected:Npn \egreg_printtable:nn #1 #2
{
\seq_set_split:Nnv \l_egreg_temp_list_seq { , } { l_egreg_ #2 _list_tl }
\tl_clear:N \l_egreg_temp_table_tl
\int_zero:N \l_egreg_col_count_int
\seq_map_inline:Nn \l_egreg_temp_list_seq
{
\int_incr:N \l_egreg_col_count_int
\tl_put_right:Nn \l_egreg_temp_table_tl { ##1 }
\int_compare:nTF { \l_egreg_col_count_int = #1 }
{ \tl_put_right:Nn \l_egreg_temp_table_tl { \\ } \int_zero:N \l_egreg_col_count_int }
{ \tl_put_right:Nn \l_egreg_temp_table_tl { & } }
}
\begin{tabular}{*{#1}{c}}
\l_egreg_temp_table_tl
\end{tabular}
}
\ExplSyntaxOff
\begin{document}
\storefile{words}{my_words.txt}
\noindent Three words per line
\noindent\printtable{3}{words}
\bigskip
\noindent Five words per line
\noindent\printtable{5}{words}
\bigskip
\noindent Eight words per line
\noindent\printtable{8}{words}
\end{document}
The \storefile
stores the contents of the file under a symbolic name (in this case words
). Then \printtable
can print the table using the data.
We first define an expl3
equivalent to \CatchFileDef
for being able to generate variants for it. Then the \storefile
command is defined: it takes a symbolic name for the word list and the file name as arguments. It stores the entire file in a token list variable (underscores will be absorbed as printable characters).
The \printtable
gets the desired number of columns and the symbolic name of the list as arguments. It's, as usual, simply translated into a function call.
The function splits the token list corresponding to the symbolic name at commas and does a mapping on the sequence thus obtained. At each step we increment a counter and add the word to a temporary token list; if the counter's value is not the number of columns we add also a &
, otherwise we add \\
and reset the counter to zero.
Finally, the entire list is printed inside a tabular
. You might want to load longtable
and change tabular
into longtable
if the word list is long.
For choosing between "row" and "column" order, you can modify it like this:
\documentclass{article}
\usepackage[T1]{fontenc} % for the underscore
\usepackage{catchfile,multicol}
\usepackage{xparse}
\ExplSyntaxOn
\cs_set_eq:NN \egreg_catchfiledef:nnn \CatchFileDef
\cs_generate_variant:Nn \egreg_catchfiledef:nnn { c }
\cs_generate_variant:Nn \seq_set_split:Nnn { Nnv }
\NewDocumentCommand{\storefile}{ m m } % #1 = symbolic name, #2 = file name
{
\egreg_catchfiledef:cnn { l_egreg_ #1 _list_tl } { #2 } { \char_set_catcode_other:N \_ }
}
\NewDocumentCommand{\printtable}{ s m m } % #2 = number of columns, #3 = symbolic name
{
\IfBooleanTF{#1}
{ \egreg_printtablev:nn { #2 } { #3 } }
{ \egreg_printtable:nn { #2 } { #3 } }
}
\tl_new:N \l_egreg_temp_table_tl
\seq_new:N \l_egreg_temp_list_seq
\int_new:N \l_egreg_col_count_int
\cs_new_protected:Npn \egreg_printtable:nn #1 #2
{
\seq_set_split:Nnv \l_egreg_temp_list_seq { , } { l_egreg_ #2 _list_tl }
\tl_clear:N \l_egreg_temp_table_tl
\int_zero:N \l_egreg_col_count_int
\seq_map_inline:Nn \l_egreg_temp_list_seq
{
\int_incr:N \l_egreg_col_count_int
\tl_put_right:Nn \l_egreg_temp_table_tl { ##1 }
\int_compare:nTF { \l_egreg_col_count_int = #1 }
{ \tl_put_right:Nn \l_egreg_temp_table_tl { \\ } \int_zero:N \l_egreg_col_count_int }
{ \tl_put_right:Nn \l_egreg_temp_table_tl { & } }
}
\begin{tabular}{*{#1}{c}}
\l_egreg_temp_table_tl
\end{tabular}
}
\cs_new_protected:Npn \egreg_printtablev:nn #1 #2
{
\seq_set_split:Nnv \l_egreg_temp_list_seq { , } { l_egreg_ #2 _list_tl }
\begin{multicols}{3}\centering
\seq_map_inline:Nn \l_egreg_temp_list_seq { ##1 \par }
\end{multicols}
}
\ExplSyntaxOff
\begin{document}
\storefile{words}{my_words.txt}
\noindent Three words per line
\noindent\printtable{3}{words}
\bigskip
\noindent Five words per line
\noindent\printtable{5}{words}
\bigskip
\noindent Three equispaced columns (column order)
\noindent\printtable*{14}{words}
\end{document}
The first solution is with booktabs
and without vertical rules (I think the table looks better this way); the second one is like the OP's image.
Edit: I've added a third example which doesn't use siunitx
, to show the difference to the OP.
\documentclass{article}
\usepackage{siunitx}
\usepackage{array}
\usepackage{booktabs}
\usepackage{csvsimple}
\usepackage{caption}
\begin{filecontents*}{mydata.csv}
1.8233,1.8233,1.8233,0.9243,0.8651,0.9013,0.3217,0.3377,34.4858
0.2753,0.2753,0.2753,0.5383,0.5038,0.5249,0.3217,0.3377,8.4552
0.0898,0.0898,0.0898,0.2804,0.2625,0.2734,0.3217,0.3377,1.5514
0.4689,0.1193,0.0417,0.8046,0.2227,0.1795,0.6413,0.3307,6.4488
0.339,0.8068,0.0936,0.4335,0.8036,0.2434,0.3046,0.624,20.8422
0.2162,0.133,0.8711,0.1707,0.1503,0.8215,0.1562,0.0692,2.3365
0.5187,0.9138,1.0432,0.4332,0.8028,0.8406,0.2269,0.3404,22.8164
0.58,0.2096,0.9086,0.808,0.2134,0.8294,0.333,0.1596,8.4349
0.8237,0.9378,0.0855,0.8654,0.8096,0.2487,0.4338,0.5187,26.9923
\end{filecontents*}
\begin{document}
\begin{table}[ht!]
\caption{The best}
\centering\tiny\renewcommand*{\arraystretch}{1.4}
\begin{tabular}{>{\bfseries\arraybackslash}c}
\toprule
Patch\\\cmidrule(lr){1-1}
N1\\ N2\\ N3\\ R\\ G\\ B\\ C\\ M\\ Y\\
\bottomrule
\end{tabular}%
\csvloop{file=mydata.csv, no head,
before reading=\centering%\sisetup{table-number-alignment=center}
,
tabular={c@{}*8{S[table-format=1.4]}S[table-format=2.4]},
table head=\toprule & \multicolumn{3}{c}{\textbf{ACES RGB}} & \multicolumn{3}{c}{\textbf{Display RGB}} & \multicolumn{3}{c}{\textbf{Display xyY}}\\
\cmidrule(lr){1-4} \cmidrule(lr){5-7} \cmidrule(lr){8-10}, command=&\csvcoli & \csvcolii & \csvcoliii & \csvcoliv & \csvcolv & \csvcolvi & \csvcolvii & \csvcolviii & \csvcolix, table foot=\bottomrule}
\end{table}
\begin{table}[ht!]
\caption{The not-so-beautiful}
\centering\tiny\renewcommand*{\arraystretch}{2}
\begin{tabular}{|>{\bfseries\arraybackslash}c|}
\hline
Patch\\\hline
N1\\\hline N2\\\hline N3\\\hline
R\\\hline G\\\hline B\\\hline
C\\\hline M\\\hline Y\\\hline
\end{tabular}%
\csvloop{file=mydata.csv, no head,
before reading=\centering%\sisetup{table-number-alignment=center}
,
tabular={c@{}*8{S[table-format=1.4]|}S[table-format=2.4]|},
table head=\hline & \multicolumn{3}{c|}{\textbf{ACES RGB}} & \multicolumn{3}{c|}{\textbf{Display RGB}} & \multicolumn{3}{c|}{\textbf{Display xyY}}\\
\hline,
command=&\csvcoli & \csvcolii & \csvcoliii & \csvcoliv & \csvcolv & \csvcolvi & \csvcolvii & \csvcolviii & \csvcolix,
late after line=\\\hline}
\end{table}
\begin{table}[ht!]
\caption{The ugly}
\centering\tiny\renewcommand*{\arraystretch}{2}
\begin{tabular}{|>{\bfseries\arraybackslash}c|}
\hline
Patch\\\hline
N1\\\hline N2\\\hline N3\\\hline
R\\\hline G\\\hline B\\\hline
C\\\hline M\\\hline Y\\\hline
\end{tabular}%
\csvloop{file=mydata.csv, no head,
tabular={*9{l|}},
table head=\hline \multicolumn{3}{c|}{\textbf{ACES RGB}} & \multicolumn{3}{c|}{\textbf{Display RGB}} & \multicolumn{3}{c|}{\textbf{Display xyY}}\\
\hline,
command=\csvcoli & \csvcolii & \csvcoliii & \csvcoliv & \csvcolv & \csvcolvi & \csvcolvii & \csvcolviii & \csvcolix,
late after line=\\\hline}
\end{table}
\end{document}
Best Answer
Thank you @TonioElGringo, that does exactly what I was looking for. Here an example for a 3 column .csv file.