带空白的c# regex性能太慢



我在c#中使用WinForms . NET 2.0。

我有文本文件,大约1000-1500行。它们中的某些行以4个或更多字母的单词开头,我必须在这些单词后面加一个冒号。这些行开头是否有空格是可选的,除了这些单词之外,该行可以包含更多文本。下面是一个例子:

    lda $00,x
    mov $20
    rep #$20
    tax
    lda #$0000,y
word
    ...         ; comment
  anotherword           ; this word has whitespace before it.

同样,如果已经有冒号,则忽略它们以防止添加更多冒号。下面是我的代码:

Regex R = new Regex(@"^s*(?<word>[A-Za-z0-9_]{4,})", RegexOptions.Multiline); //keep the words stored in a group called word
MatchCollection M = R.Matches(txt); //let my text file string be "txt"
foreach (Match m in M)
{
    string mm = m.Groups["word"].Value;
    if (!Regex.IsMatch(txt, @"^s*b" + mm + @"b:", RegexOptions.Multiline)) // if already a colon, return
        txt = Regex.Replace(txt, @"^s*b" + mm + @"b", mm + ":", RegexOptions.Multiline);
}

它工作和所有,但问题是?它太慢了。我在文本文件中做了其他操作,但我已经确认它们很快,问题在于上面正则表达式中的两个"s*"。当我同时删除它们时,搜索速度会提高10倍。

我该如何解决这个问题?

@TimPietzcker的替代方案:

result = Regex.Replace(subject, @"^(?>(s*w{4,}))(?!:)", "$1:", RegexOptions.Multiline);

,其中(?>...)是一个原子分组。当regex引擎进入原子分组时,不允许回溯该分组所使用的输入中的任何地方。

现在,为什么这是有益的?考虑这样一行:

             ab3 #13 spaces, then a, b, 3

如果不使用原子分组,当regex无法匹配第二个量词中的第4个字符时,它必须回溯到a之前的字符:但它是一个空格,它不匹配。以此类推,直到它到达行开始前的字符,^不匹配,然后才声明失败(s*可以匹配空字符串)。

使用原子分组,引擎将不会以这种方式回溯,这是一个巨大的性能增益,特别是当您处理大数据时。

我看到了三个主要问题:

  1. 基本上在每行上执行相同的正则表达式匹配多达三次。正如Tim所演示的,无论该行是否匹配正则表达式,您都不需要多次触摸该行。此外,在使用相同的正则表达式执行Replace()之前,您不需要使用Match()或IsMatch()测试字符串。如果字符串不匹配正则表达式,Replace()将简单地返回原封不动的字符串。

  2. 没有必要像你现在做的那样在我的手上做一个替换的弦。

  3. s匹配所有空白字符,包括换行符。如果(例如)有9个空行后面跟着一个匹配行,则正则表达式将消耗全部10行。如果第十行*不匹配,regex引擎将放弃匹配尝试,并从第二个空白行开始重新尝试。第三行,第四行,等等。如果从正则表达式中删除s*有很大的影响,这可能是原因:它试图匹配大量不必要的空白。如果你知道你正在寻找的字符串将总是在单行上,你应该确保正则表达式只匹配水平空白。

来演示:

result = Regex.Replace(subject, @"(?m)^([ t]*w{4,})(?![w:])", "$1:");
解释:

  • (?m)仅仅是一种更方便的方式来指定Multiline选项。
  • ^([ t]*w{4,})匹配一行中的第一个单词以及任何前导空格,并将其全部捕获在组#1中。
  • (?![w:])为负正向;它断言下一个字符(如果有的话)既不是单词字符也不是冒号。这确保你已经阅读了整个单词,并且单词后面没有冒号。
  • 在replacement参数中,$1是第一个捕获组内容的占位符。

我注意到您的regex匹配前导空白而不捕获它,并且您没有在替换中添加任何空白。其效果是从执行此替换的任何行中删除前导空格,但不会从任何其他行中删除。如果你真的想这样做,你可以把^([ t]*w{4,})改成^[ t]*(w{4,})

最新更新