附加和阅读文本文件

时间:2012-09-06 03:30:07

标签: c#

环境:欢迎任何.Net Framework。 我有一个日志文件,可以全天候写入。

我正在尝试创建一个将读取日志文件并处理数据的应用程序。

有效读取日志文件的最佳方法是什么?我想用FileSystemWatcher之类的东西监视文件。但是,如果我的应用程序处理完数据后,如何确保不读取相同的数据?或者说应用程序因某些未知原因而中止,它会如何从最后一次停止的地方开始?

日志文件中的有效负载周围通常有页眉和页脚。也许是内容中的id字段。虽然关于id字段在那里还不确定。

我还想象也许可以将行读取计数保存在某个地方,也许可以将其用作书签。

4 个答案:

答案 0 :(得分:1)

嗯,你必须自己为自己的特定情况找出自己的魔力。如果你打算使用众所周知的文本编码,那可能是非常简单的问题。查看System.IO.StreamReader,它是ReadLine(),DiscardBufferedData()方法和BaseStream属性。您应该能够记住文件中的最后位置,然后再回到该位置并再次开始阅读,因为您确定只附加了文件。还有其他事情需要考虑,而且没有一个通用的答案。

就像一个天真的例子(你可能仍需要调整很多才能使它工作):

    static void Main(string[] args)
    {
        string filePath = @"c:\log.txt";
        using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            using (var streamReader = new StreamReader(stream,Encoding.Unicode))
            {
                long pos = 0;
                if (File.Exists(@"c:\log.txt.lastposition"))
                {
                    string strPos = File.ReadAllText(@"c:\log.txt.lastposition");
                    pos = Convert.ToInt64(strPos);
                }
                streamReader.BaseStream.Seek(pos, SeekOrigin.Begin); // rewind to last set position.
                streamReader.DiscardBufferedData(); // clearing buffer
                for(;;)
                {
                    string line = streamReader.ReadLine();
                    if( line==null) break;

                    ProcessLine(line);
                }
                // pretty sure when everything is read position is at the end of file.
                File.WriteAllText(@"c:\log.txt.lastposition",streamReader.BaseStream.Position.ToString());
            }
        }
    }

答案 1 :(得分:1)

由于显而易见的原因,读取文件的整个内容,以及从日志文件中删除行(在将它们加载到应用程序之后)是不可能的。

我能想到的部分解决方案是拥有一个小型数据库(可能比完整的MySQL / MS SQL / PostgreSQL实例小得多)并使用从日志文件中读取的内容填充表。我很确定即使断电然后机器再次启动,大多数关系数据库也应该能够轻松恢复它的状态。此解决方案需要一些可用于标识日志文件中的行的数据(例如:记录操作的确切时间,执行操作的计算机等)。

答案 2 :(得分:1)

我认为你会发现与LINQ结合使用的File.ReadLines(filename)函数对于这样的事情会非常方便。 ReadAllLines()将整个文本文件作为string []数组加载到内存中,但ReadLines允许您在遍历文件时立即开始枚举行。这不仅可以节省您的时间,而且可以将内存使用率保持在非常低的水平,因为它一次处理每一行。使用语句非常重要,因为如果此程序被中断,它将关闭文件流以清除写入程序并将未写入的内容保存到文件中。然后当它启动时,它将跳过已经读取的所有文件。

int readCount = File.ReadLines("readLogs.txt").Count();
using (FileStream readLogs = new FileStream("readLogs.txt", FileMode.Append))
using (StreamWriter writer = new StreamWriter(readLogs))
{
     IEnumerable<string> lines = File.ReadLines(bigLogFile.txt).Skip(readCount);
     foreach (string line in lines)
     {
         // do something with line or batch them if you need more than one
         writer.WriteLine(line);
     }
}

正如MaciekTalaska所提到的,我强烈建议使用数据库,如果这是24/7写的东西并且会变得非常大。文件系统根本无法处理这样的卷,您将花费大量时间尝试创建数据库可以轻松实现的解决方案。

答案 3 :(得分:1)

是否有原因记录到文件?文件很棒,因为它们易于使用,并且作为最低的共同点,相对较少可能出错。但是,文件有限。正如您所说,当您阅读文件时,无法保证对文件的写入完成。写入日志的多个应用程序可能会相互干扰。没有简单的排序或过滤机制。日志文件可以非常快速地增长,并且没有简单的方法可以将旧事件(例如超过24小时的事件)移动到单独的文件中进行备份和保留。

相反,我会考虑将日志写入数据库。表结构可以非常简单,但您可以获得事务的优势(这样您可以轻松地提取或备份)并使用几乎普遍理解的语法进行搜索,排序和过滤。如果您担心负载高峰,请使用消息队列,例如SQL Server的http://msdn.microsoft.com/en-us/library/ms190495.aspx

为了简化转换,请考虑使用log4net之类的日志记录框架。它将大部分内容从代码中抽象出来。

另一种方法是使用syslog之类的系统,或者如果您有多台服务器和大量日志,则使用flume。通过将日志文件移离源计算机,您可以更有效地存储它们或在不同的计算机上检查它们。但是,对于您当前的问题,这些可能是过度的。