C#

【C#】高速でファイルの末尾を読み取りする方法

C#でファイルを読み取りする場合は、StreamReaderクラスFileStreamクラスFileクラスを使います。

このクラスにはファイル内のテキストを読み取りするメソッドが用意されています。

クラスメソッド
StreamReaderReadLine、ReadToEnd
FileStreamRead、ReadByte
FileReadAllLine、ReadAllText

 

通常はファイルの先頭からテキストが読み込まれるので、ファイルの先頭行なら簡単に取得が可能ですね。

ファイルの末尾を読み取りする場合は、いくつか方法があります。実際に処理速度を計測して高速でファイルの末尾を読み取りする方法を検証してみたいと思います。

高速で最後の行を読み取りしたい方は、本記事を最後まで読んでみてください。

測定結果

1行~1,000万行、1列~100列(100,000行×100列)に 1 ~ 100 までの数値データが書き込まれたcsvファイル「Sample.xlsx」を用意します。

csvファイルのサイズは、28,614KBです。

それぞれの読み取りする方法でファイルの読み込みをし、処理時間を測定した結果が次の通りです。

順位末尾を取得する方法処理速度(msec)
ファイルの後ろから取得する方法6.15
ファイルを全て読み取りして取得する方法(ReadAllLine)320.82
ファイルを全て読み取りして取得する方法(ReadLine)352.39

最後の行を取得するなので、全ての行を読み込むよりも後ろからファイルの読み込みをする方法が圧倒的に早いという結論になりました。

やはり全ての行を読み取りするより、ピンポイントで取得しにいく方が高速に処理することができます。

使用している環境によって多少の誤差は生じると思います。ここでの結果は参考値として考えてください。

処理速度の測定

今回測定した処理を以下に記載しています。

ファイルの末尾から読み込む

この方法が高速でファイルの最終行を読み取りすることができる方法です。

ファイルの最後から順番に読んでいき、改行コードが見つかるまで繰り替えします。改行コードが見つかれば、それまで読んだテキストがファイルの最後の行(末尾)にあるテキストということになります。

検証したサンプルコードは次の通りです。

private void ReadByteFile(string fileName)
{
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();

    try
    {
        using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
        {
            var str = "";
            var index = 0;
            var bytes = new List<byte>();
            while (fs.Position >= 0)
            {
                fs.Position = fs.Seek(0, SeekOrigin.End) - index;

                if (fs.CanSeek)
                {
                    int read;
                    while ((read = fs.ReadByte()) >= 0)
                    {
                        bytes.Add((byte)read);
                    }

                    str = Encoding.GetEncoding("Shift_JIS").GetString(bytes.ToArray());
                    if (str.Contains("\r\n")) { break; }
                }
                index++;
                bytes.Clear();
            }

            Debug.WriteLine(str.Trim('\r').Trim('\n'));
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }

    stopwatch.Stop();
    Debug.WriteLine(stopwatch.Elapsed.TotalMilliseconds.ToString("0.00"));
}

簡単にソースコードの解説を行います。

ストリームには「現在の位置」があり、この位置を開始位置としてテキストの読み書きをします。ストリームを読み取りすると読み書きした分だけ自動的に位置が移動します。

Fileクラスには「現在の位置」をメソッドやプロパティで取得または設定することができます。

プロパティ or メソッド説明
Postionプロパティストリームの位置を取得または設定します。
Seek(long offset, SeekOrigin origin)ストリームの「現在の位置」を特定の位置に設定します。

  • SeekOrigin.Begin→先頭
  • SeekOrigin.Current→現在の位置
  • SeekOrigin.End→末尾

 

Seekメソッドの第2引数をSeekOriginの列挙体のうちの1つである「End」を渡します。メソッドの戻り値は最後の位置となる値になるで、Postionプロパティにセットします。

あとは改行コードが見つかるまで、Postionプロパティの値を1つずつ移動させていきます。

上記のソースコードを実行させると次の結果になります。

処理速度 : 6.15(msec)

 

ファイルを全て読み取りして取得する方法(ReadAllLine)

ファイルを操作するための Fileクラス のReadAllLineメソッド を使って、ファイルを開いてテキストを行単位で全て読み取りをします。

取得した行は配列なので、LINQの機能である Lastメソッド を使うことで IEnumerable を継承したシーケンスの最後の要素を取得することが可能です。

検証したサンプルコードは次の通りです。

private void ReadAllLineFile(string fileName)
{
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();

    var text = File.ReadAllLines(fileName, Encoding.GetEncoding("Shift_JIS")).Last();
    Debug.WriteLine(text);

    stopwatch.Stop();
    Debug.WriteLine(stopwatch.Elapsed.TotalMilliseconds.ToString("0.00"));
}

ファイルの後ろから取得する方法と比べて、かなり短くなります。

上記のソースコードを実行させると次の結果になります。

処理速度 : 320.82(msec)

 

ファイルを全て読み取りして取得する方法(ReadLine)

ファイルを読み込むための StreamReaderクラス のReadLineメソッド を使って、ストリームからテキストを1行毎にリストへ追加して全て読み取りをします。

StreamReaderの詳しい使い方はこのサイトを参考にしてみてください。

【C#】ファイルを読み込む方法を紹介(StreamReader)C#にはファイル読み込みする専用クラス「StreamReader」が存在します。このクラスには全て取得するReadToEndメソッドや1行だけ取得するReadLineメソッドがあります。この2つのメソッドの利用方法を紹介していますので、ぜひ最後まで読んでみてください。...

リストの最後の要素を指定すれば、最後の行を取得することができます。

検証したサンプルコードは次の通りです。

private void ReadLineFile(string fileName)
{
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();

    try
    {
        using (var sr = new StreamReader(fileName, Encoding.GetEncoding("Shift_JIS")))
        {
            var texts = new List();
            while (0 <= sr.Peek())
            {
                texts.Add(sr.ReadLine());
            }

            Debug.WriteLine(texts[texts.Count - 1]);
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }

    stopwatch.Stop();
    Debug.WriteLine(stopwatch.Elapsed.TotalMilliseconds.ToString("0.00"));
}

ファイルを全て読み取りして取得する方法(ReadAllLine)と比較して、ストリームの位置をチェックしたり、リストに追加したりしているからか処理速度は遅くなる結果となりました。

上記のソースコードを実行させると次の結果になります。

処理速度 : 352.39(msec)

 

まとめ

この記事ではファイルの最後の行を取得する処理を測定して、高速に読み取りをする方法について紹介しました。

結論としては、先頭から全てのテキストを取得するより、ファイルの後ろからピンポイントで読み取りをした方が遥かに早いということが分かりました。

大容量のファイルから最後の行だけ高速に読み込みたい場合には、参考になると思います。

以上、最後まで読んで頂きありがとうございました。

プログラミングを学習したいなら…

プログラミングスキルを身に付けるなら、プログラミングを効率良く学べる
プログラミングスクール」がオススメです。

特にこんな方にオススメ!!
これからエンジニアを目指したい
プログラミングの専門性を高めたい
プログラミングを学んで副業をしたい
エンジニアに転職して年収をアップさせたい

プログラミングを触ったことがない未経験からでも、プログラミングスクールで学習すれば、エンジニアへ就職・転職することも可能です。

あなたの「行動力」と「やる気」で、あなたの人生を大きく変えるチャンスになることでしょう。

プログラミングスクールに興味がある方は是非チェックしてみてください。

> プログラミングを学ぶ <

COMMENT

メールアドレスが公開されることはありません。

CAPTCHA