从文件末尾开始读取,类似于tail命令。

40

在C#中,如何从文件末尾读取内容?

这很重要,因为我需要读取日志文件,而读取最后3行不必读取10k行。


1
10KB并不算太多 :) - Hamish Grubijan
3
值得注意的是,如果你需要在Windows上使用实用工具Baretail,它的效果非常好。 - cfeduke
@cfeduke 需要对应用程序中的最后一行使用正则表达式。不过还是谢谢你。 - C. Ross
4个回答

56

读取最后的1024个字节:

using (var reader = new StreamReader("foo.txt"))
{
    if (reader.BaseStream.Length > 1024)
    {
        reader.BaseStream.Seek(-1024, SeekOrigin.End);
    }
    string line;
    while ((line = reader.ReadLine()) != null)
    {
        Console.WriteLine(line);
    }
}

3
这将适用于可以使用多个字节来编码字符的字符集(如UTF-8)吗?看起来你可能会从多字节序列的中间开始。 - Eric J.
你需要测试一下我接下来要说的话,但是如果你知道字符串的字符大小(UTF8为2个字节),并且使用可被该数字整除的寻址偏移量,那么这应该可以防止你读取到字符中间的任何内容。我从未见过流返回除文件内容以外的任何东西(即你永远不会得到文件结束标记或其他不属于文件的字符)。 - Andrew Bonsall
这并没有完全读取最后一行,而是读取了若干行之前的内容。 - Bassie
1
@AndrewBonsall UTF8 不是每个字符都是2个字节,而是每个字符可变的1-4个字节。UTF16 每个字符固定为2个字节。 - TessellatingHeckler

11
也许像这样做可以解决你的问题:
using (var fs = File.OpenRead(filePath))
{
    fs.Seek(0, SeekOrigin.End);

    int newLines = 0;
    while (newLines < 3)
    {
        fs.Seek(-1, SeekOrigin.Current);
        newLines += fs.ReadByte() == 13 ? 1 : 0; // look for \r
        fs.Seek(-1, SeekOrigin.Current);
    }

    byte[] data = new byte[fs.Length - fs.Position];
    fs.Read(data, 0, data.Length);
}

请注意,这假定为 \r\n


1
如果对此问题存在疑问,请参考以下链接:https://dev59.com/_FLTa4cB1Zd3GeqPanEC - VVS
+1 展示如何扫描行。附注:逐字节检查“13”假定为ASCII / UTF8编码。如果您不知道文件的编码,则可能无法正常工作。 - Alexei Levenkov

5
下面的代码使用随机访问的 FileStream 在接近文件结尾的偏移量处为 StreamReader 提供种子,丢弃第一个读取的行,因为它很可能只是部分内容。
FileStream stream = new FileStream(@"c:\temp\build.txt", 
    FileMode.Open, FileAccess.Read);

stream.Seek(-1024, SeekOrigin.End);     // rewind enough for > 1 line

StreamReader reader = new StreamReader(stream);
reader.ReadLine();      // discard partial line

while (!reader.EndOfStream)
{
    string nextLine = reader.ReadLine();
    Console.WriteLine(nextLine);
}

1

查看这个相关问题的答案,以反向读取文本文件。由于编码等原因,正确地反向读取文件非常复杂。


1
只有考虑到编码的答案才是完整的,这是一个非常重要的细节。 - raider33

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接