提高正则表达式的性能

时间:2010-07-07 10:52:56

标签: c# .net regex optimization

我的软件允许用户使用regexp来准备文件。我正在添加一个带有常用表达式的默认regexp库,可以重复使用它来准备各种格式。 一个常见的任务是删除文件特定部分中的crlf,但不删除其他部分。例如,这个:

    <TU>Lorem 
    Ipsum</TU>
    <SOURCE>This is a sentence
    that should not contain
    any line break.
    </SOURCE>

应该成为:

    <TU>Lorem 
    Ipsum</TU>
    <SOURCE>This is a sentence that should not contain any line break.
    </SOURCE>

我有一个rexep可以很好地完成工作:

(?(?<=<SOURCE>(?:(?!</?SOURCE>).)*)(\r\n))

问题是它处理密集,文件高于500kb,可能需要30秒以上。 (编译正则表达式,在这种情况下,未编译的速度要慢得多)

这不是一个大问题,但我想知道是否有更好的方法来与Regex实现相同的结果。

提前感谢您的建议。

3 个答案:

答案 0 :(得分:2)

“编译”正则表达式只是意味着将其从Deterministic Finite Automaton转换为Non-deterministic Finite Automaton。它并不像你期望的那样“编译成机器代码”。

NFA通常比相应的DFA小,可以有效地执行更多空间。每个DFA至少有一个等效的NFA,反之亦然。但是,Perl Compatible Regular Expressions不是actually Regular。所以我不知道他们被转换成NFA,或者“编译”只是另一种形式的词法分析,一次不需要再做。

根据Russ Cox,PCRE很慢,部分原因是它们不规律,上面的表达非常规律。

哦,如果您尝试使用regexp识别嵌套标签,请不要。使用真正的(X|HT)?ML解析器。你真的不希望the pony

答案 1 :(得分:2)

我会在正则表达式中添加一个负前瞻断言,以确保您实际上可以在当前位置匹配\r\n。否则,引擎必须对整个文件中的每个字符执行整个lookbehind(任意大小引导),然后才发现没有回车符。

(?=\r\n)(?(?<=<SOURCE>(?:(?!</?SOURCE>).)*)(\r\n))

应该快得多。至少在RegexBuddy中,正则表达式引擎需要更少的步骤才能完成匹配。如果它不在.NET中,我不知道为什么。也许条件正则表达式效率不高(我必须承认,起初我没有认出它,并认为你的正则表达式中存在语法错误)。我认为在这种情况下你不需要条件正则表达式。 <怎么样

\r\n(?<=<SOURCE>(?:(?!</?SOURCE>).)*)

这更快吗?我假设您正在使用RegexOptions.Singleline来编译正则表达式。

如果没有,那么,<SOURCE>块内可能会有非常多的回车和许多其他字符,并且任意大小的后视需要很长时间。然后我的另一个建议是分开任务:

  1. 匹配<SOURCE>
  2. 替换块内的所有CRLF(无需正则表达式)
  3. 替换<SOURCE>

答案 2 :(得分:2)

试试这个:

\r\n(?=(?>[^<>]*(?><(?!/?SOURCE>)[^<>]*)*)</SOURCE>)

首先匹配\r\n,然后使用前瞻来查看匹配是否在<SOURCE></SOURCE>之间。它通过查找</SOURCE>来实现,但如果找到<SOURCE>则首先失败。原子组阻止它保存回溯所需的状态信息,因为传递或失败,从不需要回溯。