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

What is the best way for converting phone numbers into international format (E.164) using Java?

提问人:Vihung 提问时间:10/9/2008 最后编辑:Vihung 更新时间:5/13/2015 访问量:75519

问:

使用 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)

Java 验证 格式电话号码

评论

0赞 Sam Adamsh 7/2/2013
看看这个网站: droidprism.blogspot.com/2013/06/...

答:

11赞 cletus 10/10/2008 #1

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

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

你必须接受你的解决方案不会是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(三)、...

杜兹(十二),特伦特-夸特(三十四),......

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

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

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

评论

0赞 MSalters 10/10/2008
字符到数字的映射在广泛使用的每个国家/地区都是一致的(或者,声明相同:在没有一致映射的国家/地区,电话号码不会表示为字母)。由于国家/地区代码可用,您还可以确定需要哪个映射。
0赞 Joe Pineda 10/31/2008
优秀的帖子。顺便说一句,我们墨西哥人也会成对地对电话号码进行分组(和发音)。至少对我来说,只记住 4 个单独的数字比记住 8 个更容易(我倾向于使用英文电话号码)。
0赞 Chry Cheng 8/4/2009
如果您拨打电话。格式化程序从 java.text.Format 扩展,您将能够优雅地处理语言环境。
0赞 Windows programmer 10/10/2008 #2

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

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

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

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

1赞 Vihung 10/12/2008 #3

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

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

  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 是美国的国际拨号前缀,1 是美国的国际拨号代码)、1-123-456-7890(其中 1 是美国的本地拨号前缀)甚至 456-7890(假设我当时在 123 区号)并每次获得相同的结果。我假设在内部将这些拨打的号码转换为相同的 E.164 标准格式,并且转换都是在软件中完成的。

评论

0赞 grahamparks 6/10/2010
步骤 5 中存在潜在缺陷。在世界的某个地方,可能存在一些电话号码,其中区号可以以与国家拨号前缀相同的数字开头(注意,我不知道是否有,但我不知道也没有),因此您不知道是删除前缀还是删除部分区号。(这不是英国或美国数字的问题)
0赞 Henk 10/12/2008 #4

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

我们的产品需要将 PBX 提供的来电显示格式化为 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

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

评论

0赞 Windows programmer 10/15/2008
怎么样:CC cc AREA_CODE aa AREA_CODE_LENGTH n SUBSCRIBER 什么SUBSCRIBER_LENGTH 6 或 7。(我想我也看过 7 或 8 个。
0赞 Henk 10/16/2008
缩短的列名是合理的,我按照上面的格式设置了这个表格的格式,纯粹是为了说明原因。我不清楚这些评论对订阅者和子长度意味着什么。
1赞 Alnitak 10/31/2008 #5

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

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

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

60赞 Collin Peters 3/11/2011 #6

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));

评论

0赞 Vihung 4/15/2011
谢谢。这看起来很有希望。我会尝试一下
0赞 Maksym Kozlenko 5/15/2014
图书馆看起来不错。唯一阻止我在 Web 应用程序中使用它的是 JavaScript 文件大小:-(
0赞 Rob Stoecklein 7/29/2020
新网址为 opensource.google/projects/libphonenumber
1赞 gursahib.singh.sahni 10/30/2020
有没有办法使用库将纯数字文本转换为 +1-647-288-4782?国家/地区代码后面有一个连字符。谢谢。
4赞 arksoft 10/7/2011 #7

这是我的解决方案:

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

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

自由享受和使用。