什么是回溯?
引擎默认使用贪婪量词。贪婪量词会匹配所有可能的内容,然后根据需要进行回溯,从而实现高效匹配。
参考贪婪 vs. 勉强 vs. 占有量词:
贪婪量词首先尽可能多地匹配。所以 .*
匹配整个字符串。然后匹配器尝试匹配其后面的 f
,但没有剩余字符。因此它“回溯”,使贪婪量词少匹配一个内容(留下字符串末尾的 "o" 未匹配)。这仍然无法匹配正则表达式中的 f
,因此它再次“回溯”一步,使贪婪量词再次少匹配一个内容(留下字符串末尾的 "oo" 未匹配)。这仍然无法匹配正则表达式中的 f
,因此它再次回溯一步(留下字符串末尾的 "foo" 未匹配)。现在,匹配器最终匹配了正则表达式中的 f
,接下来的 o
和下一个 o
也被匹配了。成功![...]
a*+b
与回溯有什么关系?
在 /a*+b/
中:
a
表示字面字符 "a"。
*+
表示零个或多个,占有量词。
b
表示字面字符 "b"。
参考贪婪 vs. 勉强 vs. 占有量词:
占有量词与贪婪量词非常相似,但它不会回溯。因此,它使用 .*
匹配整个字符串,没有留下未匹配的内容。然后,它无法与正则表达式中的 f
匹配。由于占有量词不回溯,匹配失败。
为什么这很重要?
机器本身无法意识到它是否进行了(不)高效的匹配。这里有一个不错的例子:当匹配正则表达式时程序运行时间无限长。在许多情况下,快速编写的正则表达式可能不是很高效,并且在部署中可能会出现问题。
什么是原子组?
在原子组匹配完成后,它将永远不会放手。看这个例子:
Pattern: (?>\d\w{2}|12)c
Matching string: 12c
看起来完全合法,但这场比赛失败了。步骤很简单:原子组的第一个交替项完全匹配-\d\w{2}
消耗了12c
。该组然后完成其匹配-现在这是我们的指针位置:
Pattern: (?>\d\w{2}|12)c
^
Matching string: 12c
^
模式在前进。现在我们尝试匹配 c
,但没有 c
。与其尝试回溯(释放 \d\w{2}
并消耗 12
),匹配失败。
这样做不好啊!为什么要防止回溯,Unihedron?
现在想象一下我们正在操作一个 JSON 对象。这个文件不小。从结尾回溯会是个坏主意。
"2597401":[{"jobID":"2597401",
"account":"TG-CCR120014",
"user":"charngda",
"pkgT":{"pgi/7.2- 5":{"libA":["libpgc.so"],
"flavor":["default"]}},
"startEpoch":"1338497979",
"runTime":"1022",
"execType":"user:binary",
"exec":"ft.D.64",
"numNodes":"4",
"sha1":"5a79879235aa31b6a46e73b43879428e2a175db5",
"execEpoch":1336766742,
"execModify":"Fri May 11 15:05:42 2012",
"startTime":"Thu May 31 15:59:39 2012",
"numCores":"64",
"sizeT":{"bss":"1881400168","text":"239574","data":"22504"}},
{"jobID":"2597401",
"account":"TG-CCR120014",
"user":"charngda",
"pkgT":{"pgi/7.2-5":{"libA":["libpgc.so"],
"flavor":["default"]}},
"startEpoch":"1338497946",
"runTime":"33" "execType":"user:binary",
"exec":"cg.C.64",
"numNodes":"4",
"sha1":"caf415e011e28b7e4e5b050fb61cbf71a62a9789",
"execEpoch":1336766735,
"execModify":"Fri May 11 15:05:35 2012",
"startTime":"Thu May 31 15:59:06 2012",
"numCores":"64",
"sizeT":{"bss":"29630984","text":"225749","data":"20360"}},
{"jobID":"2597401",
"account":"TG-CCR120014",
"user":"charngda",
"pkgT":{"pgi/7.2-5": {"libA":["libpgc.so"],
"flavor":["default"]}},
"startEpoch":"1338500447",
"runTime":"145",
"execType":"user:binary",
"exec":"mg.D.64",
"numNodes":"4",
"sha1":"173de32e1514ad097b1c051ec49c4eb240f2001f",
"execEpoch":1336766756,
"execModify":"Fri May 11 15:05:56 2012",
"startTime":"Thu May 31 16:40:47 2012",
"numCores":"64",
"sizeT":{"bss":"456954120","text":"426186","data":"22184"}},{"jobID":"2597401",
"account":"TG-CCR120014",
"user":"charngda",
"pkgT":{"pgi/7.2-5":{"libA":["libpgc.so"],
"flavor":["default"]}},
"startEpoch":"1338499002",
"runTime":"1444",
"execType":"user:binary",
"exec":"lu.D.64",
"numNodes":"4",
"sha1":"c6dc16d25c2f23d2a3321d4feed16ab7e10c2cc1",
"execEpoch":1336766748,
"execModify":"Fri May 11 15:05:48 2012",
"startTime":"Thu May 31 16:16:42 2012",
"numCores":"64",
"sizeT":{"bss":"199850984","text":"474218","data":"27064"}}],
哦糟糕...
现在你明白我的意思了吧?:P
我将让你自己找出其余部分,并尝试了解更多关于占有量词和原子组的内容;我不会在此帖子中再写任何东西。 这里是JSON来源,我几天前看到了答案,非常启发人: REGEX重新格式化。
另请阅读
a*+b
是一个原子组吗? - Avinash Raja*+b
中的*+
是 贪婪 的,它并不会使其原子化。2. 步骤数量更多是因为引擎回溯以匹配。这直接需要正则表达式优化来防止过度回溯和潜在的 ReDoS。3.(?>a*)
=/=(?>a)*
。 - Unihedrona*+b
不是原子操作,但可以防止回溯。所以我尝试了那个。 - vks