99 到 9999999 之间的数字正则表达式
Posted
技术标签:
【中文标题】99 到 9999999 之间的数字正则表达式【英文标题】:Numbers between 99 and 9999999 regular expression 【发布时间】:2019-01-09 23:37:35 【问题描述】:我正在尝试生成一个正则表达式,它将匹配 99 和 9999999 范围内的任何数字。我无法理解生成数字范围的一般工作原理。我设法在网上找到了一个可以为我完成这项工作的范围生成器,但我想了解它的实际工作原理。
我对这个范围的尝试如下:
(99|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9])
这应该匹配 99、任何 3 位数字或任何 4 位数字,但它没有按预期工作。经测试,它仅匹配数字 99 和 3 位数字。四位数字根本不匹配。如果我只将 4 位数字的部分单独写为
[1-9][0-9][0-9][0-9]
它匹配 4 位数字,但是当我按照第一个示例构建它时,它不起作用。谁能给我一些说明,这实际上是如何工作的,以及如何成功地为 99 到 9999999 生成正则表达式。
演示链接 - Here
【问题讨论】:
你必须使用正则表达式吗?正则表达式在检查数字范围方面效率不高。 ***.com/questions/7649752/… 您在问题中标记了php
,所以我假设这就是您正在编码的内容。只需将输入转换为 int
并进行评估。
@user3783243 我不需要它来进行任何开发我只是想理解它,因为它看起来很简单,但我无法理解它,这让我很恼火
如果你只是想要最简单的模式来自己理解正则表达式,那就是^99|\d3,7$
【参考方案1】:
所以你想知道它是如何工作的......
正则表达式对字符串中数字的值没有真正的理解,它只关心它们是如何表示的,这就是为什么在一个范围内查找数字似乎比它应该的更尴尬的原因。您的正则表达式引擎可以理解character class 中的范围(如[0-9]
)的唯一原因是因为字符在a list 中的位置([&-~]
之类的字符范围同样有效,并且同样可以理解.)
因此,要匹配 99-9999999 这样的范围,您必须拼出它的样子:字面意思“99”,或者没有前导零的三位数字,或者没有前导零的四位数字,等等。
但这就是你的demo 所做的,对吧?它没有用。在您的测试字符串“9293”中,您的正则表达式仅匹配“929”。这里发生的事情是正则表达式引擎渴望返回一个完整的匹配 - 一旦它找到一个它就会返回它,即使稍后可能会发生更好/更长的匹配。
比赛是这样发生的。 (我会跳过一些细节,比如grouping,因为它们在这里不是很相关。)
第 1 步。
引擎将正则表达式中的第一个标记与字符串中的第一个字符进行比较
(<strong><em><kbd>9</kbd></em></strong>9|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9])
<kbd><strong><em>9</em></strong></kbd>293
✅
成功,他们匹配。
第 2 步。
引擎然后前进到正则表达式中的下一个标记和字符串中的下一个字符并进行比较。
(<strong><em>9</em></strong><kbd>9</kbd>|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9])
<strong><em>9</em></strong><kbd>2</kbd>93
❌
失败,不匹配。引擎会在此处停止并返回失败,但您使用的是alternation via |
,因此它知道可以尝试替代表达式。
第 3 步。
引擎前进到正则表达式中下一个替代表达式的第一个标记,并倒回字符串中的位置。
(99|<strong><em><kbd>[1-9]</kbd></em></strong>[0-9][0-9]|[1-9][0-9][0-9][0-9])
<strong><em><kbd>9</kbd></em></strong>293
✅
成功,他们匹配。
第 4 步。
继续。
(99|<strong><em>[1-9]<kbd>[0-9]</kbd></em></strong>[0-9]|[1-9][0-9][0-9][0-9])
<strong><em>9<kbd>2</kbd></em></strong>93
✅
匹配。
第 5 步。
又一次。
(99|<strong><em>[1-9][0-9]<kbd>[0-9]</kbd></em></strong>|[1-9][0-9][0-9][0-9])
<strong><em>92<kbd>9</kbd></em></strong>3
✅
成功。完整的表达式匹配。没有必要尝试剩下的替代品。这里返回的匹配是:
<strong><em>929</em></strong>
您可能已经知道,如果您的输入字符串改为“9923”,那么第 2 步将匹配并且那里的引擎将有 stopped and returned "99"。
您也可能已经发现,如果您将备用表达式从最长到最短重新排列
([1-9][0-9][0-9][0-9]|[1-9][0-9][0-9]|99)
将首先尝试最长的,即match and return your expected "9293"。
简化
但它仍然很冗长,尤其是当您增加范围内的位数时。您可以做几件事来简化它。
字符类[0-9]
可以用shorthand character class\d
来表示。
([1-9]\d\d\d|[1-9]\d\d|99)
不要重复它们,而是在大括号中使用quantifier,如下所示:
([1-9]\d3|[1-9]\d2|99)
碰巧,量词也可以采用min, max
的形式,因此您可以将两个相似的替代词结合起来:
([1-9]\d2,3|99)
您可能希望这会让您再次返回“929”,引擎非常渴望,但量词默认为greedy,因此他们会尽可能多地获取。这非常适合您更大的期望范围:
([1-9]\d2,6|99)
完成
你从这里用它做什么取决于你需要正则表达式做什么。就目前而言,括号是多余的,创建整个正则表达式本身的capturing group 是没有意义的。但是,当您输入如下字符串时,就会做出决定:
你很可能会被 1000 格鲁吃掉。
如果你想找出有多少格鲁将要吃掉你,你可以使用
[1-9]\d2,6|99
这将是return 1000。
但是,这种排序又回到了您的演示的原始问题。如果它是“12345678 grue”,超出范围,这将匹配“1234567”,这可能不是你想要的。您可以使用negative lookarounds 确保您匹配的号码后面没有紧跟(或前面)另一个数字。
(?<!\d)([1-9]\d2,6|99)(?!\d)
(?<!\d)
表示“从这个位置开始,前一个字符不是数字”,而(?!\d)
表示“从这个位置开始,下一个字符不是数字”。
替代项周围的括号又回来了,因为它们是此处分组所必需的,否则后瞻将仅是第一个替代表达式的一部分并应用于第一个替代表达式,而前瞻将仅是第二个替代表达式的一部分并应用于第二个替代表达式。
另一方面,如果您要确保整个字符串 only 由您范围内的数字组成,您需要改用 anchors ^
和 @987654363 @(分别是字符串的开头和结尾):
^([1-9]\d2,6|99)$
最后你可以将捕获组换成non-capturing group (?:...)
,所以:
^(?:[1-9]\d2,6|99)$
或
(?<!\d)(?:[1-9]\d2,6|99)(?!\d)
您仍然会抓取号码作为匹配项,只是不会在组抓取中重复。 (环视已经是非捕获的,无需担心这些。)
【讨论】:
【参考方案2】:首先,您需要为您的正则表达式设置一些字符串边界(除数字之外的任何内容,在我的示例中,我使用 ^
和 $
-- 乞求和行尾或字符串)
试试这个:
^([1-9][0-9]2,6|99)$
【讨论】:
是的,它现在可以找到 4 位数字,但是您能否更详细地解释一下您是如何得出这个答案的以及为什么 [1-9][0-9][0-9 ][0-9] 确实可以自己工作,但在我给出的第一个示例中没有? @IvayloGeorgiev,只需添加某种边界,例如^(99|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9])$
@IvayloGeorgiev,或从 4 位数字开始,然后是 3 等,例如:([1-9][0-9][0-9][0-9]|[1-9][0-9][0-9]|99)
以上是关于99 到 9999999 之间的数字正则表达式的主要内容,如果未能解决你的问题,请参考以下文章