如何逐行读取文件并将每行存储到数组中?

如何逐行读取文件并将每行存储到数组中?

正如标题所说,我正在寻找一种逐行读取文件的方法(完成!)并将每一行、每个字符串存储在一个数组中。

我在网络和 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}

在此处输入图片描述

相关内容