LoginSignup
1
1

More than 1 year has passed since last update.

C# テキストファイルを最終行から指定行数だけ読み出す

Last updated at Posted at 2023-01-22

テキストファイルの最終行から指定行数だけ読み出して返します。
バッファやポジションの扱いは苦手で、もう一度書きたくなかったので残します。

  • 本記事のすべての情報は利用者の責任において利用ください。
  • 本記事の情報が正しいこと、誤っていることと実行効率を検証していません。
  • 動作することも、動作しないことも保証しません。

誤りや改善案はご指摘いただければ感謝します。が、修正できない場合があることをご了承ください。

Program.cs
internal class Program
{
    static void Main(string[] _)
    {
        Console.Write(
            ReadTailLines(
                @"C:\temp\新規 テキスト ドキュメント.txt",
                Encoding.UTF8,
                3
                ));
    }

    static string? ReadTailLines(
        string filePath,
        Encoding encoding,
        int lineCount,
        int bufferBytes = 10,
        string delimiter = "\r\n"
        )
    {
        using FileStream stream = new(
            filePath,
            FileMode.Open,
            FileAccess.Read
            );

        return ReadTailLines(stream, encoding, lineCount, bufferBytes, delimiter);
    }

    static string? ReadTailLines(
        FileStream stream,
        Encoding encoding,
        int lineCount,
        int bufferBytes = 10,
        string delimiter = "\r\n"
        )
    {
        if (bufferBytes <= 0) throw new ArgumentException($"bufferBytes <= 0");
        if (string.IsNullOrEmpty(delimiter)) throw new ArgumentException($"empty delimiter");

        Stack<IEnumerable<byte>> stack = new();

        // 読み出すバイト数
        int readBytes = (int)Math.Min(bufferBytes, stream.Length);

        stream.Position = stream.Length - readBytes;

        while (stream.Position >= 0 && readBytes > 0)
        {
            var buffer = new byte[readBytes];

            if (stream.Read(buffer, 0, readBytes) > 0)
            {
                // 読み出したバイト列の評価
                {
                    stack.Push(buffer);

                    var s = encoding.GetString(
                        stack.SelectMany(x => x).ToArray()
                        ).TrimEnd();

                    var lastIndex = s.LastIndexOfTimes(delimiter, lineCount);

                    if (lastIndex > -1)
                    {
                        return s[(lastIndex + delimiter.Length)..];
                    }
                }

                // 次の周回の Position 合わせ
                {
                    int beforeReadPosition = (int)stream.Position - readBytes;

                    readBytes = (beforeReadPosition - bufferBytes >= 0)
                        ? bufferBytes
                        : beforeReadPosition;

                    stream.Position = beforeReadPosition - readBytes;
                }
            }
        }

        return null;
    }

}
StringExtension.cs
static class StringExtension
{
    public static int LastIndexOfTimes(this string self, string search, int times)
    {
        int foundCount = 0;

        int index = self.LastIndexOf(search, self.Length - 1);

        while (index > -1)
        {
            foundCount++;

            if (foundCount >= times)
            {
                return index;
            }
            else
            {
                index = self.LastIndexOf(search, index);
            }
        }

        return -1;
    }
}
新規 テキスト ドキュメント.txt
A,B,C,D,E
F,G,H,I,J,K,L,M,N,O,P,Q,R,

ぎゃはS,T,U,V,W,X,Y,Z
出力
F,G,H,I,J,K,L,M,N,O,P,Q,R,

ぎゃはS,T,U,V,W,X,Y,Z

確認した環境

  • Windows 11 Home 22H2
  • Microsoft Visual Studio Community 2022 (64 ビット) Version 17.4.3
  • .NET Core 6.0

関連記事

1
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1