使用JavaScript的正则表达式重新格式化美国电话号码

160

我想在Javascript中重新格式化(替换而不是验证-有许多验证参考)电话号码以供显示。以下是一些数据示例:

  • 123 4567890
  • (123) 456-7890
  • (123)456-7890
  • 123 456 7890
  • 123.456.7890
  • (空/无效)
  • 1234567890

是否有一种简单的方法使用正则表达式来完成这个任务?我正在寻找最佳方法。是否有更好的方法?

我想将号码重新格式化为:(123) 456-7890


5
你的目标格式是哪一个? - Till Helge
1
这个:(123)456-7890 - Matt K
3
我会尽量简洁地翻译以下内容:建议只保留数字字符并将其分为三个子字符串。 - Wiseguy
2
@Wiseguy请将其作为答案发布(附带示例),因为这确实是OP应该做的。 - Brian Driscoll
1
您还需要指定每个接受的格式如何映射到目标格式,如果输入为null,则这一点并不明显。除非您愿意使用额外的条件来排除该情况。 - Jon
国际号码呢? - RamPrasadBismil
17个回答

372
假设您想要的格式是“(123) 456-7890”:

function formatPhoneNumber(phoneNumberString) {
  var cleaned = ('' + phoneNumberString).replace(/\D/g, '');
  var match = cleaned.match(/^(\d{3})(\d{3})(\d{4})$/);
  if (match) {
    return '(' + match[1] + ') ' + match[2] + '-' + match[3];
  }
  return null;
}

这是一个版本,可以选择性地添加国际代码+1

function formatPhoneNumber(phoneNumberString) {
  var cleaned = ('' + phoneNumberString).replace(/\D/g, '');
  var match = cleaned.match(/^(1|)?(\d{3})(\d{3})(\d{4})$/);
  if (match) {
    var intlCode = (match[1] ? '+1 ' : '');
    return [intlCode, '(', match[2], ') ', match[3], '-', match[4]].join('');
  }
  return null;
}
formatPhoneNumber('+12345678900') // => "+1 (234) 567-8900"
formatPhoneNumber('2345678900')   // => "(234) 567-8900"

2
非常好,谢谢!不过,在添加了这个函数后,我将return (!m) ? null更改为return (!m) ? "" - Matt K
3
一个很好的解决问题方法的课程。我一直在思考如何匹配所有可能的情况--你可以排除不相关的内容,看看是否有匹配项。非常好。 - Jkleg
3
请注意,此方法不适用于像+1555-555-5555这样的数字。 - William
3
除了“+1”之外,还有其他国家呼叫代码。为了考虑到这一点,在正则表达式中将“^(1|)”替换为“^(\d|)”,在“var intlCode”行上将“'+1'”替换为“'+${match[1]}'”。 - Adam Zerner
1
@AdamZerner 确实,但是其他国家的电话号码并不都有十位数字,因此这里的格式不适用于国际通用。 - maerics
显示剩余4条评论

46

var x = '301.474.4062';
    
x = x.replace(/\D+/g, '')
     .replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3');

alert(x);


1
谢谢Sean,我喜欢你简短简单的内联解决方案。 - user752746
1
谢谢!我将其更改为 x = x.replace(/[^\d]+/g, '') .replace(/(\d{1})(\d{3})(\d{3})(\d{4})/, '+$1 ($2) $3-$4');,以便在电话号码前添加“+1”等内容。 - Greg A
谢谢!这正是我所需要的。 - Albert Hidalgo

40

可能的解决方案:

function normalize(phone) {
    //normalize string and remove all unnecessary characters
    phone = phone.replace(/[^\d]/g, "");

    //check if number length equals to 10
    if (phone.length == 10) {
        //reformat and return phone number
        return phone.replace(/(\d{3})(\d{3})(\d{4})/, "($1) $2-$3");
    }

    return null;
}

var phone = '(123)4567890';
phone = normalize(phone); //(123) 456-7890

14

这个答案借鉴了maerics的回答。它主要的不同在于它接受部分输入的电话号码,并对已输入的部分进行格式化。

phone = value.replace(/\D/g, '');
const match = phone.match(/^(\d{1,3})(\d{0,3})(\d{0,4})$/);
if (match) {
  phone = `${match[1]}${match[2] ? ' ' : ''}${match[2]}${match[3] ? '-' : ''}${match[3]}`;
}
return phone

