C# - 定期读取文件最后一部分的最佳方法

8
我希望定期读取一个同时正在写入的日志文件。程序将定期读取日志文件内容并解析以提取一些值。但我不想每次都读取整个文件。
有没有办法从特定行开始读取文件?
例如,在第一次读取时,文件有100行。我记录此值,并在下一次读取时从第100行开始读取并存储当前文件的行号。
有没有有效的方法来做到这一点?日志文件将增长到约100MB,我需要每5秒读取一次。因此,每次完整读取整个文件可能不太有效。
非常感谢任何建议。

3
你可以看一下Tail.NET http://www.codeproject.com/Articles/7568/Tail-NET。 - itsmatt
你可以将文件分成多个文件或一个专为此目的而设计的数据库。虽然,我不确定对于只有100MB的文件会提供多少好处。流往往具有起始索引。为什么不直接使用它呢? - P.Brian.Mackey
4个回答

6
我想这就是你需要的内容,其中offset表示你想要回溯多少。参考资料:MSDN
using (FileStream fs = new FileStream(filepath, FileMode.Open, FileAccess.Read))
{
    fs.Seek(offset, SeekOrigin.End);
}

现在,文件流指向您设置“offset”的文件中的位置,并且您可以从那里读取。

哈哈,我慢了两分钟 ;) - J...
@bland 谢谢。我会研究一下这种方法。看起来这对我可能有用。 - madu

3

Seek能够很好地完成这项任务。但我希望提供其他前进的方式。

    public static void Read()
    {
        var fs = new FileStream(@"G:\test.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
        int lastReadCount = 0;
        while (true)
        {
            var totalCountOfFile = fs.Length;
            if (lastReadCount < totalCountOfFile)
            {
                var buffer = new byte[1024];
                int count = fs.Read(buffer, 0, buffer.Length);
                lastReadCount += count;
                Display(buffer);
            }
            Thread.Sleep(5000);
        }
    }

    private static void Display(byte[] buffer)
    {
        var text = Encoding.UTF8.GetString(buffer.Where(p=>p != 0).ToArray());
        Console.Write(text);
    }

3
如果日志只是被追加,你可以尝试以无锁的只读模式打开文件。这样,在你读取文件时,其他进程仍然可以向它写入内容。
var fs = new FileStream(path,FileMode.Open,FileAccess.Read, FileShare.ReadWrite);

如果您记得文件的长度,您可以在下一次读取时使用“Seek”到该位置。 - JosephHirn

2

对于一些快速而简单的操作,我使用以下方法。在这种情况下,它是用于日志转储 - 我并不关心我获取了多少行,我只想要最后的一堆(numBytes):

cmdLogReader = new System.IO.StreamReader(cmdLogFileIn);

if (cmdLogReader.BaseStream.Length < (numBytes - 1)) {
    return cmdLogReader.ReadToEnd;
} else {
    cmdLogReader.BaseStream.Seek(-numBytes, System.IO.SeekOrigin.End);
    cmdLogReader.ReadLine();
    return cmdLogReader.ReadToEnd;         
} 

你可以在开始时保存BaseStream.Length,并使用它来计算下一次要回溯多远(例如:numBytes 变成 BaseStream.Length - previousBaseStreamLength或其他),这样连续调用就能获取自上次读取以来添加的所有内容。如果这样做,您可能需要跳过ReadLine调用,因为它只是在随机回溯之后移动到最近的一行。如果你知道你要落在一行边界上,那么你可以直接用ReadToEnd。这是一个有点粗糙的实现,但非常快,这就是我使用它的原因。

谢谢。我不知道BaseStream.Length属性,也不知道使用这种方法来读取文件。我会研究一下这个方法。 - madu

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