【问题标题】:C# - Most effective way to periodically read last part of a fileC# - 定期读取文件最后一部分的最有效方法
【发布时间】:2013-05-01 13:41:24
【问题描述】:

我想定期读取一个正在写入的日志文件。 该程序将定期读取日志文件内容并对其进行解析以提取一些值。但我不想每次都读取整个文件。

有没有办法从特定行开始读取文件?

例如,在第一次读取时,文件有 100 行。我记下了这个值,下次阅读时,我会从第 100 行开始读取并存储当前文件的行号。

有没有一种有效的方法来做到这一点? 日志文件将增长到大约 100MB,我需要大约每 5 秒读取一次。所以每次都读取完整的文件效率不会那么高。

非常感谢任何建议。

【问题讨论】:

  • 你可以看看 Tail.NET codeproject.com/Articles/7568/Tail-NET
  • 您可以将文件划分为多个文件或用于此目的的数据库。虽然,我不确定只有 100MB 的文件能带来多少好处。流往往具有起始索引。为什么不直接使用呢?

标签: c# readfile


【解决方案1】:

我认为您正在寻找这个,其中偏移量将是您想要回溯的程度。参考:MSDN

using (FileStream fs = new FileStream(filepath, FileMode.Open, FileAccess.Read))
{
    fs.Seek(offset, SeekOrigin.End);
}

现在文件流指向您设置“偏移”的文件中的任何位置,您可以从那里读取。

【讨论】:

  • ...哈哈,我慢了两分钟;)
  • @bland 谢谢。我将研究这种方法。看起来这对我有用。
【解决方案2】:

如果日志只是附加的,你可以尝试在没有锁的情况下以只读模式打开文件。这样,其他进程可以在您读取它时对其进行写入。

var fs = new FileStream(path,FileMode.Open,FileAccess.Read, FileShare.ReadWrite);

【讨论】:

  • 如果你记得文件的长度,你可以在下次读取时Seek到那个位置。
【解决方案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);
    }

【讨论】:

    【解决方案4】:

    对于又快又脏的东西,我用这个。在这种情况下,它是一个日志转储——我并不关心我得到了多少行,我只想要最后一堆(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 属性并使用此方法读取文件。我会看看这个方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-06
    • 1970-01-01
    • 2012-03-02
    • 2017-06-14
    • 2013-05-03
    • 1970-01-01
    • 2013-05-21
    相关资源
    最近更新 更多