如何 \read 一个命令并返回结果?

如何 \read 一个命令并返回结果?

作为如何将文件内容用作长度的数值?,我正在尝试这样做(我确实需要读取data.txt这个新命令里面的文件并在稍后调用该命令时使用该值):

\documentclass{article}
\usepackage{calc}
\begin{document}
\newcommand\x{
  \newread\foo
  \openin\foo=data.txt
  \read\foo to \temp
  \temp
}
\setlength\parskip{1pt * \x}
\end{document}

我越来越:

! Missing number, treated as zero.
<to be read again>
                   \global
l.35 \setlength\parskip{1pt * \x}

怎么了?

答案1

您可以这样做,但需要 LuaTeX。您可以使用 \beginlocalcontrol/\endlocalcontrol 定义一个代码块,该代码块在 TeX 的扩展阶段完全执行(包括打开文件):

\documentclass{article}
\usepackage{calc}

% First define \beginlocalcontrol based on tex.runtoks
\newluafunction\beginlocalcontrol
\directlua{lua.get_functions_table()[\the\allocationnumber] = function() return tex.runtoks(token.get_next) end}
\luadef\beginlocalcontrol\allocationnumber
\begin{document}
\newcommand\x{%
  \beginlocalcontrol % This block gets executed during expansion
    \begingroup % Let's keep our definitions local to avoid confusion macros which might not expect definitions to change during expansion
    \newread\foo
    \openin\foo=data.txt
    \read\foo to \temp
    \closein\foo
    \expandafter\endgroup % The two \expandafter's ensure that the final \temp gets the value from inside of the \endgroup and \endlocalcontrol blocks.
  \expandafter\endlocalcontrol
  \temp
}
\setlength\parskip{1pt * \x}
\end{document}

我建议至少移动\newread \foo命令的外部,否则每次扩展宏时都会浪费一个输入文件。

答案2

其实这个问题恰好是可以解决的:

\documentclass{article}
\begin{document}

\makeatletter

\setlength\parskip{\@@input{b.tex}pt}
\typeout{\the\parskip}

\setlength\parskip{\dimexpr 1pt*\@@input{b.tex}\relax}
\typeout{\the\parskip}

\makeatother

\end{document}

假设b.tex包含123,则两者都会打印出来123.0pt

答案3

\read不可扩展,但本身就是一个赋值命令。
因此,它不能在执行另一个赋值命令(如)的过程中执行\setlength
当您打算定义临时宏时,\x您可以这样做:

\begin{filecontents*}{mydata.txt}
8
\end{filecontents*}

\documentclass{article}
\usepackage{calc}
\newread\foo

\begin{document}
\immediate\openin\foo=mydata.txt
\immediate\read\foo to \x
\immediate\closein\foo
%\setlength\parskip{1pt * \x}
\setlength\parskip{\x pt}
\showthe\parskip
\end{document}

\@@input是 LaTeX 2ε 的可扩展 TeX 原语的副本\input。/
\input无法\@@input在直接获取一组标记以形成宏参数的过程中执行,因为无论如何都会遇到文件末尾,而该文件末尾将被视为标记\outer
但您可以安排事物,使其不被处理为宏参数:

\begin{filecontents*}{mydata.txt}
8
\end{filecontents*}

\documentclass{article}
\begin{document}
\parskip=\csname @@input\endcsname mydata.txt pt %
%\parskip=\dimexpr1pt*(\csname @@input\endcsname mydata.txt )\relax
\showthe\parskip
\end{document}

答案4

既然你要完成一项作业,那么可扩展性就不是一个因素。

最简单的方法是定义一个合适的命令,比如

\setlengthfromfile{<length>}{<file>}{<expression>}

<length>您要设置的寄存器在哪里,<file>您要从中读取值的文件在哪里,并<expression>包含#1表示文件内容的文件,因此您的情况是

\setlengthfromfile{\parskip}{data.txt}{1pt * #1}

带有示例的代码:

\begin{filecontents*}{\jobname.dat}
20
\end{filecontents*}

\documentclass{article}

\ExplSyntaxOn

\NewDocumentCommand{\setlengthfromfile}{mmm}
 {% #1 = length parameter
  % #2 = file name
  % #3 = expression
  \yegor_setlengthfromfile:nnn { #1 } { #2 } { #3 }
 }

\cs_new:Nn \__yegor_setlengthfromfile_aux:n { } % initialize
\cs_generate_variant:Nn \__yegor_setlengthfromfile_aux:n { V }

\tl_new:N \l__yegor_setlengthfromfile_tl

\cs_new_protected:Nn \yegor_setlengthfromfile:nnn
 {
  \file_get:nnN { #2 } { } \l__yegor_setlengthfromfile_tl
  \cs_set:Nn \__yegor_setlengthfromfile_aux:n { #3 }
  \skip_set:Nn #1 { \__yegor_setlengthfromfile_aux:V \l__yegor_setlengthfromfile_tl }
 }

\ExplSyntaxOff

\setlengthfromfile{\parskip}{\jobname.dat}{1pt * #1}
\showthe\parskip

\setlengthfromfile{\parskip}{\jobname.dat}{1pt * #1-12pt}
\showthe\parskip

\setlengthfromfile{\parskip}{\jobname.dat}{#1\parskip}
\showthe\parskip

\setlengthfromfile{\parskip}{\jobname.dat}{#1pt plus 0.1pt}
\showthe\parskip

\stop

控制台将显示

(./yegordim.dat
)
> 20.0pt.
l.31 \showthe\parskip

?
(./yegordim.dat)
> 8.0pt.
l.34 \showthe\parskip

?
(./yegordim.dat)
> 160.0pt.
l.37 \showthe\parskip

?
(./yegordim.dat)
> 20.0pt plus 0.1pt.
l.40 \showthe\parskip

在第三个例子中,我们重用了以前的值\parskip

相关内容