Javascript 正则表达式的灾难性回溯问题

Javascript 正则表达式的灾难性回溯问题,javascript,regex,Javascript,Regex,我在正则表达式方面是新手,目前面临着一个与此相关的问题 我正在尝试构建一个正则表达式,该正则表达式与以下格式的字符串匹配: OptionalStaticText{OptionalStaticText%(持票人)OptionalStaticText{OptionalSubSection}OptionalStaticText}OptionalStaticText 每个部分或子部分由{…}表示。每个占位符由%(…)表示。每个部分或子部分都可以具有任意排列的可选静态文本、%(标语牌持有人)和可选子部分

我在正则表达式方面是新手,目前面临着一个与此相关的问题

我正在尝试构建一个正则表达式,该正则表达式与以下格式的字符串匹配:

OptionalStaticText{OptionalStaticText%(持票人)OptionalStaticText{OptionalSubSection}OptionalStaticText}OptionalStaticText

每个
部分
子部分
{…}
表示。每个
占位符
%(…)
表示。每个
部分
子部分
都可以具有任意排列的
可选静态文本
%(标语牌持有人)
可选子部分

为此,我创建了一个正则表达式,如下所示(也可以看到)

此表达式完全匹配有效字符串(例如:
abc{st1%(ph1)st11}int{st2%(ph2)st22}{st3%(ph3)st33{st31%(ph4)st332}}}cd
,这可以在给定的链接中进行测试

