99 到 9999999 之间的数字正则表达式

Numbers between 99 and 9999999 regular expression

我正在尝试生成一个正则表达式来匹配 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 的正则表达式。

Link 演示 - Here

首先你需要一些正则表达式的字符串边界(除了数字之外的任何东西,在我的例子中我使用 ^$ - 乞讨和行尾或字符串)

试试这个:

^([1-9][0-9]{2,6}|99)$

所以您想知道这是如何工作的...

正则表达式对字符串中数字的值没有真正的理解,它只关心它们的表示方式,这就是为什么在一个范围内查找数字看起来比它应该的更尴尬。您的正则表达式引擎可以理解 character class like [0-9] at all is because of the characters' positions in a list 中的范围的唯一原因(像 [&-~] 这样的字符范围同样有效,并且同样可以理解。)

因此,要匹配 99-9999999 这样的范围,您必须拼出它的样子:字面值“99”,或者不带前导零的三位数字,或者不带前导零的四位数字,等等。

但这就是你的 demo 所做的,对吧?它没有用。在您的测试字符串“9293”中,您的正则表达式仅匹配“929”。这里发生的事情是正则表达式引擎 eager 到 return 完全匹配 - 一旦它找到一个它 returned 它,即使 [=241] =] 匹配可能稍后发生。


这是比赛的经过。 (我将跳过一些细节,例如 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

失败,不匹配。引擎将停止并且 return 此处失败,但您正在使用 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

成功。完整的表达式匹配。没有必要尝试剩下的替代方案。此处 returned 的匹配项是:

<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"


简化

虽然它仍然很冗长,尤其是当您增加范围内的位数时。您可以做几件事来简化它。

字符class[0-9]可以用shorthand character class\d.

表示
([1-9]\d\d\d|[1-9]\d\d|99)

而不是重复它们,而是在大括号中使用 quantifier,如下所示:

([1-9]\d{3}|[1-9]\d{2}|99)

碰巧,量词也可以采用 {min, max} 的形式,因此您可以将两个相似的替代词组合起来:

([1-9]\d{2,3}|99)

您可能希望这会让您再次返回 returning“929”,引擎非常急切,但量词默认为 greedy,因此他们会尝试选择尽他们所能。这很适合您更大的期望范围:

([1-9]\d{2,6}|99)

完成

你从这里用它做什么取决于你需要正则表达式做什么。就目前而言,括号是多余的,没有必要创建整个正则表达式本身的 capturing group 。但是,当您获得如下输入字符串时,就会做出决定:

You will likely be eaten by 1000 grue.

如果你想找出有多少稀饭要吃掉你,你可以使用

[1-9]\d{2,6}|99

这将 return 1000

但是,您的演示又回到了最初的问题。如果它是超出范围的“12345678 grue”,这将匹配“1234567”,这可能不是您想要的。您可以使用 negative lookarounds.

确保您匹配的号码后没有紧跟(或前接)另一个数字
(?<!\d)([1-9]\d{2,6}|99)(?!\d)

(?<!\d) 表示“从这个位置开始,前一个字符不是数字”,而 (?!\d) 表示“从这个位置开始,下一个字符不是数字”。

替代项周围的括号又回来了,因为它们是在这里分组所必需的,否则后视只会成为第一个替代表达式的一部分并应用于第一个替代表达式,而前瞻只会成为第二个替代表达式的一部分并应用于第二个替代表达式.

另一方面,如果您试图确保整个字符串 只有 由您范围内的数字组成,您需要使用 anchors ^$(分别为字符串的开头和结尾):

^([1-9]\d{2,6}|99)$

最后你可以用捕获组交换 non-capturing group (?:...),所以:

^(?:[1-9]\d{2,6}|99)$

(?<!\d)(?:[1-9]\d{2,6}|99)(?!\d)

您仍然会抓取号码作为匹配项,只是不会在组抓取中重复。 (环顾四周已经是非捕获的,无需担心这些。)