[Tex/LaTex] Reading Arbitrary Text into a table format

expl3external filestables

Another file question from Latex; this time I'm trying to read in data from a single text file and put it directly into a table.

The file reads something like:

something | data | stuff | bacon

something else | data | different stuff | more bacon

3rd | 3rd data | you get the idea | bacon

To do this I could just use a simple \input but then I'd have to have all the table setup inside the text file. Since I'm not the one going to make the .txt files I'd prefer it stay nice and simple.
My solution was to use expl3 and xparse. However I'm having a rough time figuring out how to make Latex spit out the file into a table. The closest I've come uses the following code and makes something like the picture.

This code I did not make myself. I found it from Arbitrary text parsing from a separate file via egreg: https://tex.stackexchange.com/users/4427/egreg . His response there was very helpful and I looked into expl3 and am beginning to get a better understanding. Unfortunately I don't understand this code too much; merely the syntax (still a bit shaky there too). My general understanding is that it makes a function within a function, etc. And then reads in a file. This code will read all the lines of a file, and I'm not sure how that loops or if it's recursive. I also have no idea what \taylor means. (Google was helpful in figuring most of it out, but no luck finding \taylor).

Any explanations of the code would also be much appreciated.

\NewDocumentCommand{\readdata}{O{|} m}
{ 
    \readdata:nn { #1 } { #2 }%
}


\cs_new_protected:Npn \readdata:nn #1 #2%
{
    \ior_open:Nn \g__read_ior { #2 }%
    \ior_map_inline:Nn \g__read_ior%
    {%
        \__process:nn { #1 } { ##1 }%
    }%
    \ior_close:N \g__read_ior%

}


\cs_new_protected:Npn \__process:nn #1 #2%
{   
    \seq_set_split:Nnn \l__line_seq { #1 } { #2 }%
    \use:x%
    {%
        \exp_not:N \DataEntry%
        \seq_map_function:NN \l__line_seq \__brace:n%
    }%
}


\cs_new:Npn \__brace:n #1 { { #1 } }%
\ExplSyntaxOff


\newcommand{\DataEntry}[4]  % Predetermined number of columns for data
{
    #1 & #2 & #3 & #4 \\%
    \hline
}


...later...


\begin{table}[ht] 
    \head{\caption{Results}}% title of Table 
    \centering 
    \begin{tabular}{|p{2.5cm}|p{2.5cm}|p{2.5cm}|p{3.5cm}|}

           I'd prefer to use |c| to have them centered, but that doesn't seem to work yet...

    \hline\hline
    Sample \# & Something & Results & Description \\ [0.5ex] % inserts table headings
    \hline      

\readdata[|]{data6.txt}
    \label{table:nonlin} % is used to refer this table in the text 
        \end{tabular}
    \end{table}

Image of horrible table

Best Answer

The problem is that you're trying to add more lines to a tabular, but this can't be done with a loop starting in one cell and ending in another one. The loop must be performed completely, by storing the various table rows in a token list variable and eventually delivering this one.

\begin{filecontents*}{\jobname-data.txt}
something | data | stuff | bacon
something else | data | different stuff | more bacon
3rd | 3rd data | you get the idea | bacon
\end{filecontents*}

\documentclass{article}
\usepackage{xparse,booktabs}

\ExplSyntaxOn
\NewDocumentCommand{\readdata}{O{|} m}
 { 
  \mhag_readdata:nn { #1 } { #2 }
 }
\NewDocumentCommand{\DataEntry}{m m m m}% Predetermined number of columns for data
 {
  #1 & #2 & #3 & #4 \\
 }

\ior_new:N \g__mhag_read_stream % an input stream
\seq_new:N \l__mhag_line_seq    % a temporary sequence for processing a row
\tl_new:N \l__mhag_body_tl      % a container for the table body

\cs_new_protected:Npn \mhag_readdata:nn #1 #2
 {
  % clear the table body
  \tl_clear:N \l__mhag_body_tl
  % open the input stream
  \ior_open:Nn \g__mhag_read_stream { #2 }
  % loop on the file lines
  \ior_map_inline:Nn \g__mhag_read_stream
   {% process the current line
    \__mhag_process:nn { #1 } { ##1 }
   }
  % the loop has ended, close the stream
  \ior_close:N \g__mhag_read_stream
  % deliver the table body
  \tl_use:N \l__mhag_body_tl
 }


\cs_new_protected:Npn \__mhag_process:nn #1 #2
 {% split the line at | (or the character specified in the optional argument
  \seq_set_split:Nnn \l__mhag_line_seq { #1 } { #2 }
  % add a line to the table body
  \tl_put_right:Nx \l__mhag_body_tl
   {% this will put a in \l__mhag_body_tl the tokens
    % \DataEntry{<item 1>}{<item 2>}{<item 3>}{<item 4>}
    \DataEntry \seq_map_function:NN \l__mhag_line_seq \__mhag_brace:n
   }
 }

% the auxiliary function for bracing each item
\cs_new:Npn \__mhag_brace:n #1 { { #1 } }
\ExplSyntaxOff

\begin{document}

\begin{table}[ht] 
\centering 
\caption{Results}% title of Table 
\label{table:nonlin} % is used to refer this table in the text

\medskip

\begin{tabular}{@{}p{2.5cm}p{2.5cm}p{2.5cm}p{2.5cm}@{}}
\toprule
Sample \# & Something & Results & Description \\ % inserts table headings
\midrule
\readdata[|]{\jobname-data.txt}
\bottomrule
\end{tabular}

\end{table}

\end{document}

I've used filecontents* to keep the example selfcontained. I also used booktabs to get a better table.

enter image description here