5

将文本文件读入行数组的正确方法是什么?我在罗塞塔石碑上发现了以下内容:

string[] readLines(string filename) {
  auto f = File(filename);
  scope(exit) f.close();
  string[] lines;

  foreach (str; f.byLine) {
    lines ~= str.idup;
  }

  return lines;
}

但它看起来像每行调整一个数组大小,这是非常低效的。我可以通过标准的加倍方法跟踪读取的行数并调整数组的大小

  int i = 0;
  foreach (str; f.byLine) {
    if (lines.length <= i + 1) {
      lines.length = lines.length * 2 + 1;
    }
    lines[i] = str.idup;
    i++;
  }
  lines.length = i;

但这已经足够样板代码了,我不得不怀疑我是否只是忽略了标准库中已经为我做这件事的东西。


编辑:给 fwend 的评论更多的可见性:这篇文章详细描述了数组分配器是如何工作的,以及为什么运行时可以有效地处理追加

4

3 回答 3

4

实际上,只要空间不足,D 就会将数组的保留空间加倍,因此您无需手动操作。这里有很多关于D的数组的信息

于 2012-04-25T05:51:07.300 回答
4

最初您可能会得到很多重新分配,但随着数组的增长,它的容量应该会增长,这样它就不太可能通过进一步的附加来分配。您可以打印出数组的capacity属性以查看它是如何增长的。

但是,如果您特别担心附加性能,那么您可能应该使用std.array.Appender,在这种情况下,您的代码将如下所示:

string[] readLines(string filename)
{
    auto file = File(filename);
    auto lines = appender!(string[]);

    foreach(line; file.byLine())
        lines.put(to!string(line));

    return lines.data;
}

Appender旨在使附加更有效,并将利用它可以使附加比~=本身更有效的任何技巧。

于 2012-04-25T09:53:41.350 回答
4

也许是这样:

import std.algorithm;
import std.array;
import std.file;

string[] readLines(string input)
{
    Appender!(string[]) result;
    foreach (line; input.splitter("\n"))
        result.put(line);
    return result.data;
}

void main()
{
    string input = cast(string)std.file.read("test.d");
    string[] lines = readLines(input);
}

它应该足够快,因为结果只是创建预加载输入字符串的切片而不分配新数组(除了切片本身的分配,IOW 指针+长度字段)。

于 2012-04-25T21:48:39.023 回答