删除单词周围的一对双引号字符并将其定义为宏

删除单词周围的一对双引号字符并将其定义为宏

我有一个外部程序,它写入如下文本文件:

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

我如何解析所有行并将每个数字分配给给定名称的宏?

这是我所寻找的原型:

\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}

宏如何\parseInput实现?

编辑:这里还有一个需求,即宏名称应该允许重复。这是因为我想准备更多的子文件夹,每个子文件夹都有自己的myinput.txt。每个子文件夹将与书中提出的不同练习相关。练习的数据和结果由外部程序处理并写入文件中。但其中一些可能与相同的物理量相关,并且值可能会因练习而异。所以,我可能需要\parseInput多次使用宏,需要重新定义一些宏。我可能想要有类似的东西:

\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$.

答案1

最好的方法是将软件配置为以以下格式输出数据

\newcommand{\<名称A>}{<编号>}

然后您只需输入数据即可\input{myinput.txt}

但即使数据是现在的,也可以使用各种 TeX 和 LaTeX 结构对其进行解析:

\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}

循环将逐行读取文件。然后,每行都传递给\parseline宏,宏使用分隔参数来提取值。然后\@namedef创建指定名称的宏。请注意,如果输入行与的参数规范不符\parseline,TeX 将引发错误。

答案2

这个 LaTeX3 代码应该可以工作

\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

它与 Andrey Vihrov 的代码非常相似。

\ago_processline:w函数在输入以下行时

0.45 "wingTaperRatio"

将执行相当于

\gdef\wingTaperRatio{0.45}

因此,如果新命令读取另一个文件,则不会出现问题\parseInput:的定义\wingTaperRatio将被默默覆盖。

笔记

不幸的是,expl3 软件包中的一个小错误似乎阻止了使用\ior_open:Nn;LaTeX3 软件包的下一次更新应该可以解决这个问题。目前,我们可以使用\ior_open_unsafe:Nn代替\ior_open:Nn

答案3

为了完整性,这里有一个更通用的解决方案。如果使用已定义解析器的星号 (*) 变体,则无法重新定义现有名称。

\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}

相关内容