但是,每当无效的输入字符串(例如:
abc{st1%(ph1)st11}int{st2%(ph2)st22}{st3%(ph3)st33{st31%(ph4)st332}}c-d
-
根据
[\s\w]
字符组,它都会导致超时

这种无效字符串通过灾难性回溯导致超时,也可以在上面的链接中进行测试

我一定是犯了一些新手错误,但不确定是什么。我应该做些改变来避免这种情况吗


谢谢。

如果您有超时问题,可能是因为这个原因 这是您要查找的表单中包含的一类字符

改用表单本身

^((?:[\s\w]*(?:({(?:[\s\w]*%\(\w*\)[\s\w]*)+(?:{(?:[\s\w]*%\(\w*\)[\s\w]*)+)[\s\w]*)+)$


您可以编写一个解析器来解析此类结构化字符串,解析器本身将允许您检查字符串的有效性。例如(不完整):


尝试使用所有这些嵌套的重复运算符(
*
+
)精确匹配从开始到结束的行会导致灾难性的回溯

移除末端锚定
$
,只需对照匹配的长度检查输入字符串的长度

我已经重写了正则表达式,以便在可选部分也被删除的情况下使用alse:

^(?:[\w \t]*(?:{(?:[\w \t]*|%\(\w+\)|{(?:[\w \t]*|%\(\w+\))+})+})?)+

Legenda

^                              # Start of the line
(?:                            # OPEN NGM1 - Non matching group 1
  [\w \t]*                     # regex word char or space or tab (zero or more)
  (?:                          # OPEN NMG2
    {                          # A literal '{'
    (?:                        # OPEN NMG3 with alternation between:
      [\w \t]*|                # 1. regex word or space or tab (zero or more)
      %\(\w+\)|                # 2. A literal '%(' follower by regex word and literal ')'
      {(?:[\w \t]*|%\(\w+\))+} # 3. 
    )+                         # CLOSE NMG3 - Repeat one or more time
    }                          # A literal '}'
  )?                           # CLOSE NMG2 - Repeat zero or one time
)+                             # CLOSE NMG1 - Repeat one or more time
正则表达式模式

^                              # Start of the line
(?:                            # OPEN NGM1 - Non matching group 1
  [\w \t]*                     # regex word char or space or tab (zero or more)
  (?:                          # OPEN NMG2
    {                          # A literal '{'
    (?:                        # OPEN NMG3 with alternation between:
      [\w \t]*|                # 1. regex word or space or tab (zero or more)
      %\(\w+\)|                # 2. A literal '%(' follower by regex word and literal ')'
      {(?:[\w \t]*|%\(\w+\))+} # 3. 
    )+                         # CLOSE NMG3 - Repeat one or more time
    }                          # A literal '}'
  )?                           # CLOSE NMG2 - Repeat zero or one time
)+                             # CLOSE NMG1 - Repeat one or more time

Js演示版

var re=/^(?:[\w\t]*(?:{(?:[\w\t]*.%\(\w+\){(?:[\w\t]*.%\(\w+\)+})+/;
var测试=['OptionalStaticText{OptionalStaticText%(占位符)OptionalStaticText{OptionalSubSection}OptionalStaticText}OptionalStaticText','{%(占位符)OptionalStaticText{OptionalSubSection}','OptionalStaticText{OptionalStaticText{OptionalSubSection}','{OptionalStaticText{OptionalSubSection}','OptionalStaticText{OptionalStaticText{(占位符)}OptionalStaticText}OptionalStaticText},'abc{st1%(ph1)st11}int st2%(ph2)st22}{st3cd、abc{st1%(ph1)st11}int{st2%(ph2)st22}{st3%(!ph3!)st33{st31%([ph4])st332}cd、abc{st1%(ph1)st11}int{st2%(ph2)st22}{st3%(ph3)st33{st31%(ph4)st332}c-d、abc{st1%(ph1 st11}int{st2%(ph2)st22}st33};
var-m;
while(t=tests.pop()){
document.getElementById(“r”).innerHTML+='“+t+'”
; document.getElementById(“r”).innerHTML++=“有效字符串”+((t.match(re)[0]。长度==t.length)?“是”:“否”)+”

; }

我认为单靠正则表达式并不是完成这项工作的正确工具。即使你能做到这一点,生成的正则表达式对大多数人来说都是完全不可读的,因此你将面临严重的维护问题。你可以试着编写一个解析器。开始删除无用的组。这个正则表达式匹配到字符串末尾,但不会停止rk在几个测试用例上参见(此链接)[它还有一个不必要的括号级别(第一个)@GsusRecovery-是的,它确实有一个不必要的嵌套群集组。重要的是,我不尝试教育人们如何使用样式。我只是用他的正则表达式修复了他的回溯问题。我不知道你如何知道他的测试用例是什么,但这真的是个问题吗?并向我展示一个单一嵌套群集组是一微秒慢的证据你可以自己测试(如果我正确格式化了上面的链接):没有外部组,正则表达式引擎跳过71个步骤,但这不是重点。我从OP在问题中提出的字符串描述中提取了测试用例:
OptionalStaticText{OptionalStaticText%(Placholder)OptionalStaticText{OptionalSubSection}OptionalStaticText}OptionalStaticText
跳过了71个步骤
我试图将每个步骤的总时间加起来,但我就是做不到。引擎存在于一个编译的世界,而不是一个步骤的世界。对于在线测试人员来说,它们是无用的垃圾..充满了噪音。我指出了一些与你的答案不一致的地方(主要是在这些情况下,外部括号只是一种你可以忽略的嗜好)如果你能做到的话,只是为了诱使你去修复它们。我真的很好奇是否存在一个纯粹的正则表达式解决方案(主要是javascript)。仅供参考。
尝试使用所有这些嵌套的重复运算符从开始到结束完全匹配行$(*或+)导致灾难性的回溯
-这本身不会导致回溯问题。像这样,
^(?:a(?:b(?:(:c(?:d+)*)*)+)$
^(?:[\w \t]*(?:{(?:[\w \t]*|%\(\w+\)|{(?:[\w \t]*|%\(\w+\))+})+})?)+
^                              # Start of the line
(?:                            # OPEN NGM1 - Non matching group 1
  [\w \t]*                     # regex word char or space or tab (zero or more)
  (?:                          # OPEN NMG2
    {                          # A literal '{'
    (?:                        # OPEN NMG3 with alternation between:
      [\w \t]*|                # 1. regex word or space or tab (zero or more)
      %\(\w+\)|                # 2. A literal '%(' follower by regex word and literal ')'
      {(?:[\w \t]*|%\(\w+\))+} # 3. 
    )+                         # CLOSE NMG3 - Repeat one or more time
    }                          # A literal '}'
  )?                           # CLOSE NMG2 - Repeat zero or one time
)+                             # CLOSE NMG1 - Repeat one or more time