【问题标题】:Regex catastrophic backtracking only in .net正则表达式灾难性回溯仅在 .net 中
【发布时间】:2013-10-18 03:15:21
【问题描述】:

我有一个在http://gskinner.com/RegExr/http://regexhero.net/tester/ 上运行良好的正则表达式,即.net。但是它在我的 .net v4.5 代码中超时(1h+)。

(?<OuterDescription>[ \t]*--[ \t]+Description:[ \t]+(?!\<Description)(?<Description>\S[^\r\n]{1,})((\r\n|\r|\n)(?![ \t]*--[ \t]*Modified)[^\r\n]*)*)

带有样本数据:

-- ========================================================================================================
-- Author:        A Name
-- Create date: 11/26/2012
-- Description:    A description

    --    A multiline description 
        -------------------------------------- Group Name -----------------------------------------
        -- More details
        -- More details
--
--  Modified: 01/7/2012 - Some reason
--  Modified: 12/7/2012 - Some other reason
-- ========================================================================================================

我的代码是这样的

var isMatch = new Regex(pattern, RegexOptions.None, TimeSpan.FromMinutes(1)).IsMatch(_fileText);

希望 OuterDescription 从 -- Description 捕获到 -- Modified 之前

我已将其范围缩小到接近尾声的[^\r\n]*。我不知道如何在 c# 中解决此问题以不超时

编辑:

感谢您的讨论和回答。它有助于将超时从描述中移出。不幸的是,我仍然遇到问题。这就是我目前所拥有的

[ \t]*--[ \t]+={3,}
(\r\n|\n|\r)
(?<OuterAuthor>[ \t]*--[ \t]+
    Author:[ \t]+
    (?!\<Author)
    (?<Author>\S[^\r\n]+))
(\r\n|\n|\r)
(?<OuterCreateDate>[ \t]*--[ \t]+
    Create\ [Dd]ate:[ \t]+
    (?!\<Create)
    (?<CreateDate>\S[^\r\n]{1,}))
(\r\n|\n|\r)
(?<OuterDescription>[ \t]*--[ \t]+
    Description:[ \t]+
    (?!\<Description)
    (?<Description>\S[^\r\n]+)
    (?<MultilineDescription>((\r\n|\r|\n)|[^\r\n]*)*?)
    (?=(
        [ \t]*--[ \t]*Modified)|(
        [ \t]*--[ \t]*={3,})
    ))

这很好,但是一旦我在此之后添加一些内容,它就会超时。

对不起,我没有首先提到这一点,我认为一点点贪婪的星星变化将是我的全部问题。为了了解最终图片,我有一个 isAdded 布尔值,它将确定我是否检查修改的行(与描述相同),然后以页眉/页脚结束。像这样

var entireCommentHeaderNamedGroupsRegex = headerFooterRegex + newlineRegex
                                          + authorRegex + newlineRegex
                                          + createDateRegex + newlineRegex
                                          + descriptionRegex + newlineRegex
                                          + (_isAdded ? modifiedRegex + newlineRegex : "")
                                          + headerFooterRegex;

未修改时的更多示例数据:

-- =============================================
-- Author:      Garrett Carson
-- Create date: 10/4/2013
-- Description: This is a test
-- =============================================
CREATE PROCEDURE dbo.ThisIsATest
AS
BEGIN
    PRINT 'This is a test'
END

另外,正如 cmets 中提到的,我对正则表达式(在这个规模上)相当陌生,所以如果这实际上不是灾难性的回溯,请原谅我的术语。

编辑 2

作为最后的编辑,我最终选择了一个穷人的 fsm

string currentState = "LookForAuthor"
foreach (var line in lines) {
    switch currentState {
        case "LookForAuthor" : {
            ... use author regex ... save to author variable ...
            if(found) currentState = "LookForCreateDate"
            else throw new InvalidCommentException();
        }
        case "LookForCreateDate": {
            ... use createDate regex ... save to createDate variable ...
            ...
        }
        ...
    }
}
if (!_isAdded && !(currentState == "Modified-FirstLine" || currentState == "Modified-MoreLines")) {
    throw new InvalidCommentException();
}

然后我重新考虑使用枚举。逐行应用的一口大小的正则表达式不再导致超时。

【问题讨论】:

  • 据我所知,正则表达式永远不会回溯...您确定超时是由于正则表达式引起的吗?也许您只是在分析一个巨大的文件?
  • @CommuSoft 你错了。不管怎样,Garret,你期望得到什么输出,也许我可以用一个更好的正则表达式来代替你的问题?
  • 我认为您需要指定 RegexOptions.Multiline,因为您使用 ^ 作为行首。
  • 加勒特,我怀疑您发布的那一小段示例文本会导致灾难性的回溯。如果我们没有实际导致问题的输入数据,我们应该如何调试您的正则表达式? :)
  • @Sniffer:根据我在编译器方面的工作,我知道正则表达式被编译为非确定性有限自动机,然后将此类自动机修改为确定性的自动机(使用动态编程),在 O( n)。所有不能在线性时间内运行的东西,都不应该被称为正则表达式,并且可以做的不仅仅是有限状态机......

标签: c# .net regex


【解决方案1】:

以下似乎对我有用(为了清楚起见,使用RegexOptions.IgnorePatternWhitespace):

@"(?<OuterDescription>[ \t]*--[ \t]+
    Description:[ \t]+
    (?!\<Description)
    (?<Description> \S[^\r\n]{1,})
    (?<MultilineDescription>(\r?\n|[^\r\n]*)*?)
    (?=[ \t]*--[ \t]*Modified)
)";

一般来说,嵌套贪婪量词会导致您遇到的问题。不幸的是,我太累了,无法深入研究它,但如果您对出了什么问题感到好奇,我可以做个笔记,以便稍后查看

【讨论】:

  • 谢谢。这有助于将超时移出描述区域。但是,在没有超时的情况下,我仍然无法在此正则表达式之后添加任何内容。查看已编辑的问题
  • @GarrettCarson,很抱歉,但看看您的编辑,它看​​起来太复杂了,单个正则表达式解决方案不合适。我建议您只使用自己的状态机解析器 - 它可以在内部使用正则表达式,但比将所有内容都放入一个巨大的正则表达式中更易于维护和调试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-24
  • 2017-09-24
  • 1970-01-01
  • 2016-10-07
  • 1970-01-01
  • 2017-07-20
相关资源
最近更新 更多