这个可以实时地工作,从源海报中添加所需格式。经过1.5小时的搜索,我很高兴尝试了这个! - fungusanthrax
如果有帮助的话,我在区号周围添加了括号:(${match[1]}${match[2] ? ') ' : ''}${match[2]}${match[3] ? '-' : ''}${match[3]} - Peter Hollingsworth
实际上问题在于,您无法在字符串中间通过退格键删除“-”或空格。当用户使用退格键时,您需要防止重新格式化(例如,newstring.length < oldstring.length),或跟踪光标位置并找出用户刚刚通过这些分隔符进行了退格操作,例如:if (cursorPosition === 4 && numericString.length > 3) - Peter Hollingsworth
在我的React代码中,我通过仅内部存储输入的数字,然后格式化放置在字段中的内容来解决这个问题。因此,退格键会删除实际值中的前一个字符,而不是显示值中的字符。 - David Baucum

7
我正在使用这个函数来格式化美国电话号码。
function formatUsPhone(phone) {

    var phoneTest = new RegExp(/^((\+1)|1)? ?\(?(\d{3})\)?[ .-]?(\d{3})[ .-]?(\d{4})( ?(ext\.? ?|x)(\d*))?$/);

    phone = phone.trim();
    var results = phoneTest.exec(phone);
    if (results !== null && results.length > 8) {

        return "(" + results[3] + ") " + results[4] + "-" + results[5] + (typeof results[8] !== "undefined" ? " x" + results[8] : "");

    }
    else {
         return phone;
    }
}

它接受几乎所有可能的美国电话号码写法。结果格式化为标准形式(987) 654-3210 x123。


7

倒推思维

只取最后10位数字(忽略第一个“1”)。

function formatUSNumber(entry = '') {
  const match = entry
    .replace(/\D+/g, '').replace(/^1/, '')
    .match(/([^\d]*\d[^\d]*){1,10}$/)[0]
  const part1 = match.length > 2 ? `(${match.substring(0,3)})` : match
  const part2 = match.length > 3 ? ` ${match.substring(3, 6)}` : ''
  const part3 = match.length > 6 ? `-${match.substring(6, 10)}` : ''    
  return `${part1}${part2}${part3}`
}

当您输入时的示例输入/输出

formatUSNumber('+1333')
// (333)

formatUSNumber('333')
// (333)

formatUSNumber('333444')
// (333) 444

formatUSNumber('3334445555')
// (333) 444-5555

4

2021

libphonenumber-js

示例

import parsePhoneNumber from 'libphonenumber-js'

const phoneNumber = parsePhoneNumber('+12133734253')

phoneNumber.formatInternational() === '+1 213 373 4253'
phoneNumber.formatNational() === '(213) 373-4253'
phoneNumber.getURI() === 'tel:+12133734253'

8
这个库看起来非常实用!但是大小为145KB,如果你只需要格式化数字的话,可能有点重。这是其他人寻找时需要注意的事情。 - BU0

3

根据David Baucum的回答,这里提供了一个版本,尝试改进自动替换“即时输入”的功能,例如在React的onChange事件处理程序中:

function formatPhoneNumber(phoneNumber) {
  const cleanNum = phoneNumber.toString().replace(/\D/g, '');
  const match = cleanNum.match(/^(\d{3})(\d{0,3})(\d{0,4})$/);
  if (match) {
    return '(' + match[1] + ') ' + (match[2] ? match[2] + "-" : "") + match[3];
  }
  return cleanNum;
}

//...

onChange={e => setPhoneNum(formatPhoneNumber(e.target.value))}

只要有3个数字,它就会插入 (###),然后它将继续遵循正则表达式,直到看起来像这样 (###) ###-####。

2

我已经扩展了David Baucum的答案,以支持长度为4位的扩展名。它还包括原始问题中请求的括号。这种格式将在您输入字段时起作用。

phone = phone.replace(/\D/g, '');
const match = phone.match(/^(\d{1,3})(\d{0,3})(\d{0,4})(\d{0,4})$/);
if (match) {
    phone = `(${match[1]}${match[2] ? ') ' : ''}${match[2]}${match[3] ? '-' : ''}${match[3]}${match[4] ? ' x' : ''}${match[4]}`;
}
return phone;

2
几乎所有这些问题都会在用户试图删除分隔符时出现,特别是从字符串中间开始。以下是一种使用jQuery解决此问题的方法,并确保您编辑时光标停留在正确位置:
//format text input as phone number (nnn) nnn-nnnn
$('.myPhoneField').on('input', function (e){
    var $phoneField = e.target;
    var cursorPosition = $phoneField.selectionStart;
    var numericString = $phoneField.value.replace(/\D/g, '').substring(0, 10);

    // let user backspace over the '-'
    if (cursorPosition === 9 && numericString.length > 6) return;

    // let user backspace over the ') '
    if (cursorPosition === 5 && numericString.length > 3) return;
    if (cursorPosition === 4 && numericString.length > 3) return;

    var match = numericString.match(/^(\d{1,3})(\d{0,3})(\d{0,4})$/);
    if (match) {
        var newVal = '(' + match[1];
        newVal += match[2] ? ') ' + match[2] : '';
        newVal += match[3] ? '-' + match[3] : '';

        // to help us put the cursor back in the right place
        var delta = newVal.length - Math.min($phoneField.value.length, 14);      
        $phoneField.value = newVal;
        $phoneField.selectionEnd = cursorPosition + delta;
    } else {
        $phoneField.value = '';        
    }
})

这个问题和之前的回答都不是关于“当用户正在输入时”的。只是关于获取输入并给出格式化输出的。 - Don Cheadle
我知道这不是 OP 特别要求的内容,但这正是我在寻找的,而且它完美地运作。 - DJFriar

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