提高多个正则表达式匹配

时间:2018-03-30 01:08:18

标签: c# regex console .net-4.5

如果有人提出类似的问题,我很抱歉,2天后我的谷歌查询没有结果。它可能是我一直在使用的搜索字符串。

无论如何,我正在尝试提高我正在编写的应用程序的性能。它当前打开一个文本文件,读取整个内容,并匹配正则表达式模式。这些文件通常不超过10MB,但每个文件最多可包含~70k行(或更多,文件大小设置为不超过10MB)

以下是相关的代码块:

    Regex m1 = new Regex(pattern_1);
    Regex m2 = new Regex(pattern_2);
    Regex m3 = new Regex(pattern_3);
    Regex m4 = new Regex(pattern_4);
    Regex m5 = new Regex(pattern_5); 
    Regex m6 = new Regex(pattern_6); 
    Regex m7 = new Regex(pattern_7); 

        foreach (string file in files)
            using (StreamReader fr = new StreamReader(file))
            {
                string lr;

                while ((lr = fr.ReadLine()) != null)
                {
                    Match m1_1 = m1.Match(lr);
                    Match m2_2 = storageSize.Match(lr);
                    Match m3_3 = logStart.Match(lr);
                    Match m4_4 = mergeStart.Match(lr);
                    Match m5_5 = filePatcher.Match(lr);
                    Match m6_6 = mergeEnd.Match(lr);
                    Match m7_7 = logEnd.Match(lr);

                    if (m1_1.Success)
                    {
                        string m1_str = m1_1.Value;
                    //do stuff
                    }
                    if (m2_2.Success)
                    {
                        string m2_str = m2_2.Value;
                    //do stuff
                    }

                }
            }

我把剩下的成功陈述拿出来了,但你明白了。

7种正则表达式模式中的每一种都只能匹配一条线(绝对零重叠,其中2个正则表达式查询可以达到同一条线)

为了表现,我认为做以下(我称之为'程序化英语')是个好主意:

如果match.success = true,则移至下一个lr(行读取),然后从m1开始。这(理论上)会节省一些时间来尝试匹配其余的正则表达式模式。

我将如何做到这一点,还是有更好的攻击角度来提高性能?我目前正在使用RegexOptions.Compiled进行测试,因为这些字符串可能非常长/复杂。我也对ZIP中包含的文件执行相同的操作,并从内存中读取/匹配。

我对C#相当陌生(有些,几乎没有任何老实说,大约12年前使用c ++的经验),而且我的应用程序正在按照我的意愿行事;我无法弄清楚性能方面有助于加快速度:)

编辑:对于那些要求我的正则表达式定义的人,我不能出于隐私原因发布它们。我能做的最好的就是这个非常简化的样本:

Line 1 - [Sentence] This is a sample of User1's Sentence, the total length is 564 characters.
Line 2 - [Sentence] I'm writing another sentence just because.
Line 3 - Garbage Sentence
Sentence Line 4
Line 5 - [Sentence] This is a sample of User1's Sentence, the total length is 10459275 characters.

获得第1行和第5行的模式是:

Regex m1 = new Regex (@"\[.*\bSentence\].*User1.*\b", RegexOptions.Compiled);

Edit2:结束修复我的性能问题。我花了一些时间喝啤酒,然后退后一步,重新评估我在做什么。转过来说我正在寻找的RegEx模式已经足够独特了,如果line.contains(“单词”)我可以做,然后在那个我无法获得足够的独特性来过滤掉那个的正则表达式匹配。

从2小时到大约6秒(在更大的文件包上)。

4 个答案:

答案 0 :(得分:0)

我不确定C#部分,但是为了优化正则表达式效率(从你所描述的是程序性能的瓶颈)我发现在模式上使用调试器是有帮助的:

您可以在PCRE模式下尝试https://www.debuggex.com。并试图改变你的正则表达式(这不是问题)开始阅读血腥细节正则表达式:http://www.rexegg.com/regex-optimizations.html#optimization

希望这有帮助

答案 1 :(得分:0)

也许,您可以尝试使用Async Tasks

获得更好的效果
public static async Task ForEachAsync<T>(this List<T> list, Func<T, Task> func)
{
    foreach (var file in files)
    {
        await func(file);
    }
}

现在,您可以执行类似

的操作
await files.ToList().ForEachAsync(async i => {
    await ReadStream(i);
});

void ReadStream(File file){
    using (StreamReader fr = new StreamReader(file))
    {
        string lr;

        while ((lr = fr.ReadLine()) != null)
        {
            Match m1_1 = m1.Match(lr);
            Match m2_2 = storageSize.Match(lr);
            Match m3_3 = logStart.Match(lr);
            Match m4_4 = mergeStart.Match(lr);
            Match m5_5 = filePatcher.Match(lr);
            Match m6_6 = mergeEnd.Match(lr);
            Match m7_7 = logEnd.Match(lr);

            if (m1_1.Success)
            {
                string m1_str = m1_1.Value;
                //do stuff
            }
            if (m2_2.Success)
            {
                 string m2_str = m2_2.Value;
                 //do stuff
            }

        }
    }
}

答案 2 :(得分:0)

由于您在循环中重复使用正则表达式,因此您可以使用RegexOptions.Compiled选项进入正确的轨道。如果要重用许多Regex并且模式是静态的,您可能需要探索Regex.CompileToAssembly方法:https://msdn.microsoft.com/en-us/library/9ek5zak6(v=vs.110).aspx

至于你的请求&#34;如果match.success = true,请转到下一个lr(行读取),然后从m1开始。这将(理论上)节省一些时间来尝试匹配其余的正则表达式模式。&#34;这可以使用IsMatch方法(https://msdn.microsoft.com/en-us/library/3y21t6y4(v=vs.110).aspx)和while循环中的continue关键字来完成。

while ((lr = fr.ReadLine()) != null)
{
     if (m1.IsMatch(lr))
     {
         string m1_str = m1.Match(lr).Value;
         //do stuff
         continue;
     }

     if (m2.IsMatch(lr))
     {
        //same idea
     }

     //and so on...
}

作为事后的想法,您可能希望使用您可能遇到的任何问题特定知识,以最有可能匹配的顺序排列您的匹配尝试。

答案 3 :(得分:0)

以下是您的选择

  1. 优化/优化您的模式
  2. 并行工作负载
  3. 自己编写解析器
  4. 最简单的方法(使用现有代码)是1 and 2的混合。然而事实是,2 and 3可能会让你的表现更好(因素更好)。

    关于2 and 3您可以将每个文件加载到内存中,使用unsafefixedpointer访问权限并使用<有效地检查每个模式的占用空间强> O(n) 时间复杂度并且采用并行方法更好。即,同时在多个线程中对每个字符进行一次检查。

    我的猜测是你当前的方法(充其量)搜索每一个模式的每一行,但最坏的情况是每个模式都会多次搜索每一行。

    示例

    fixed (char* p = expression)
    {
       var max = p + expression.Length;
       for (var i = p; i < max; i++)
       {
          if (*i = 's') // just an example
          {
              // Potential pattern
              ...
    

    显然这需要更多的时间代码,但是如果时间紧迫,它将远远超出目前所示的任何正则表达式解决方案(IMO)