5

テキストファイルを行の配列に読み込む適切な方法は何ですか? ロゼッタ ストーンで次の情報を見つけました。

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

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

  return lines;
}

しかし、行ごとに 1 つの配列のサイズ変更を行っているように見えますが、これはかなり非効率的です。読み込まれた行数を追跡し、標準の 2 倍の方法で配列のサイズを変更することができました

  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

最初は多くの再割り当てを取得する可能性がありますが、アレイが大きくなるにつれて、その容量が大きくなり、さらに追加して割り当てる可能性が低くなるはずです。配列の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 に答える
4

実際、D は配列のスペースがなくなるたびにその予約スペースを 2 倍にするので、手動で行う必要はありません。ここには D の配列に関する多くの情報があります。

于 2012-04-25T05:51:07.300 に答える