有没有类似 C 的 scanf 的函数可以同时解析 stdin 或 txt 并将它们分配给变量?例如,我正在尝试从文本文件排版 pdf。第一行包含我应该排版的行数,其余包含三个数字,形成一个加法问题。
$ cat input.txt
5
5 2 7
4 7 11
8 0 8
7 5 12
9 7 16
在 C 中可以执行以下操作:
$ cat program.c
#include <stdio.h>
int main() {
FILE* input = fopen("input.txt", "r");
int n, a, b, c, i;
fscanf(input, "%d", &n);
for (i = 1; i <= n; i++) {
fscanf(input, "%d %d %d", &a, &b, &c);
printf("%d + %d = %d\n", a, b, c);
}
return 0;
}
$ gcc program.c -o program.o
$ program.o
5 + 2 = 7
4 + 7 = 11
8 + 0 = 8
7 + 5 = 12
9 + 7 = 16
我接近一个工作示例,只需要找到正确的命令。
$ cat program.tex
\documentclass{article}
\usepackage{pgffor}
\begin{document}
\newread\input
\openin\input=input.txt
% Read first line and assign to n
\foreach \i in {1,...,n} {%
% Read line and assign to a, b, c
$\a + \b = \c$\\%
}%
\closein\input
\end{document}
答案1
我添加了一个计算来检查您在文件中输入的答案。
\documentclass{article}
\newread\x
\openin\x=input.txt
\begin{document}
\read\x to \varn
\def\do#1 #2 #3 #4\relax{%
\par
$#1 + #2 = #3\quad \mbox{check: } \the\numexpr#1+#2\relax$%
}
\loop
\read\x to\varline
\expandafter\do\varline\relax
\edef\varn{\the\numexpr\varn - 1}
\ifnum\varn>0
\repeat
\end{document}
或者作为 L3
\documentclass{article}
\ExplSyntaxOn
\ior_new:N\l_input
\ior_open:Nn\l_input{input.txt}
\ExplSyntaxOff
\begin{document}
\ExplSyntaxOn
\ior_get:NN\l_input\l_n
\cs_new:Npn\l_format:w #1 ~ #2 ~ #3 ~ #4\q_stop{
\par
$#1 + #2 = #3\quad \mbox{check: ~ } \int_eval:n{#1+#2}$
}
\bool_do_until:nn{ \int_compare_p:nNn \l_n = 0 }
{
\ExplSyntaxOff
\ior_get:NN\l_input\l_line
\ExplSyntaxOn
\exp_after:wN\l_format:w\l_line\q_stop
\cs_set:Npx\l_n{\int_eval:n{\l_n - 1}}
}
\end{document}
答案2
我会用expl3
。
第一行存储用于决定要执行的操作的数量(它最多必须等于以下行的数量)。
在本地定义的函数的帮助下,其他行被映射,您可以为其指定参数的格式以及如何处理它们。
\begin{filecontents*}{\jobname.txt}
5
5 2 7
4 7 11
8 0 8
7 5 12
9 7 16
4 4 8
\end{filecontents*}
\begin{filecontents*}{\jobname-1.txt}
3
5 2 10
4 7 28
8 0 0
\end{filecontents*}
\documentclass{article}
\ExplSyntaxOn
\NewDocumentCommand{\operationsfromfile}{mm+m}
{% #1 = file name, #2 = args, #3 = template
\qem_opfromfile:nnn { #1 } { #2 } { #3 }
}
\ior_new:N \g_qem_opfromfile_ior
\tl_new:N \l__qem_opfromfile_number_tl
\int_new:N \l__qem_opfromfile_current_int
\cs_new_protected:Nn \qem_opfromfile:nnn
{
\cs_set:Npn \__qem_temp:w #2 \q_stop { #3 }
\int_zero:N \l__qem_opfromfile_current_int
\ior_open:Nn \g_qem_opfromfile_ior { #1 }
\ior_str_get:NN \g_qem_opfromfile_ior \l__qem_opfromfile_number_tl
\ior_str_map_inline:Nn \g_qem_opfromfile_ior
{
\int_incr:N \l__qem_opfromfile_current_int
\int_compare:nF { \l__qem_opfromfile_current_int > \l__qem_opfromfile_number_tl }
{
\__qem_temp:w ##1 \q_stop
}
}
\ior_close:N \g_qem_opfromfile_ior
}
\ExplSyntaxOff
\begin{document}
\operationsfromfile{\jobname.txt}{#1 #2 #3}{$#1+#2=#3$\par}
\operationsfromfile{\jobname-1.txt}{#1 #2 #3}{$#1\cdot#2=#3$\par}
\end{document}
答案3
您也可以使用\input
primitive 代替\read
primitive。这意味着您的数据在主输入流中进行扫描。
\def\scanf#1 {\par \def\num{#1}\ifnum\num>0 \expandafter\scanfA\fi}
\def\scanfA#1 #2 #3 {$#1+#2=#3$\par
\edef\num{\the\numexpr\num-1}
\ifnum\num>0 \expandafter\scanfA\fi
}
\expandafter\scanf\input input.txt
答案4
如果您喜欢“老派风格”,您可以实现一个基于\immediate\read
和\ifeof
和递减的循环。
但是 TeXbook 在第 20 章:定义(也称为宏)中说:
要从打开的文件中获取输入,您可以说,
\read⟨number⟩to⟨control sequence⟩
并且控制序列被定义为无参数宏,其替换文本是从指定文件读取的下一行的内容。根据当前类别代码,使用第 8 章的过程将此行转换为标记列表。
如果需要,将读取其他行,直到找到相等数量的左括号和右括号。一个空行被隐式地添加到正在被复制的文件的末尾。\read
。
也就是说,通过TeX 打开\openin
并读取现有文件时\read
,总是假装在该文件后附加了一个空行。
因此,如果文件存在,则\ifeof
只有在执行一些操作之后才为真\read
,从而还读取了附加的额外空行。
因此我建议多写一些空行;如果一行是 TeX 注释的话,那么它就等同于空行。
在本例中,如果读取和处理的条目数最多与数据文件第一行中所示的条目数相同,则读取数据行结束。如果
文件没有那么多条目,则在到达数据文件末尾时读取/处理数据行结束。
\begin{filecontents*}{equationdata.txt}
% Amount of entries to process:
5
% Operand1 Operand2 Result Operator
% Operator: 0=+, 1=-, 2=*, 3=/
12 34 46 0
90 5 18 3
117 38 79 1
23 4 19 2
6 7 42 2
22 5 110 2
85 17 5 3
\end{filecontents*}
% There is an incorrectness: The line "23 4 19 2" should be "23 4 19 1".
% This is on purpose so you can see that the \ifnum-check with the result calculated
% via \numexpr detects the difference.
\documentclass{article}
\newcommand\FirstOfTwo[2]{#1}
\newcommand\SecondOfTwo[2]{#2}
\newcommand\Exchange[2]{#2#1}
\newread\dataread
\newcommand*\dataline{}%
\newcommand*\scanf[3]{%
% #1 - file
% #2 - routine for splitting a line of equation-data into its components - assumes a space token is appended at
% the end of the line; the loop does append a space token for you.
% #3 - separator between calls to routine for splitting a line of equation-data
\immediate\openin\dataread=#1 %
\readdatalinesloop{1}{}{}{\SplitOneDelimitedArgAndDefineScratchmacro{\dataline}}{\dataread}%
\readdatalinesloop{\dataline}{}{#3}{#2}{\dataread}%
\immediate\closein\dataread
}%
\newcommand\readdatalinesloop[5]{%
%-------------------------------------------------------------------------------------------------------------------------
% #1 - amount of entries to read
% #2 - separator between calls to prepend in this iteration
% #3 - separator between calls to routine for splitting a line of data into its components
% #4 - routine for splitting a line of data into its components - assumes a space token is appended at
% the end of the line; the loop does append a space token for you.
% #5 - \read-handle
%-------------------------------------------------------------------------------------------------------------------------
\ifeof#5\expandafter\FirstOfTwo\else\expandafter\SecondOfTwo\fi{}{%
\ifnum\numexpr(#1)\relax<1 \expandafter\FirstOfTwo\else\expandafter\SecondOfTwo\fi{}{%
%--------------------------------------------------------------------------------------------------------------------
% Read the next line of data from the file:
%--------------------------------------------------------------------------------------------------------------------
\begingroup
\endlinechar=-1 %
\immediate\read#5 to \dataline
%--------------------------------------------------------------------------------------------------------------------
% Check if the next line of data from the file is empty:
%--------------------------------------------------------------------------------------------------------------------
\ifcat$\detokenize\expandafter{\dataline}$\expandafter\FirstOfTwo\else\expandafter\SecondOfTwo\fi
{%
\endgroup
\expandafter\readdatalinesloop\expandafter{\the\numexpr(#1)\relax}{#2}%
}{%
\expandafter\Exchange\expandafter{\dataline}{\endgroup#2#4} %
\expandafter\readdatalinesloop\expandafter{\the\numexpr((#1)-1)\relax}{#3}%
}%
{#3}{#4}{#5}%
}%
}%
}%
\csname @ifdefinable\endcsname\SplitOneDelimitedArgAndDefineScratchmacro{%
\def\SplitOneDelimitedArgAndDefineScratchmacro#1#2 {%
% #1 scratch macro to define
% #2 number in 1st line of file
\def#1{#2}%
}%
}%
\csname @ifdefinable\endcsname\SplitFourSpaceDelimitedArgs{%
\def\SplitFourSpaceDelimitedArgs#1 #2 #3 #4 {%
% #1 operand ; #2 operand ; #3 result; #4 number denoting operator
%--------------------------------------------------------------------------------------------------
% Deliver the tokens forming the equation:
%--------------------------------------------------------------------------------------------------
$#1\ifcase\numexpr(#4)\relax+\or-\or\times\or\div\fi#2=#3$%
%--------------------------------------------------------------------------------------------------
% Calculate, using eTeX-extensions' \numexpr - you can comment this out if you don't want it:
%--------------------------------------------------------------------------------------------------
\Exchange({ }\texttt{\string\numexpr}-calculation %
\ifnum\the\numexpr(#1\ifcase\numexpr(#4)\relax+\or-\or*\or/\fi#2)\relax=\numexpr(#3)\relax
also yields%
\else
yields a different number%
\fi
: \the\numexpr(#1\ifcase\numexpr(#4)\relax+\or-\or*\or/\fi#2)\relax)%
%--------------------------------------------------------------------------------------------------
% End of code for calculating using \numexpr.
%--------------------------------------------------------------------------------------------------
}%
}%
\begin{document}
\noindent
\scanf{"equationdata.txt"}{\SplitFourSpaceDelimitedArgs}{\\}
\end{document}