你可以使用这种模式:
\A(?=\d{11}\z)(?:(\d)(?!\d*\1\d))*(\d)(?=\d*\2\d)(?:(\d)(?!\d*\3\d))+\d\z
online demo
图案细节:
这个想法是将字符串描述为由非重复数字包围的重复数字。
使用capture group、lookahead 断言和backreference 可以轻松找到重复数字:
(\d)(?=\d*\1)
您可以使用相同的模式来确保数字没有重复,但这次使用负前瞻:(\d)(?!\d*\1)
要在搜索重复项时不考虑最后一个数字(digit n°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