使用Java将电话号码转换为国际格式(E.164)的最佳方法是什么?

时间:2008-10-09 13:20:07

标签: java validation formatting phone-number

使用Java将电话号码转换为国际格式(E.164)的最佳方法是什么?

考虑到“电话号码”和国家/地区ID(假设是ISO国家/地区代码),我想将其转换为标准的E.164国际格式电话号码。

我相信我可以很容易地手工完成 - 但我不确定它在所有情况下都能正常工作。

您建议使用哪个Java框架/库/实用程序来完成此任务?

P.S。 “电话号码”可以是公众可识别的任何内容 - 例如

* (510) 786-0404
* 1-800-GOT-MILK
* +44-(0)800-7310658

最后一个是我最喜欢的 - 这是有些人在英国写他们的号码并且意味着你应该使用+44,或者你应该使用0。

E.164格式编号应全部为数字,并使用完整的国际国家代码(例如+ 44)

8 个答案:

答案 0 :(得分:49)

Google提供了一个用于处理电话号码的库。他们用于Android的同一个

http://code.google.com/p/libphonenumber/

String swissNumberStr = "044 668 18 00"
PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
try {
  PhoneNumber swissNumberProto = phoneUtil.parse(swissNumberStr, "CH");
} catch (NumberParseException e) {
  System.err.println("NumberParseException was thrown: " + e.toString());
}

// Produces "+41 44 668 18 00"
System.out.println(phoneUtil.format(swissNumberProto, PhoneNumberFormat.INTERNATIONAL));
// Produces "044 668 18 00"
System.out.println(phoneUtil.format(swissNumberProto, PhoneNumberFormat.NATIONAL));
// Produces "+41446681800"
System.out.println(phoneUtil.format(swissNumberProto, PhoneNumberFormat.E164));

答案 1 :(得分:10)

根据撰写此类文章的经验,100%可靠性真的很难实现。我已经编写了一些Java代码,这些代码在处理我们拥有的数据方面相当不错,但并不适用于每个国家/地区。您需要提出的问题是:

国家/地区之间的数字映射是否一致?美国使用了很多(例如1800-GOT-MILK),但在澳大利亚,作为一个例子,它非常罕见。您需要做的是确保您正在为相关国家/地区做正确的映射,如果它有所不同(可能没有)。我不知道哪些国家使用不同的字母表(例如俄罗斯的Cyrilic和前东方国家);

你必须接受你的解决方案不是100%而你不应该期望它。你需要采取“最好的猜测”方法。例如,没有真正的方式知道132345是澳大利亚的有效电话号码,1300 123 456,但这是13xx号码中唯一的两种模式,而且它们不能从海外调用;

您还必须询问是否要验证区域(区号)。我相信美国使用的区域代码的第二个数字是1或0的系统。这可能曾经是这种情况,但我不确定它是否仍然适用。无论如何,许多其他国家都会有其他规则。在澳大利亚,固定电话和移动(手机)电话的有效区号是两位数(第一位是0)。 08,03和04都是有效的。 01不是。你怎么迎合这个?你想要吗?

无论他们写多少位数,各国都会使用不同的约定。您必须决定是否要接受“规范”以外的其他内容。这些在澳大利亚都很常见:

  • (02)1234 5678
  • 02 1234 5678
  • 0411 123 123(但我从未见过04 1112 3456)
  • 131 123
  • 13 1123
  • 131 123
  • 1 300 123 123
  • 1300 123 123
  • 02-1234-5678
  • 1300-234-234
  • +44 78 1234 1234
  • +44(0)78 1234 1234
  • + 44-78-1234-1234
  • + 44-(0)78-1234-1234
  • 0011 44​​ 78 1234 1234(0011是标准的国际拨号代码)
  • (44)078 1234 1234(不常见)

那就是我的头脑。对于一个国家。例如,在法国,通常用数字对写出电话号码(12 34 56 78),他们也这样说:而不是:

un(one),deux(two),trois(three),...

douze(十二),trente-quatre(三十四),......

您想要迎合这种程度的文化差异吗?我会假设没有,但这个问题值得考虑,以防万一你的规则过于严格。

也有些人可能会在电话号码上附加分机号码,可能带有“分机”或类似的缩写。你想要迎合这个吗?

对不起,这里没有代码。只是一系列问题要问自己和需要考虑的问题。正如其他人所说,一系列正则表达式可以完成上述大部分工作,但最终电话号码字段(大部分)是在一天结束时自由形式的文本。

答案 2 :(得分:3)

这是我的解决方案:

public static String FixPhoneNumber(Context ctx, String rawNumber)
{
    String      fixedNumber = "";

    // get current location iso code
    TelephonyManager    telMgr = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);
    String              curLocale = telMgr.getNetworkCountryIso().toUpperCase();

    PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
    Phonenumber.PhoneNumber     phoneNumberProto;

    // gets the international dialling code for our current location
    String              curDCode = String.format("%d", phoneUtil.getCountryCodeForRegion(curLocale));
    String              ourDCode = "";

    if(rawNumber.indexOf("+") == 0)
    {
        int     bIndex = rawNumber.indexOf("(");
        int     hIndex = rawNumber.indexOf("-");
        int     eIndex = rawNumber.indexOf(" ");

        if(bIndex != -1)
        {
            ourDCode = rawNumber.substring(1, bIndex);
        }
        else if(hIndex != -1) 
        {               
            ourDCode = rawNumber.substring(1, hIndex);
        }
        else if(eIndex != -1)
        {
            ourDCode = rawNumber.substring(1, eIndex);
        }
        else
        {
            ourDCode = curDCode;
        }           
    }
    else
    {
        ourDCode = curDCode;
    }

    try 
    {
      phoneNumberProto = phoneUtil.parse(rawNumber, curLocale);
    } 

    catch (NumberParseException e) 
    {
      return rawNumber;
    }

    if(curDCode.compareTo(ourDCode) == 0)
        fixedNumber = phoneUtil.format(phoneNumberProto, PhoneNumberFormat.NATIONAL);
    else
        fixedNumber = phoneUtil.format(phoneNumberProto, PhoneNumberFormat.INTERNATIONAL);

    return fixedNumber.replace(" ", "");
}

