Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I have an external program who writes a text file as follows:

<number> "<nameA>"
<number> "<nameB>"
<number> "<nameC>"
...

How would I parse all lines and assign each number to a macro of the given name?

Here is a prototype of what I'm looking for:

\documentclass{article}
\usepackage{filecontents}% http://ctan.org/pkg/filecontents
\usepackage{siunitx}

\begin{filecontents}{myinput.txt}
0.45 "wingTaperRatio"
12.0 "wingSpanMT"
10.2 "wingAreaMTSquared"
\end{filecontents}

\begin{document}

%
% We want to assign 
%   0.45 to \wingTaperRatio
%   12.0 to \wingSpanMT
%   10.2 to \wingAreaMTSquared
%
\parseInput{myinput.txt}

% So that we can write
Given a wing whose planform has
a taper ratio $\lambda=\num{\wingTaperRatio}$,
a span $b=\SI{\wingSpanMT}{\metre}$, 
and a reference surface $S=\SI{\wingAreaMTSquared}{\metre^2}$,
solve the following problem \ldots

\end{document}

How the macro \parseInput would be implemented?

EDIT: There's one more need here, i.e. that the macro names should admit repetitions. This is because I want to prepare more subfolders, each one with its own myinput.txt. Each subfolder will be related to a different proposed exercise in a book. The data and results of the exercises are handled and written on file by the external program. But some of them may be related to the same physical quantity and values may change from one exercise to the next. So, I may have to use the macro \parseInput several times, with the need of redefining some macros. I may want to have something like:

\section{Exercise 1}
\parseInput{exerciseOne/myinput.txt}

Given a wing whose planform has
a taper ratio $\lambda=\num{\wingTaperRatio}$,
a span $b=\SI{\wingSpanMT}{\metre}$, 
and a reference surface $S=\SI{\wingAreaMTSquared}{\metre^2}$,
solve the following problem \ldots

\section{Exercise 2}
\parseInput{exerciseTwo/myinput.txt}

Now, the taper ratio $\lambda$ is not given,
while the wing has a new span $b=\SI{\wingSpanMT}{\metre}$, 
and a reference surface $S=\SI{\wingAreaMTSquared}{\metre^2}$.
Find the new value of $\lambda$.
share|improve this question
    
The solutions provided by Andrey and me allow for redefining the macros without any problem. –  egreg Mar 13 '12 at 9:56
    
Yeah. I've edited my question further to explain why I need to handle a file like that. –  agodemar Mar 13 '12 at 14:32
add comment

3 Answers

up vote 8 down vote accepted

The best approach would be to configure your software to output data in the format

\newcommand{\<nameA>}{<number>}

Then you could input the data with a mere \input{myinput.txt}.

But even as the data is now, parsing it is possible using various TeX and LaTeX constructs:

\documentclass{article}

\begin{filecontents*}{myinput.txt}
0.45 "wingTaperRatio"
12.0 "wingSpanMT"
10.2 "wingAreaMTSquared"
\end{filecontents*}

\makeatletter
\newread\myinput
\def\parseline#1 "#2"{\@namedef{#2}{#1}}
\newcommand{\parseinput}[1]{%
  \def\@tempb{\par}%
  \openin\myinput=#1
  \loop\unless\ifeof\myinput
    \read\myinput to \@tempa
    \ifx\@tempa\@tempb\else
      \expandafter\parseline\@tempa
    \fi
  \repeat
  \closein\myinput
}
\makeatother

\parseinput{myinput.txt}

\begin{document}

\wingTaperRatio, \wingSpanMT, \wingAreaMTSquared.

\end{document}

The loop will read the file line by line. Each line is then passed to the \parseline macro, which uses delimited arguments to extract the values. Then \@namedef creates a macro of the specified name. Beware that if an input line does not correspond to the parameter specification of \parseline, TeX will raise an error.

share|improve this answer
    
\newread must go outside the definition of \parseinput –  egreg Mar 10 '12 at 13:22
    
@egreg: Indeed, I just fixed it. –  Andrey Vihrov Mar 10 '12 at 13:24
    
@Andrey Vihroy: This answer suits my needs. The external program is 'rigid' and I cannot change the way the output is formatted. Hence the wording of this question. The purpose here is stripping off those pair of double-quote characters. –  agodemar Mar 10 '12 at 14:02
    
@agodemar Note that this will fail if you have a line starting with % in the data file. –  egreg Mar 12 '12 at 15:39
add comment

This LaTeX3 code ought to work

\usepackage{xparse}
\ExplSyntaxOn
\ior_new:N \l_ago_read_s
\tl_new:N \l_ago_read_tl
\NewDocumentCommand{\parseInput}{ m }
  {
   \ior_open:Nn \l_ago_read_s { #1 }
   \group_begin: \tex_endlinechar:D \c_minus_one
   \bool_until_do:nn { \ior_if_eof_p:N \l_ago_read_s }
     {
      \ior_to:NN \l_ago_read_s \l_ago_read_tl
      \tl_if_empty:NF \l_ago_read_tl
      { \exp_after:wN \ago_process_line:w \l_ago_read_tl \q_stop }
     }
   \group_end:
   \ior_close:N \l_ago_read_s
  }

\cs_new:Npn \ago_process_line:w #1 ~ " #2 " \q_stop
  {
   \cs_gset:cpn { #2 } { #1 }
  }

\ExplSyntaxOff

and it's very similar to Andrey Vihrov's code.

The \ago_processline:w function, when feeded with the line

0.45 "wingTaperRatio"

will perform the equivalent of

\gdef\wingTaperRatio{0.45}

so there will be no problem if a new \parseInput command reads another file: the definition of \wingTaperRatio will be silently overwritten.

Note

Unfortunately, it seems that a small bug in the expl3 packages prevents using \ior_open:Nn; the next update of the LaTeX3 package should solve the issue. For the moment one can do with \ior_open_unsafe:Nn instead of \ior_open:Nn.

share|improve this answer
    
Similar? as a train to a mini:) –  Yiannis Lazarides Mar 10 '12 at 16:49
    
@YiannisLazarides It parallels almost perfectly the other code. –  egreg Mar 10 '12 at 16:53
    
Sure it does and is probably more reliable, gets you there; but is so much more wider:) –  Yiannis Lazarides Mar 10 '12 at 16:55
    
