正如标题所说,我正在寻找一种逐行读取文件的方法(完成!)并将每一行、每个字符串存储在一个数组中。
我在网络和 stackexchange 上搜索了一段时间,但找不到合适的解决方案——事实上,我仍然不知道数组作为数据类型是否存在。
由于缺乏信息,我还没有 MWE。
\newread\file
\openin\file=myfilename.txt
\loop\unless\ifeof\file
\read\file to\fileline % Reads a line of the file into \fileline
\fileline
\repeat
\closein\file
这是我逐行读取文件的构造,似乎工作正常。\fileline
是我想存储在数组中的字符串,或者某种程度上等同于数组。在 Java 中,我会使用 ArrayList;
ArrayList<String> list = new ArrayList<String>();
将是对象,并且
list.add(readline);
将是将参数存储readline
到的语句list
。
好吧,我担心这些解决方案对我来说太复杂了,事实上,我不知道你们所有答案中到底在说什么。我的问题和解决方案似乎太难了。
我想读取一个每行只包含一个字符的文件,然后将其存储在一个数组或任何类型的列表中,这样我就可以读取该数组myarray[2]
或其他东西来获取列表中的第二个(或第三个,取决于是从 1 还是 0 开始)值并对其进行操作。
我将研究datatool
进一步包装——乍一看,它似乎隐约能做我想要做的事,但我不知道该怎么做。
感谢您所有的回复,很抱歉浪费了您的时间!
问候 hringriin
编辑:
好吧,我担心这些解决方案对我来说太复杂了,事实上,我不知道你们所有答案中到底在说什么。我的问题和解决方案似乎太难了。
我想读取一个每行只包含一个字符的文件,然后将其存储在数组或任何类型的列表中,这样我就能够使用 myarray[2] 或其他东西读取数组,以获取列表中的第二个(或第三个,取决于是从 1 还是 0 开始)值来处理它。所以我的问题本身是如何在 LaTeX 中存储文件中的数据或字符。问题是我无法从文件中读取一行。
我将进一步检查这个datatool
包裹——乍一看,它似乎能满足我的要求,但我还不知道该怎么做。
答案1
使用 并不难。我定义了一个接受两个参数的expl3
命令\ReadFile
\ReadFile{\myarray}{somefile.dat}
第一个参数是控制序列名称,第二个参数是要读取的文件。这样,文件将被读入,命令\myarray
将被定义,以便
\myarray{2}
产生第二项;特殊调用\myarray{*}
将返回项数。您还可以调用\myarray{-1}
来访问最后的物品。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\ior_new:N \g_hringriin_file_stream
\NewDocumentCommand{\ReadFile}{mm}
{
\hringriin_read_file:nn { #1 } { #2 }
\cs_new:Npn #1 ##1
{
\str_if_eq:nnTF { ##1 } { * }
{ \seq_count:c { g_hringriin_file_ \cs_to_str:N #1 _seq } }
{ \seq_item:cn { g_hringriin_file_ \cs_to_str:N #1 _seq } { ##1 } }
}
}
\cs_new_protected:Nn \hringriin_read_file:nn
{
\ior_open:Nn \g_hringriin_file_stream { #2 }
\seq_gclear_new:c { g_hringriin_file_ \cs_to_str:N #1 _seq }
\ior_map_inline:Nn \g_hringriin_file_stream
{
\seq_gput_right:cx
{ g_hringriin_file_ \cs_to_str:N #1 _seq }
{ \tl_trim_spaces:n { ##1 } }
}
\ior_close:N \g_hringriin_file_stream
}
\ExplSyntaxOff
\begin{document}
\ReadFile{\myarray}{somearray.dat}
\myarray{*}
\myarray{1}
\myarray{2}
\myarray{3}
\myarray{-1}
\myarray{-2}
\myarray{-3}
\end{document}
如果文件somearray.dat
是
And now for something completely different
1 2 3 4 5 6 7 8
a bc def ghij
(我使用了与 Christian 相同的方法),结果将是
答案2
Latex 没有任何特别好的数组数据类型——特别是在普通语言中,你会期望有一些常数时间操作,一些线性时间。在 Latex 中,在开头或结尾插入和连接可能是常数时间,但所有其他操作都是通过在列表上映射一些函数来完成的,因此是线性时间。
标准 LaTeX 数组看起来像\def\myarray{\\{entry1}\\{entry2}\\{entry3}...}.
这样,适合连接和映射。您可以通过以下方式将另一个函数映射到它上面:\def\\#1{do something with #1} \myarray.
如果你想存储这样的行,你会说:
\newtoks\temptoksi
\newtoks\temptoksii
\def\myarray{}
\newread\file
\openin\file=myfilename.txt
\loop\unless\ifeof\file
\read\file to\fileline % Reads a line of the file into \fileline
\temptoksi\expandafter{\myarray}
\temptoksii\expandafter{\fileline}
\edef\myarray{\the\temptoksi\\{\the\temptoksii}}
\repeat
\closein\file
其工作原理是 \edef 递归扩展其定义主体,然后将 \myarray 设置为等于结果。标记列表有点像没有参数的宏,只不过您通过说 \the\tokenlist 来调用它,并且标记列表的内容不会在 \edef 中递归扩展。因此,我在循环主体中添加的三行表示“设置为\temptoksi
等于 的内容\myarray
,设置\temptoksii
为 的内容\fileline
,然后设置\myarray
为"old contents of myarray" \\{"contents of fileline"}
。当然,为了便于阅读,您可以进行单独的定义:
\def\addtomacro#1#2{
\temptoksi\expandafter{#1}\temptoksii\expandafter{#2}
\edef#1{\the\temptoksi\\{\the\temptoksii}
}
然后您只需说 \addtomacro\myarray\fileline。
编辑:实际上有一种方法可以实现更好的数组,但它更技术性。基本上,这个想法是将数组的每个元素存储在不同的控制序列中,因此第一个元素将存储在名为“myarray1”的控制序列中,第二个元素存储在“myarray2”中,依此类推。这本质上是一个哈希数组,因为 latex 在内部哈希表中查找控制序列的定义。要使用这种数组的极简版本,您可以这样说:
\def\setarrayelt#1#2{\expandafter\xdef\csname array@#1@#2\endcsname}
\def\getarrayelt#1#2{\csname array@#1@#2\endcsname}
然后你可以说:
\newcount\arraylength
\arraylen=0
\newread\file
\openin\file=myfilename.txt
\loop\unless\ifeof\file
\read\file to\fileline % Reads a line of the file into \fileline
\setarrayelt{myarray}{\the\mycount}{\fileline}
\advance\arraylength1
\repeat
\closein\file
当然,您可以做更复杂的事情,例如存储长度\array@myarray@length
,然后\getarrayelt
如果给它的参数太大(或不是数字),则抛出越界错误。然后您可以添加宏,例如\addelttoendofarray,
等。
答案3
也许这个readarray
包可以有所帮助。已编辑以使用来自2016-11-07
包更新的较新的首选语法。
\documentclass{article}
\usepackage{readarray}[2016-11-07]
\usepackage{filecontents}
\begin{filecontents*}{mydata.dat}
First record
2nd record
3rd record
...
last record (before blank record at end)
\end{filecontents*}
\begin{document}
\readrecordarray{mydata.dat}\Mydata
There are \MydataROWS{} rows of data\par
The 5th record is: \Mydata[5]\par
The 3rd record is: \Mydata[3]
\end{document}
答案4
根据 OP,这里所有的解决方案都是too much
- 好吧,我保留它。
这是一种expl3
将字符串读入seq
变量的方法,可以将其视为array
。
如何处理这些字符串的细节取决于O.P.
这是文件somearray.dat
And now for something completely different
1 2 3 4 5 6 7 8
a bc def ghij
代码
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\ior_new:N \l_input_stream
\seq_new:N \g_all_lines_seq
\bool_new:N \g_not_eof_bool
\ior_open:Nn \l_input_stream {somearray.dat} %Open the file
\newcommand{\ReadOneLine}{%
\ior_get_str:NN \l_input_stream \l_tmpa_tl % Read one Line
\seq_gput_right:NV \g_all_lines_seq {\l_tmpa_tl} % Put it to next array cell
}
\newcounter{foo}
\newcommand{\ReadAll}{%
\bool_gset_true:N \g_not_eof_bool
\bool_while_do:nn {\g_not_eof_bool} {%
% can be replaced by \ior_if_eof:NTF most likely
\if_eof:w \l_input_stream
\bool_gset_false:N \g_not_eof_bool % Set the end of reading
\else:
\ReadOneLine
\fi:
} % End of loop
}
\newcommand{\ShowArrayLine}[1]{%
\seq_item:Nn \g_all_lines_seq {#1}
}
\newcommand{\CloseFile}{%
\ior_close:N \l_input_stream
}
\ExplSyntaxOff
\begin{document}
\ReadAll
\CloseFile
\ShowArrayLine{1}
\ShowArrayLine{3}
\ShowArrayLine{3}
\ShowArrayLine{2}
\end{document}