32

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

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

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

您会推荐哪个 Java 框架/库/实用程序来完成此任务?

PS“电话号码”可以是公众可以识别的任何东西 - 例如

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

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

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

4

7 回答 7

54

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));
于 2011-03-10T19:30:00.903 回答
10

从写这种东西的经验来看,要做到 100% 的可靠性真的很难。为此,我编写了一些 Java 代码,这些代码在处理我们拥有的数据方面相当出色,但并不适用于每个国家/地区。你需要问的问题是:

国家之间的字符到数字映射是否一致?美国使用了很多这种(例如 1800-GOT-MILK),但在澳大利亚,例如,它非常罕见。您需要做的是确保您正在为所讨论的国家/地区进行正确的映射,如果它不同(它可能不会)。我不知道使用不同字母的国家(例如俄罗斯的西里尔语和前东区国家)是做什么的;

您必须接受您的解决方案不会是 100%,而且您不应该期望它会是 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 (一), deux (二), trois (三), ...

它的

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

你想迎合这种程度的文化差异吗?我认为不会,但这个问题值得考虑,以防你的规则过于严格。

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

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

于 2008-10-10T05:02:44.470 回答
4

这是我的解决方案:

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(" ", "");
}

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

自由享受和使用。

于 2011-10-07T12:51:29.363 回答
1

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

我目前有一些手工制作的代码,它需要一个电话号码字符串(由用户输入)和一个源国家上下文和目标国家上下文(拨打号码的国家,以及拨打号码的国家) - 这是系统已知的),然后分步进行以下转换

  1. 从数字中删除所有空格

  2. 将所有字母转换为数字 - 使用字母到数字的查找表(例如 A-->2、B-->2、C-->2、D-->3)等用于键盘(我不知道一些键盘以不同的方式分配这些)

  3. 去掉所有标点符号 - 如果前面的“+”存在,则保持原样(如果数字已经采用某种国际格式)。

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

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

到目前为止,它似乎适用于我所抛出的所有内容 - 除了 +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 标准格式,并且转换都是在软件中完成的。

于 2008-10-12T01:15:04.227 回答
1

老实说,听起来您已经涵盖了大部分基础。

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

无论如何,电话网络本身并不总是使用 E.164。PBX 生成的 ISDN 信号中通常会有一个标志(如果您使用的是蒸汽电话,则在网络中),它告诉网络所拨打的号码是本地、国内还是国际。

于 2008-10-30T16:23:37.827 回答
0

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

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

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

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

于 2008-10-10T05:35:54.107 回答
0

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

用于我们产品的解决方案需要将 PBX 提供的 caller-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

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

于 2008-10-12T04:23:14.147 回答