+1 for the LaTeX3 example. Does \ago_process_line handle the case when more spaces are present between #1 and "#2" ? –  agodemar Mar 11 '12 at 9:09
    
@agodemar When TeX reads the file, it normalizes consecutive spaces into one space token. –  egreg Mar 11 '12 at 9:20
add comment

For completeness, here is a more general solution. If the starred (*) variant of the defined parser is used, then existing names can't be redefined.

\documentclass{article}
\usepackage{catoptions}

\begin{filecontents*}{myinput1.txt}
% Exaggerated example with large spaces:
   0.45   "   wingTaperRatio1  "
% No space between number and tag:
12.0"wingSpanMT1"
10.2 "wingAreaMTSquared1"
% No name tag:
10.2
% No number:
"wingAreaMTSquaredB1-b"
% The next one will given an error if the starred (*) variant of parser is
% called: existing name is being redefined.
% 11.3 "wingAreaMTSquared1"
\end{filecontents*}

% A different quotation mark (') is used in the next example:
\begin{filecontents*}{myinput2.tex}
0.45x 'wingTaperRatio2'
12.0x 'wingSpanMT2'
10.2x'wingAreaMTSquared2'
10.2x
'wingAreaMTSquared2-b'
% The next one will give an error for starred variant of parser:
% 11.3 'wingAreaMTSquared2'
\end{filecontents*}

\makeatletter
% You can redefine \doemptytag to specify what happens to numbers 
% having empty tags:
\robust@def*\doemptytag#1{%
  \typeout{Parsing input: empty tag for number '\cpttrimspace{#1}'}%
}
\robust@def*\DefineLineParser#1#2{%
  \ifescapedTF{#2}{}{\cpt@notescapederr{#2}}%
  \csn@def{\string#2@aux@a}##1#1##2#1##3\@nil{%
    \iflacus##2\dolacus
      \iflacus##1\dolacus\else
        \doemptytag{##1}%
      \fi
    \else
      \ifcpt@st
        \ifcsndefinable{\cpttrimspace{##2}}\relax
      \fi
      \csn@edef{\cpttrimspace{##2}}{\cpttrimspace{##1}}%
    \fi
  }%
  \robust@def*#2{\aftercsname\cpt@testst{\string#2@aux@b}}%
  \csn@def{\string#2@aux@b}##1{%
    \defpass\reserved@a####1.####2.####3\@nil{%
      \edef\reserved@a{\ifblankTF{####2}{tex}{####2}}%
      \openin\@inputcheck=####1.\reserved@a\space
      \ifeof\@inputcheck
        \@latex@error{File '####1.\reserved@a' doesn't exist}\@ehd
      \fi
    }%
    ##1..\@nil
    \def\par@tmp{\par}%
    \cptloop\ifeof\@inputcheck\else
      \read\@inputcheck to\reserved@a
      \ifx\reserved@a\par@tmp\else
        \aftercsname\expandafter{\string#2@aux@a}\reserved@a#1#1\@nil
      \fi
    \cptrepeat
    \closein\@inputcheck
    \undefcs\par@tmp
  }%
}
\makeatother

\DefineLineParser{"}\parseinput
\parseinput{myinput1.txt}

% The next call is starred and will raise errors for existing names:
% \parseinput*{myinput1.txt}

% The default file extension (tex) is assumed by the next example:
\DefineLineParser'\parseinputb
\parseinputb{myinput2}

\begin{document}
\let\use\usename
\use{wingTaperRatio1}, \use{wingSpanMT1}, \use{wingAreaMTSquared1}.
\par\medskip
\use{wingTaperRatio2}, \use{wingSpanMT2}, \use{wingAreaMTSquared2}.
\end{document}
share|improve this answer
    
+1 Does this solution handle the case <more spaces><number><more spaces>"<name>" ? –  agodemar Mar 11 '12 at 8:42
    
@agodemar: Yes, it works as well in that case. In <spaces><number><spaces><name> the first <spaces> is skipped by TeX and the second one is trimmed by the above code. –  Ahmed Musa Mar 12 '12 at 5:01
    
as I said in the edit to my question, I may want to admit repetitions in the macro names. –  agodemar Mar 13 '12 at 7:57
    
@agodemar: I have edited the answer to attend to your latest comment. –  Ahmed Musa Mar 13 '12 at 18:45
    
This is a very flexible solution. One minor inconvenience is that a code like \use{wingTaperRatio} requires more typing as compared to \wingTaperRatio. But, on the good side, it looks like your approach allows for variable names with digits. Am I correct? –  agodemar Mar 13 '12 at 22:20
show 1 more comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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