(通常情况下,在写这篇文章时,我认为我修复了表达式本身,所以它现在可以用于我的目的,所以效率现在是我的主要关注点 - 但我仍然想要输入至于表达是否可以改进,或者是否会让通过的方式超出应有的范围,所以我将整个解释留在了。)
我正在尝试编写一个正则表达式来验证用户提交的文本是否符合长度要求。用户必须写出 7 个或更多 4 个或更多单词的完整句子。我们定义如下:
- 4 words means 3 or more sections of '1 or more non-space characters followed by 1 or more spaces', then 1 instance of '1 or more non-space characters optionally followed by a space' (because some people like to put spaces before their punctuation marks I guess)
- A sentence is ended with a punctuation mark (.?!)
- Zero or more spaces are allowed after each sentence
- (Repeat 7 times)
可以将此定义更改为任何合理的定义,但这就是我到目前为止的想法。这给了我以下正则表达式:
((\S+\s+){3,}\S+[.?!]\s*){7,}
这似乎可行,但显然我捏造了很多东西,想知道是否有人有更好的主意。 (它必须在任何时候都允许 html,以及用户编写的许多其他怪癖。我不太担心有人玩这个系统——仍然有手动检查,这只是第一阶段的检查,以减轻加载。)
我的另一个主要问题是效率——我是正则表达式的新手,不知道什么是“正常”计算时间,但我使用的调试器有时会在我粘贴 block 时遇到问题要检查的文本,我不知道这是由我的 RegEx 还是调试器引起的。它经常在没有匹配的较长文本部分超时。有没有更有效的方法来做我想做的...?
最佳答案
首先,在进行全文匹配时,始终用 ^...$
将正则表达式括起来。 ^
将正则表达式的开头锚定到验证字符串的开头,$
将正则表达式的结尾锚定到字符串的末尾。否则,如果匹配失败,它将从每个字符开始重复验证尝试(至少(4 个单词 * 3 个空格)* 7 个句子 = 过多的工作量)。
其次,尽可能使用互斥组。 \S (anything not white-space)
包含字符 .?!
,所以如果找不到标点符号,它必须回溯并重试每个 \S
它匹配。 (也就是说,因为第一遍会将其标记为单词而不是标点符号)所以我建议将 \S
替换为更相互排斥的“任何非空格或标点符号” [^\s.?!]
。请注意 [] 包含小写字母 s 而不是大写字母 s。 [^...]
是“匹配不在该组中的任何字符”。
根据段落长度,这两件事将使您从灾难性的回溯到合理的 ~1-3k 步。
更新:
如果您允许对验证逻辑进行小的改动,使多个短句可以一起算作一个句子,那么应该使用以下正则表达式。
^(\s*(\S+\s+){3}([.?!]\s*)?([^\s.?!]+\s+)*\S+\s*[.?!]){7,}$
这个混合版本将允许使用短句,而不会导致灾难性的回溯。如果没有小的规则更改,您将需要在可变长度模式中嵌套可变长度模式;当模式不完全相互排斥时,这是灾难性的。 (更新演示)
此外,从技术上讲,您可以将 {7,}$
替换为 {7}
如果一旦找到 7 个句子,您不关心后面会发生什么. (这将使正则表达式在发现最小可行性后立即停止,这将更能接受一些极端的边缘情况)
(您可以在 regex101.com 上使用它 here)
关于regex - 需要特定句型但允许 html 等的高效正则表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45151001/