读取日志文件并提取包含特定文本的行

Read a Log file and pull out lines with specific text

我有一个不断添加的日志(例如每秒 1-5 行或更多),文件可能会变得相当大 20MB+

示例日志文件:

[Wed Aug 26 00:01:48 2015] You try to kick a snake, but miss!
[Wed Aug 26 00:01:50 2015] A snake hits YOU for 3 points of damage.
[Wed Aug 26 00:01:51 2015] You punch a snake for 6 points of damage.
[Wed Aug 26 00:01:51 2015] A decaying skeleton tries to hit Clumsy, but misses!
[Wed Aug 26 00:01:53 2015] A snake hits YOU for 1 point of damage.
[Wed Aug 26 00:01:53 2015] A decaying skeleton tries to hit Clumsy, but misses!
[Wed Aug 26 00:01:54 2015] You gain experience!!
[Wed Aug 26 00:01:54 2015] You punch a snake for 6 points of damage.
[Wed Aug 26 00:01:54 2015] You have slain a snake!
[Wed Aug 26 00:01:56 2015] --You have looted a Snake Egg.--
[Wed Aug 26 00:01:56 2015] A decaying skeleton tries to hit Clumsy, but misses!
[Wed Aug 26 00:01:57 2015] --You have decided to not loot 2 item(s): Snake Fang. The item(s) will be available to anyone after the corpse(s) unlock.--
[Wed Aug 26 00:01:57 2015] --You have decided to not loot 2 item(s): Snake Scales. The item(s) will be available to anyone after the corpse(s) unlock.--
[Wed Aug 26 00:01:59 2015] a decaying skeleton's corpse splinters into hundreds of tiny bone fragments.
[Wed Aug 26 00:01:59 2015] Clumsy crushes a decaying skeleton for 7 points of damage.
[Wed Aug 26 00:01:59 2015] a decaying skeleton has been slain by Clumsy!
[Wed Aug 26 00:01:59 2015] Auto attack is on.
[Wed Aug 26 00:02:00 2015] Grug tells General:2, 'LFM AM/LORD'
[Wed Aug 26 00:02:00 2015] You punch a large rat for 5 points of damage.
[Wed Aug 26 00:02:00 2015] A large rat bites YOU for 1 point of damage.
[Wed Aug 26 00:02:00 2015] You kick a large rat for 1 point of damage.
[Wed Aug 26 00:02:02 2015] You punch a large rat for 2 points of damage.
[Wed Aug 26 00:02:03 2015] A large rat tries to bite YOU, but misses!
[Wed Aug 26 00:02:06 2015] You punch a large rat for 6 points of damage.
[Wed Aug 26 00:02:06 2015] A large rat bites YOU for 1 point of damage.
[Wed Aug 26 00:02:08 2015] a decaying skeleton clatters as it turns towards you.
[Wed Aug 26 00:02:08 2015] <SYSTEMWIDE_MESSAGE>: Gorenaire has been defeated by a group of hardy adventurers! Please join us in congratulating Kyrax along with everyone else who participated in this achievement!
[Wed Aug 26 00:02:08 2015] Clumsy tries to crush a decaying skeleton, but misses!
[Wed Aug 26 00:02:08 2015] A decaying skeleton tries to hit Clumsy, but misses!
[Wed Aug 26 00:02:09 2015] A large rat tries to bite YOU, but YOU dodge!
[Wed Aug 26 00:02:09 2015] You try to punch a large rat, but miss!
[Wed Aug 26 00:02:11 2015] Clumsy crushes a decaying skeleton for 8 points of damage.
[Wed Aug 26 00:02:11 2015] A decaying skeleton hits Clumsy for 2 points of damage.
[Wed Aug 26 00:02:11 2015] You kick a large rat for 1 point of damage.
[Wed Aug 26 00:02:12 2015] A large rat bites YOU for 1 point of damage.
[Wed Aug 26 00:02:13 2015] You gain experience!!
[Wed Aug 26 00:02:13 2015] You punch a large rat for 6 points of damage.
[Wed Aug 26 00:02:13 2015] You have slain a large rat!
[Wed Aug 26 00:02:13 2015] Clumsy crushes a decaying skeleton for 8 points of damage.
[Wed Aug 26 00:02:14 2015] A decaying skeleton hits Clumsy for 3 points of damage.
[Wed Aug 26 00:02:15 2015] --You have decided to not loot 1 item(s): Piece of Rat Fur. The item(s) will be available to anyone after the corpse(s) unlock.--
[Wed Aug 26 00:02:15 2015] --You have decided to not loot 2 item(s): Rat Whiskers. The item(s) will be available to anyone after the corpse(s) unlock.--
[Wed Aug 26 00:02:16 2015] a decaying skeleton's corpse splinters into hundreds of tiny bone fragments.
[Wed Aug 26 00:02:18 2015] Auto attack is on.
[Wed Aug 26 00:02:18 2015] a snake hisses and strikes!
[Wed Aug 26 00:02:18 2015] You punch a snake for 6 points of damage.
[Wed Aug 26 00:02:18 2015] A snake tries to hit YOU, but misses!
[Wed Aug 26 00:02:20 2015] You kick a snake for 1 point of damage.
[Wed Aug 26 00:02:21 2015] You try to punch a snake, but miss!
[Wed Aug 26 00:02:21 2015] A snake hits YOU for 4 points of damage.
[Wed Aug 26 00:02:24 2015] A snake tries to hit YOU, but misses!
[Wed Aug 26 00:02:25 2015] You try to punch a snake, but a snake parries!
[Wed Aug 26 00:02:27 2015] A snake tries to hit YOU, but misses!
[Wed Aug 26 00:02:28 2015] You kick a snake for 1 point of damage.
[Wed Aug 26 00:02:28 2015] You try to punch a snake, but miss!
[Wed Aug 26 00:02:30 2015] A snake hits YOU for 2 points of damage.
[Wed Aug 26 00:02:32 2015] You try to punch a snake, but miss!
[Wed Aug 26 00:02:33 2015] A snake tries to hit YOU, but misses!
[Wed Aug 26 00:02:36 2015] You punch a snake for 5 points of damage.
[Wed Aug 26 00:02:36 2015] A snake hits YOU for 1 point of damage.
[Wed Aug 26 00:02:37 2015] You kick a snake for 1 point of damage.
[Wed Aug 26 00:02:39 2015] A snake hits YOU for 1 point of damage.
[Wed Aug 26 00:02:40 2015] You gain experience!!
[Wed Aug 26 00:02:40 2015] You punch a snake for 6 points of damage.
[Wed Aug 26 00:02:40 2015] You have slain a snake!
[Wed Aug 26 00:02:42 2015] --You have looted a Snake Egg.--
[Wed Aug 26 00:02:42 2015] <SYSTEMWIDE_MESSAGE>: Innoruuk has been defeated by a group of hardy adventurers! Please join us in congratulating Harken along with everyone else who participated in this achievement!
[Wed Aug 26 00:02:43 2015] --You have decided to not loot 1 item(s): Snake Scales. The item(s) will be available to anyone after the corpse(s) unlock.--
[Wed Aug 26 00:02:46 2015] Auto attack is on.
[Wed Aug 26 00:02:46 2015] You punch a large rat for 6 points of damage.
[Wed Aug 26 00:02:46 2015] A large rat tries to bite YOU, but misses!
[Wed Aug 26 00:02:47 2015] You try to kick a large rat, but miss!
[Wed Aug 26 00:02:47 2015] You punch a large rat for 6 points of damage.
[Wed Aug 26 00:02:49 2015] A large rat bites YOU for 1 point of damage.
[Wed Aug 26 00:02:51 2015] You gain experience!!
[Wed Aug 26 00:02:51 2015] You punch a large rat for 5 points of damage.
[Wed Aug 26 00:02:51 2015] You have slain a large rat!
[Wed Aug 26 00:02:53 2015] --You have decided to not loot 1 item(s): Piece of Rat Fur. The item(s) will be available to anyone after the corpse(s) unlock.--
[Wed Aug 26 00:02:53 2015] --You have decided to not loot 2 item(s): Scalded Rat Skin. The item(s) will be available to anyone after the corpse(s) unlock.--
[Wed Aug 26 00:02:56 2015] Auto attack is on.
[Wed Aug 26 00:02:56 2015] You try to punch a large rat, but miss!
[Wed Aug 26 00:02:56 2015] A large rat bites YOU for 1 point of damage.
[Wed Aug 26 00:02:57 2015] You kick a large rat for 1 point of damage.

