定义一个正则表达式,它匹配一个数字两次,所有其他数字匹配一次

时间:2014-08-23 13:00:33

标签: ruby-on-rails regex

作为更大的正则表达式的一部分,我想符合以下限制:

  1. 该字符串有11位数字
  2. 所有数字均为数字
  3. 在前10位数字中,一个数字[0-9](仅限一个!)必须列出两次
  4. 这意味着以下内容应该匹配:

    12345678914
    12235879600
    

    而这些不应该:

    12345678903 -> none of the numbers at digits 1 to 10 appears twice
    14427823482 -> one number appears more than twice
    72349121762 -> two numbers appear twice
    

    我试图使用前瞻,但我所管理的只是正则表达式计算某个数字,即:

    (?!.*0\1{2})
    

    这不能满足我的需要。我的查询甚至可以使用正则表达式吗?

1 个答案:

答案 0 :(得分:3)

您可以使用这种模式:

\A(?=\d{11}\z)(?:(\d)(?!\d*\1\d))*(\d)(?=\d*\2\d)(?:(\d)(?!\d*\3\d))+\d\z

online demo

模式细节:

这个想法是将字符串描述为由非重复数字包围的重复数字。

使用capture grouplookahead断言和backreference(\d)(?=\d*\1)

,可以轻松找到重复的数字

您可以使用相同的模式来确保数字没有重复,但这次使用否定前瞻:(\d)(?!\d*\1)

要在搜索重复项时不考虑最后一位(数字编号11),您只需在反向引用后添加一个数字。 (\d)(?=\d*\1\d) (通过这种方式,您可以确保在后向引用和字符串结尾之间至少有一个数字。)

请注意,在本上下文中,所谓的重复数字是一个数字,不会立即或稍后使用相同的数字。 (即在1234567891中,第一个1是一个重复的数字,但最后一个1不再是一个重复的数字,因为它后面没有其他1

\A                       # begining of the string
(?=\d{11}\z)             # check the string length (if not needed, remove it)
(?:(\d)(?!\d*\1\d))*     # zero or more non duplicate digits
(\d)(?=\d*\2\d)          # one duplicate digit
(?:(\d)(?!\d*\3\d))+     # one or more non duplicate digits
\d                       # the ignored last digit
\z                       # end of the string

另一种方式

这一次,您可以通过前瞻检查模式开头的重复项。一个预测,以确保有一个重复的数字,一个负前瞻,以确保没有两个重复的数字:

\A(?=\d*(\d)(?=\d*\1\d))(?!\d*(\d)(?=\d*\2\d)\d*(\d)(?=\d*\3\d))\d{11}\z

模式细节:

\A
(?=                       # check if there is one duplicate digit
    \d*(\d)(?=\d*\1\d)
)
(?!                       # check if there are not two duplicate digits
    \d*(\d)(?=\d*\2\d)    # the first
    \d*(\d)(?=\d*\3\d)    # the second
)
\d{11}
\z

注意:然而,似乎第一种方式更有效。

代码方式

您可以使用数组方法轻松检查字符串是否符合要求:

> mydigs = "12345678913"
=> "12345678913"
> puts (mydigs.split(//).take 10).uniq.size == 9
true
=> nil