我希望这可以帮助有同样问题的人。

享受并自由使用。

答案 3 :(得分:1)

感谢您的回答。正如原始问题中所述,我更感兴趣的是将数字格式化为标准格式,而不是确定它是否是有效(如真实的)电话号码。

我目前有一些手工编写的代码,其中包含电话号码字符串(由用户输入)和源国家/地区背景和目标国家/地区背景(拨打该号码的国家/地区以及该号码所在的国家/地区)正在拨打 - 这是系统已知的,然后按步骤进行以下转换

  1. 从数字

  2. 中删除所有空格
  3. 将所有字母转换为数字 - 使用字母到数字的查找表(例如A - > 2,B - > 2,C - > 2,D - > 3)键盘等(我不知道有些键盘以不同方式分发)

  4. 删除所有标点符号 - 保留前面的“+”(如果存在)(如果该数字已经是某种国际格式)。

  5. 确定该号码是否具有国家/地区背景的国际拨号前缀 - 例如如果源上下文是英国,我会看它是否以'00'开头 - 并用'+'替换它。我目前不检查'00'后面的数字是否跟随目标国家/地区的国际拨号代码。我在查找表中查找源国家/地区的国际拨号前缀(例如GB - > '00',US - >'011'等)

  6. 确定该号码是否具有国家/地区背景的本地拨号前缀 - 例如如果源上下文是英国,我会查看它是否以“0”开头 - 并将其替换为“+”后跟目标国家/地区的国际拨号代码。我在查找表中查找源国家/地区的本地拨号前缀(例如GB - >'0',US - >'1'等),以及另一个查找中目标国家/地区的国际拨号代码表(例如'GB'= '44',US ='1')

  7. 到目前为止,它似乎对我所抛出的所有东西都有效 - 除了+44(0)1234-567-890情况 - 我会为那个添加一个特殊的案例检查。

    写它并不难 - 我可以为我遇到的每个奇怪的例外添加特殊情况。但我真的想知道是否有标准的解决方案。

    电话公司似乎每天都在处理这件事。使用PSTN拨号时,我从未得到不一致的结果。例如,在美国(移动电话具有与固定电话相同的区号,我可以拨打+ 1-123-456-7890,或011-1-123-456-7890(其中011是国际拨号前缀) US和1是美国的国际拨号代码,1-123-456-7890(其中1是美国的本地拨号前缀),甚至是456-7890(假设我当时在123区号中)并且每次都得到相同的结果。我假设在内部这些拨打的号码被转换为相同的E.164标准格式,并且转换都是在软件中完成的。

答案 4 :(得分:1)

说实话,听起来你已经掌握了大部分基础。

英国有时(错误地)使用的+44(0)800格式令人烦恼,并且根据E.123不是严格有效的,这是ITU-T关于如何显示数字的建议。如果您还没有获得E.123的副本,那么值得一看。

对于它的价值,电话网络本身并不总是使用E.164。通常在PBX(或者如果你在蒸汽电话上的网络中)生成的ISDN信令中会有一个标志,告知网络拨打的号码是本地的,国内的还是国际的。

答案 5 :(得分:0)

这是一项非常艰巨的任务,因为每个国家/地区的电话号码编写方式都不同。

我们曾经保留一份REGEXP列表(我们支持19种格式)来解析数字的3个部分,然后将这3部分转换为“+ {1} {2} {3}”。

首先按照更具体的方式对regexp进行排序,然后选择成功解析的第一个。

答案 6 :(得分:0)

在某些国家/地区,您可以将112验证为有效的电话号码,但如果您在其前面粘贴国家/地区代码,则不再有效。在其他国家/地区,您无法验证112,但您可以将911验证为有效的电话号码。

我见过一些手机将Q放在7键上,Z放在9键上。我见过一些把Q和Z放在0键上的手机,还有一些把Q和Z放在1键上。

昨天存在的区号可能今天不存在,反之亦然。

在北美的一半(国家代码1),区域代码的第二位数规则曾经是0或1,但该规则在10年前消失了。

答案 7 :(得分:0)

我不知道可以将电话号码格式化为E.164的标准库或框架。

用于我们产品的解决方案,需要将PBX提供的调用者ID格式化为E.164,是为所有适用的国家/地区部署包含E.164格式信息的文件(数据库表)。 这样做的好处是可以更新应用程序(处理各种PSTN网络中的所有奇怪的角落情况),而无需更改生产代码库。

该表包含每个国家/地区代码的行以及有关区号长度和用户长度的信息。一个国家/地区可能有多个条目,具体取决于区号和用户号码长度的可能变化。

使用新西兰PSTN(部分)拨号计划作为表格的示例..

CC  AREA_CODE  AREA_CODE_LENGTH  SUBSCRIBER  SUBSCRIBER_LENGTH
64                            1              7
64         21                 2              7
64        275                 3              6

我们执行类似于您所描述的操作,即剥离所提供的任何非数字字符的电话号码,然后根据有关总体数量计划长度,外部访问代码和长途/国际访问代码的各种规则进行格式化。