我将日志文件路径和名称存储在程序的设置中,如下所示:

Properties.Settings.Default.setting_logfolder // Folder Path
Properties.Settings.Default.setting_logfile // File Name

我需要在单击按钮时打开文件并读取文件直到再次单击按钮以停止进程,在读取文件时我需要将找到的行输出到文本框以显示给用户。发生这种情况时,日志文件仍将由父程序写入。

这是我目前拥有的按钮代码。

    private void btnStart_Click(object sender, EventArgs e)
    {
        if (btnStart.Text == "Start Parsing")
        {
            // change text on button and switch status image
            btnStart.Text = "Stop Parsing";
            pbStatus.Image = Properties.Resources.online;

            string logfile = Properties.Settings.Default.setting_logfolder += Properties.Settings.Default.setting_logfile;

            if (File.Exists(logfile))
            {
                Stream stream = File.Open(logfile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                StreamReader streamReader = new StreamReader(stream);
                string str = streamReader.ReadToEnd();
                tbOutput.AppendText(str);
                streamReader.Close();
                stream.Close();
            }
            else
            {
                tbOutput.AppendText(logfile);
            }
        }
        else
        {
            btnStart.Text = "Start Parsing";
            pbStatus.Image = Properties.Resources.offline;
        }
    }

我需要它继续读取文件,直到再次按下按钮,我只需要文件中的这一行

[Wed Aug 26 00:02:08 2015] <SYSTEMWIDE_MESSAGE>: XXXXXXXXX has been defeated by a group of hardy adventurers! Please join us in congratulating XXXXXX along with everyone else who participated in this achievement!

我的回答"for C#"

要查找包含系统范围消息的任何行,请使用:

\[[\w :]+\] <SYSTEMWIDE_MESSAGE>

对于该消息,但允许使用不同的名称,请使用:

\[[\w :]+\] <SYSTEMWIDE_MESSAGE>: \w+ has been defeated by a group of hardy adventurers! Please join us in congratulating \w+ along with everyone else who participated in this achievement!

这些都假定 user/character 名称可以包含字母、数字或下划线

您当前的算法执行 ReadToEnd(),它不会读取单独的行并一次将它们咳出,您似乎想这样做(?!也许!!??!)。相反,它将读取到流(文件)的末尾,然后吐出整个文件。

this answer to a related question 中简要描述了使用 FileSystemWatcher 的方法 - FileSystemWatcher 有一些注意事项,但它应该更 "pure .Net approach"

备用解决方案 (Powershell)

如果您不需要 GUI,而您只是想将此信息显示在屏幕上,以便您可以做一些事情,那么快速而肮脏的解决方案实际上就是 PowerShell 领域的一行。打开一个 PowerShell 提示符(或创建一个脚本)和(假设我们使用一个幽默地命名为 chase.dat 的日志文件和一个选择的正则表达式而不是 <#regex#>,使用以下代码:

cat chase.dat -tail 1 -wait | ?{$_ -match '<#regex#>'}

用合适的正则表达式代替 <#regex#> 你的文件名 代替 chase.dat 它会吐出任何匹配你的新行正则表达式,因为它们被写入文件。如果你有兴趣我可以解释一下...

GUI!

如果您确实想要结合使用 GUI 和 PowerShell,上面的 PS 脚本行将被传递到“PipelineExecutor" constructor in the class outlined in this article,然后它将更新指定的 属性(例如TextBox.Text) 每当脚本返回更多(在本例中为过滤后的)文本时。

刚刚这样做,效果非常好!

好的,首先,如果您要解析的日志文件可能太大而无法保存在内存中(大多数时候不是 20mb),那么请使用 File.OpenText并以块或行的形式读入。这意味着您不需要将整个文本文件加载到内存中。其次,我会将读取逻辑删除到它自己的 class 或函数中。如果您 运行 关闭 Windows Forms 或 WPF 并希望在找到一行时更新 UI ,那么您将需要在单独的 thread/task 让你的 UI 在不锁定的情况下更新。使用 Dispatcher class.

从后台任务更新您的 UI

然后,您的正则表达式将需要基于您要捕获的精确行。在我看来,您可以忽略一行开头方括号中的所有内容,然后只断言:

\w+ has been defeated by a group of hardy adventurers! Please join us in congratulating \w+ along with everyone else who participated in this achievement!$

编辑:

从您的文件共享选项来看,日志文件似乎在增长?如果这是正确的,请避免逐行阅读,因为您可以阅读部分行。如果您正在缓存您在流中的位置,这可能意味着您失去了潜在的匹配项。相反,读取一个块,检查 Environment.NewLine,并将其解析为正则表达式引擎中的一行。

试试这个:

-using (var fileStream = new FileStream("YOUR FILE", FileMode.Open, FileAccess.Read, FileShare.None))
{                  
    using (var streamReader = new StreamReader(fileStream))
    {
        string line;
        while((line = reader.ReadLine()) != null)
        {
          if(line == "YOUR TEXT")
          {
           // Do the rest
          }
        }
    }
}

您必须指定特定的模式才能完成此示例。

这是使用 FileWatcher Class

的简单方法
 public static Watchfile() 
 {
         FileSystemWatcher watch = new FileSystemWatcher();
         watch.Path = @"C:\";
         watch.Filter = "log.txt";
         watch.NotifyFilter = NotifyFilters.LastAccess |    
         NotifyFilters.LastWrite; 

        watch.Changed += new FileSystemEventHandler(OnChanged);
       watch.EnableRaisingEvents = true;
  }


 private static void OnChanged(object source, FileSystemEventArgs e)
 {


    string line;
         if (e.FullPath == @"C:\log.txt")
         {
             Regex _regex = new Regex(@"\[[\w :]+\] <SYSTEMWIDE_MESSAGE>: \w+ has been defeated by a group of hardy adventurers! Please join us in congratulating \w+ along with everyone else who participated in this achievement!");
             System.IO.StreamReader file =
                       new System.IO.StreamReader(@"C:\log.txt");

             while ((line = file.ReadLine()) != null)
             {
                 Match match = _regex.Match(line);
                 if (match.Success)
                 {

                     //Match Found

                 }


             }

             file.Close();
         }


 }