【问题标题】:Read in a file using a regular expression?使用正则表达式读入文件?
【发布时间】:2012-11-05 20:07:06
【问题描述】:

这与an earlier question of mine.有切线关系

基本上,该问题的解决方案效果很好,但现在我需要对其进行调整以在更大的分析应用程序中工作。简单地使用StreamReader.ReadToEnd() 是不可接受的,因为我将读入的一些文件非常非常大。如果出现错误并且有人忘记清理,理论上它们可能是千兆字节。显然我不能只读到最后。

不幸的是,正常的读取行也是不可接受的,因为我正在读取的某些数据行包含堆栈跟踪 - 它们显然在格式中使用/r/n。理想情况下,我想告诉程序向前阅读,直到它匹配一个正则表达式,然后它返回。 .net 中是否有任何功能可以做到这一点?如果没有,我可以就如何编写它获得一些建议吗?

编辑:为了更容易理解我的问题,这里粘贴了改编代码的一些重要部分:

foreach (var fileString in logpath.Select(log => new StreamReader(log)).Select(fileStream => fileStream.ReadToEnd()))
{
    const string junkPattern = @"\[(?<junk>[0-9]*)\] \((?<userid>.{0,32})\)";
    const string severityPattern = @"INFO|ERROR|FATAL";
    const string datePattern = "^(?=[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3})";
    var records = Regex.Split(fileString, datePattern, RegexOptions.Multiline);
    foreach (var record in records.Where(x => string.IsNullOrEmpty(x) == false))
    ......

问题在于 Foreach。 .Select(fileStream =&gt; fileStream.ReadToEnd()) 会炸毁内存,我就知道。

【问题讨论】:

  • 这就是我不太喜欢使用 RegEx 处理此类任务的主要原因之一。如果你写了一个简单的解析器,你可以简单地调整它来处理新的行。
  • @JonathanWood 正则表达式非常适合在我拥有整个记录后解析单个记录。我只是弹出我需要的所有信息,然后直接进入相关字段。问题是在这种情况下的文件输入,似乎它不够灵活,无法一次给我一个记录,我有文件。但这似乎是假的,不是吗?这不是一个不常见的问题。
  • 所以堆栈跟踪有 /r/n。为什么这会消除 readline?
  • @Blam Readline 将输入/r/n,因此如果堆栈跟踪持续 5 行,我将得到那么多不完整的记录。我可以建立一个贫民区状态机来搜索datePattern,它总是开始记录,但这只是将一个可怕的低效率换成了另一个。
  • 我认为将 ReadLine 输出收集到一个列表中,直到 Regex.Match(currentLine, datePattern) 返回 true 听起来像是一个“足够好”的解决方案

标签: c# .net regex streamreader


【解决方案1】:

首先,你应该将你的 const 定义移到类声明中——编译器会为你做这件事,但这应该由你自己做,只是为了更好的代码可读性。

正如@Blam 提到的,您应该成对使用StringBuilderStreamReader.ReadLine,如下所示:

foreach(var filePath in logpath)
{
    var sbRecord = new StringBuilder();
    using(var reader = new StreamReader(filePath))
    {
        do
        {
            var line = reader.ReadLine();
            // check start of the new record lines
            if (Regex.Match(line, datePattern) && sbRecord.Length > 0)
            {
                // your method for log record
                HandleRecord(sbRecord.ToString());
                sbRecord.Clear();
                sbRecord.AppendLine(line);
            }
            // if no lines were added or datePattern didn't hit
            // append info about current record
            else
            {
                sbRecord.AppendLine(line);
            }
        } while (!reader.EndOfStream)
    }
}

如果我对您的问题有什么不明白的地方,请在评论中澄清这一点。
此外,您可以使用ThreadPool 为您的线路安排任务,只是为了您的应用程序的速度。

【讨论】:

  • 我最终编写了自己的解决方案来处理低级别的流,因为我觉得无法将文本流解析为标准化标记是绝对荒谬的。然而,对于其他任何人来说,这或多或少都是你必须做的。
  • @YYY 你可以在这里提供你的代码,只是为了记录?我认为这会很有趣。
  • 尽管我很乐意这样做,但它很快就会成为某些银行软件的一部分,因此是专有的。也就是说,当我休息几天时,我的议程之一是制作一个开源版本并将其添加到我的 github,这样我就可以扩展解决方案。就目前而言,我基本上读取固定数量的字节并保留文件位置,这在我们的项目环境中效果很好,但不是一个好的通用解决方案。一旦我复制/清理我的初始工作并将其添加到 github,这种雄心将继续下去。
猜你喜欢
  • 2020-09-06
  • 2013-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多