11

I have a \foreach that should read the data points from a file. The points are stored in such a way that a simple copy paste would do. In fact, I don't want to do more than copy paste (if ever possible). I found that \input does not work. There are a couple solutions around but they seem to do more than copy-paste. Is there an "easy" way to achieve that?

document.tex:

\documentclass[border=5mm]{standalone}
\usepackage{tikz}

\begin{document}
  \begin{tikzpicture}
  \foreach \from/\to in { \input{data}; } %input does not work...
  {\draw [->] \from -- \to;}

  \end{tikzpicture}
\end{document}

data.dat:

{(1,1)}/{(3,3)},
{(-1,1)}/{(-3,3)},
{(1,-1)}/{(3,-3)}%
Guho
  • 6,115
dani
  • 729

3 Answers3

11

You can use catchfile:

\begin{filecontents*}{\jobname.dat}
{(1,1)}/{(3,3)},
{(-1,1)}/{(-3,3)},
{(1,-1)}/{(3,-3)}%
\end{filecontents*}

\documentclass[border=5mm]{standalone}
\usepackage{tikz}
\usepackage{catchfile}

\newcommand\loaddata[1]{\CatchFileDef\loadeddata{#1}{\endlinechar=-1}}

\begin{document}

\begin{tikzpicture}
\loaddata{\jobname.dat}
\foreach \from/\to in \loadeddata{\draw [->] \from -- \to;}
\end{tikzpicture}

\end{document}

With \endlinechar=-1 we effectively remove the end-of-lines in the loaded file.

enter image description here

egreg
  • 1,121,712
3

Interesting approach. The problems you are encountering stem from the fact that each segment is on a separate line in data.dat and that the \foreach argument should be expanded first. The following reads the contents of the data.dat file (the \loaddata command, which is based on this answer) and appends each line to the end of the \loadeddata command, resulting in all of the data on a single line ({(1,1)}/{(3,3)},{(-1,1)}/{(-3,3)},{(1,-1)}/{(3,-3)}% in your example). This is then expanded and used as the argument in the \foreach call.

\begin{filecontents*}{data.dat}
{(1,1)}/{(3,3)},
{(-1,1)}/{(-3,3)},
{(1,-1)}/{(3,-3)}%
\end{filecontents*}
\documentclass[border=5mm]{standalone}
\usepackage{tikz}

\makeatletter
    \newread\dataread
    \def\loaddata#1{%
        \gdef\loadeddata{}%
        \openin\dataread=#1
        \begingroup\endlinechar=-1
        \@whilesw\unless\ifeof\dataread\fi{%
            \read\dataread to \dataline
            \edef\rslt{\noexpand\g@addto@macro\noexpand\loadeddata{\dataline}}%
            \rslt%expanded argument
        }%
        \endgroup
        \closein\dataread
    }%
\makeatother

\begin{document}
  \begin{tikzpicture}
    \loaddata{data.dat}
    \edef\expfor{\noexpand\foreach \noexpand\from/\noexpand\to in {\loadeddata}}
    \expfor{\draw [->] \from -- \to;}
  \end{tikzpicture}
\end{document}

result

Guho
  • 6,115
2

Why reinvent the wheel? textmerg can merge the data using the following code:

\documentclass[tikz,border=10pt,multi]{standalone}
\usepackage{textmerg}
\begin{document}

\tikzset{%
  make line/.code args={#1/#2}{%
    \draw [->] #1 -- #2;
  },
}
\Fields{\myline}
\newcommand*\makeline[1]{%
  \tikzset{make line=#1}}
\begin{tikzpicture}
  \Merge{\jobname.dat}{
    \expandafter\makeline\expandafter{\myline}
  }
\end{tikzpicture}
\end{document}

data lines

cfr
  • 198,882
  • Admittedly, it is not the wheel requested in the question as I don't use \foreach but that avoids a double-loop (or it appears to - don't know what textmerg does, mind).... (No idea if it is more or less efficient.) – cfr Dec 15 '15 at 23:27
  • Excellent point. Thanks for bringing textmerg to my attention! – Guho Dec 15 '15 at 23:35
  • Thanks. It is useful. I use it to merge output from spreadsheets saved as .csv files. Then I can merge the data into a template to produce forms or letters or whatever. It doesn't seem much used on this site, though. – cfr Dec 15 '15 at 23:53