用于匹配特定电话号码的正则表达式

3

我正在尝试判断一个字符串是否符合我国的电话号码格式,该格式由区号(可能是两位数字,可能在前面加0,也可能用括号括起来)后跟8或9个数字组成,在这些数字中,在最后4个数字之前可能有一个破折号字符。

以下是一些有效的格式:


'00 00000000'
'000-000000000'
'000 00000-0000'
'00 0000-0000'
'(00) 0000-0000'
'(000) 000000000'

到目前为止,这是我拥有的工作表达式:

p = /0?\d{2}\s?-?\s?\d{4,5}\s?-?\s?\d{4}/

我试图使用条件语句来判断区号是否在括号中,使用/?(\() 0?\d{2}\)|0?\d{2} \s?-?\s?\d{4,5}\s?-?\s?\d{4},但是出现了(repl):1: target of repeat operator is not specified: /?(\() 0?\d{2}\)|0?\d{2} \s?-?\s?\d{4,5}\s?-?\s?\d{4}的错误。

我这里做错了什么?


你能否提供一些包含括号的电话号码的示例? - Tim Biegeleisen
当你说9个数字时,是指4个数字、短横线,然后是4个数字吗? - Robo Mop
刚刚添加了示例。 - aplneto
可能是4位数字,短横线,然后是4位数字,但也可能是5位数字,短横线和4位数字。 - aplneto
我已经发布了一个回答,解决了括号问题,请随意查看。 - Robo Mop
你是否考虑过通过使用 | 将多个简单模式组合起来,而不是使用一个非常复杂的模式? - Stefan
5个回答

4

不要使用正则表达式验证电话号码。我敢打赌,你不想排除那些偶然打出2个连续空格或其他字符的号码。

相反,过滤掉所有的非数字和前导零,然后再进行验证。像这样:

number.gsub(/\D+/, '').gsub(/\A0+/) =~ /\d{8,9}/

我不确定它是否符合你的需求,但我敢打赌你已经明白了。毕竟,[000]1234 56789 是一个易于理解的电话号码。


谢谢!那是个好主意,我一定会这么做的。 - aplneto
2
mudsie,我认为你想表达的是.gsub(/\A0+/, '')。这并没有告诉我们一个字符串是否代表一个有效的电话号码,但这不是我们的想法吗?例如,"Something like 99.473218% of Rubiests use snake case for names of variables and methods." .gsub (/\D+/, '').gsub(/\A0+/, '') =~ /\d{8,9}/ #=> 0。我通过给该号码打电话并听不到响铃确认了这一点。 - Cary Swoveland
这个也不符合要求,因为只有一个前导0应该被忽略,对吧? - Max
同样适用于电子邮件、地址等。我曾经住在街道1bis号,1/2-3栋,5-5号公寓(别问我为什么),你猜有多少外卖送餐表格接受了这个地址。 - Aleksei Matiushkin
@AlekseiMatiushkin 我同意。从这个角度来看,我只会去掉非数字字符并验证它不为空。去掉前导零可能是有破坏性的。 - Max
显示剩余3条评论

2
我的回答针对您对可选括号的条件想法。
自v2.0以来,Ruby支持条件语句。语法(?(A)X|Y):如果A为true,则为X,否则为Y。
  • 在开头放置包含一个左括号的可选 捕获组
    ^(\()?
  • 稍后在模式中的任何位置检查是否成功:
    (?(1)\) |[ -])
    如果成功:需要一个闭合的)后跟一个空格|否则:[ -] 空格或破折号。

因此,带有条件的整个模式可能是

^(\()?0?\d{2}(?(1)\) |[ -])\d{4,5}[ -]?\d{4}$

请查看Rubular的演示Regex101。根据您的需求进行进一步调整。

另一种选择是使用交替(?:\(abc\)|abc),@CarySwoveland已经回答了这个问题,但我认为@AlekseiMatiushkin的答案肯定会让生活更加轻松。


1
条件表达式对我来说是新的,肯定是我工具箱中值得添加的东西。考虑到它如此有用,而且 Ruby v2.0 已经发布很长时间了,我很惊讶在这里没有看到它被使用过。 - Cary Swoveland

2

可能有几种方法可以验证这些数字。其中一种方法是,我们编写所有可能的电话号码,然后编写一个表达式进行验证。可能类似于:

最初的回答:

验证这些数字可能有多种方法。一种方法是编写所有可能的电话号码,然后编写相应的表达式进行验证。例如:

