8

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)}%
6

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

  • This works wonderful - and its the easiest solution. – dani Dec 16 '15 at 19:58
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

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

  • 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

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.