[0-9]{2,3}(\s|-)[0-9]{4,5}-?[0-9]{3,4}

"最初的回答":测试
re = /[0-9]{2,3}(\s|-)[0-9]{4,5}-?[0-9]{3,4}/m
str = '\'00 00000000\'
\'000-000000000\'
\'000 00000-0000\'
\'00 0000-0000\''

# Print the match result
str.scan(re) do |match|
    puts match.to_s
end

演示

这段代码仅用于展示捕获组以及表达式可能是有效的:

最初的回答:

const regex = /[0-9]{2,3}(\s|-)[0-9]{4,5}-?[0-9]{3,4}/gm;
const str = `'00 00000000'
'000-000000000'
'000 00000-0000'
'00 0000-0000'`;
let m;

while ((m = regex.exec(str)) !== null) {
    // This is necessary to avoid infinite loops with zero-width matches
    if (m.index === regex.lastIndex) {
        regex.lastIndex++;
    }
    
    // The result can be accessed through the `m`-variable.
    m.forEach((match, groupIndex) => {
        console.log(`Found match, group ${groupIndex}: ${match}`);
    });
}

正则表达式

如果这个表达式不是你想要的,可以在regex101.com上进行修改或更改。

enter image description here

正则表达式电路图

jex.im也可以帮助可视化表达式。

enter image description here


编辑1:

如果遇到(),我们需要在最初的表达式中添加两个负回顾后发。可能类似于这个

\(?[0-9]{2,3}\)?(\s|-)[0-9]{4,5}-?[0-9]{3,4}

enter image description here


1
领先的 [0-9]{2,3} 会匹配 999,这必须根据 OP 的要求排除。 - Aleksei Matiushkin
1
是的,但如果我输入0?\d{2}它应该可以做到,对吗? - aplneto
1
我刚刚添加了更多使用括号的示例。 - aplneto
1
嗨@Emma,这几乎是对的,但它不能匹配只有开放或关闭括号的字符串,例如(00 0000-0000。此外,区号必须是一个可选的'0'后跟两个数字。 - aplneto
1
@Emma 是的,但这种格式不应该是有效的。我想在表达式中验证的是,如果区号前有一个开括号,那么也应该有一个闭括号。 例如 (0000000000 不应该是有效的,但 0000000000(00)000000000 应该是有效的。 - aplneto
显示剩余4条评论

2
我相信您可以使用以下正则表达式。原始答案翻译成"最初的回答"。请看下面的内容:

我相信您可以使用以下正则表达式。

R = /
    \A            # match beginning of string
    (?:           # begin a non-capture group
      \(0?\d{2}\) # match '(' then an optional `0` then two digits then ')'
    |             # or
      0?\d{2}     # match an optional `0` then two digits
    )             # end the non-capture group
    (?:           # begin a non-capture group
      [ ]+        # match one or more spaces
    |             # or
      -           # match a hyphen
    )             # end the non-capture group
    \d{4,5}       # match 4 or 5 digits
    -?            # optionally match a hyphen
    \d{4}         # match 4 digits
    \z            # match end of string
    /x            # free-spacing regex definition mode

arr = [
  '00 00000000',
  '000-000000000',
  '000 00000-0000',
  '00 0000-0000',
  '(00) 0000-0000',
  '(000) 000000000',
  '(000 000000000',
  '(0000) 000000000'
]

arr.map { |s| s.match? R }
  #=> [true, true, true, true, true, true, false, false]

正则表达式通常写成以下形式。

最初的回答:

R = /\A(?:\(0?\d{2}\)|0?\d{2})(?: +|-)\d{4,5}-?\d{4}\z/

如果前导数字不能为零,则应按以下方式更改。 (例如,'001-123456789'和'(12)-023456789'无效。)请参考下面的代码: 如果前导数字不能等于零,则应更改为以下内容。
R = /\A(?:\(0?[1-9]\d\)|0?\[1-9]\d)(?: +|-)[1-9]\d{3,4}-?\d{4}\z/

0

除非你确定自己在一个非常、非常有限的范围内工作,否则不要这样做,例如:

  • 数字被传递到只接受特定格式的系统中,因此您知道这些确切的格式才能使用,其他格式都不行
  • 数字只是由人类阅读,所以您可以让他们自己解决,而不必验证任何内容

否则,您应该使用像https://github.com/mobi/telephone_number(受Google的libphonenumber启发)这样的强大库。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接