使用 Java 将电话号码转换为国际格式(E.164)的最佳方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/187216/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the best way for converting phone numbers into international format (E.164) using Java?
提问by Vihung
What is the best way for converting phone numbers into international format (E.164) using Java?
使用 Java 将电话号码转换为国际格式(E.164)的最佳方法是什么?
Given a 'phone number' and a country id (let's say an ISO country code), I would like to convert it into a standard E.164 international format phone number.
给定一个“电话号码”和一个国家/地区 ID(假设是 ISO 国家/地区代码),我想将其转换为标准的 E.164 国际格式电话号码。
I am sure I can do it by hand quite easily - but I would not be sure it would work correctly in all situations.
我相信我可以很容易地手动完成 - 但我不确定它在所有情况下都能正常工作。
Which Java framework/library/utility would you recommend to accomplish this?
您建议使用哪种 Java 框架/库/实用程序来完成此任务?
P.S. The 'phone number' could be anything identifiable by the general public - such as
PS“电话号码”可以是公众可以识别的任何东西 - 例如
* (510) 786-0404
* 1-800-GOT-MILK
* +44-(0)800-7310658
that last one is my favourite - it is how some people write their number in the UK and means that you should either use the +44 or you should use the 0.
最后一个是我最喜欢的——这是一些人在英国写他们的号码的方式,这意味着你应该使用 +44 或你应该使用 0。
The E.164 format number should be all numeric, and use the full international country code (e.g.+44)
E.164 格式编号应全部为数字,并使用完整的国际国家代码(例如+44)
采纳答案by Collin Peters
Google provides a library for working with phone numbers. The same one they use for Android
Google 提供了一个用于处理电话号码的库。他们用于 Android 的同一个
http://code.google.com/p/libphonenumber/
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));
回答by Vilmantas Baranauskas
This is a very difficult task as phone numbers are written differently nearly in each country.
这是一项非常艰巨的任务,因为几乎每个国家/地区的电话号码都不同。
We used to keep a list of REGEXPs (we supported 19 formats) to parse 3 parts of a number and then converted those 3 parts to "+{1} {2} {3}".
我们曾经保留一个 REGEXP 列表(我们支持 19 种格式)来解析数字的 3 部分,然后将这 3 部分转换为“+{1} {2} {3}”。
Sort regexps by more specific first and then take the first which succeeds in parsing.
首先按更具体的正则表达式排序,然后取第一个成功解析的。
回答by cletus
Speaking from experience at writing this kind of thing, it's really difficult to do with 100% reliability. I've written some Java code to do this that is reasonably good at processing the data we have but won't be applicable in every country. Questions you need to ask are:
从写这种东西的经验来说,100%的可靠性真的很难做到。我已经编写了一些 Java 代码来做到这一点,它们在处理我们拥有的数据方面相当不错,但并不适用于每个国家/地区。你需要问的问题是:
Are the character to number mappings consistent between countries? The US uses a lot of this (eg 1800-GOT-MILK) but in Australia, as one example, its pretty rare. What you'd need to do is ensure that you were doing the correct mapping for the country in question if it varies (it might not). I don't know what countries that use different alphabets (eg Cyrilic in Russia and the former Eastern block countries) do;
国家之间的字符到数字映射是否一致?美国使用了很多这种(例如 1800-GOT-MILK),但在澳大利亚,例如,它非常罕见。您需要做的是确保您正在为所讨论的国家/地区进行正确的映射,如果它有所不同(可能不会)。我不知道使用不同字母表的国家(例如俄罗斯的西里尔字母和前东方区块国家)是做什么的;
You have to accept that your solution will not be 100% and you should not expect it to be. You need to take a "best guess" approach. For example, theres no real way of knowing that 132345 is a valid phone number in Australia, as is 1300 123 456 but that these are the only two patterns that are for 13xx numbers and they're not callable from overseas;
你必须接受你的解决方案不会是 100%,你不应该期望它是。您需要采用“最佳猜测”方法。例如,没有真正的方法知道 132345 是澳大利亚的有效电话号码,1300 123 456 也是如此,但这是 13xx 号码仅有的两种模式,并且无法从海外拨打;
You also have to ask if you want to validate regions (area codes). I believe the US uses a system where the second digit of the area code is a 1 or a 0. This may have once been the case but I'm not sure if it still applies. Whatever the case, many other countries will have other rules. In Australia, the valid area codes for landlines and mobile (cell) phones are two digits (the first is 0). 08, 03 and 04 are all valid. 01 isn't. How do you cater for that? Do you want to?
您还必须询问是否要验证区域(区号)。我相信美国使用的系统中区号的第二位数字是 1 或 0。这可能曾经是这种情况,但我不确定它是否仍然适用。无论如何,许多其他国家/地区会有其他规则。在澳大利亚,固定电话和移动(手机)电话的有效区号是两位数(第一位是 0)。08、03、04都有效。01 不是。你怎么照顾?你想要_____吗?
Countries use different conventions no matter how many digits they're writing. You have to decide if you want to accept something other than the "norm". These are all common in Australia:
无论写多少位数字,国家/地区都使用不同的约定。您必须决定是否要接受“规范”以外的其他内容。这些在澳大利亚都很常见:
- (02) 1234 5678
- 02 1234 5678
- 0411 123 123 (but I've never seen 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 is the standard international dialling code)
- (44) 078 1234 1234 (not common)
- (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(不常见)
And thats just off the top of my head. For one country. In France, for example, its common the write the phone number in number pairs (12 34 56 78) and they pronounce it that way too: instead of:
这只是我的头顶。为一国。例如,在法国,通常以数字对 (12 34 56 78) 写电话号码,他们也这样发音:而不是:
un (one), deux (two), trois (three), ...
un(一),deux(二),trois(三),...
its
它的
douze (twelve), trente-quatre (thirty four), ...
douze(十二),trente-quatre(三十四),...
Do you want to cater for that level of cultural difference? I would assume not but the question is worth considering just in case you make your rules too strict.
你想迎合那种程度的文化差异吗?我认为不是,但这个问题值得考虑,以防万一您的规则过于严格。
Also some people may append extension numbers on phone numbers, possibly with "ext" or similar abbreviation. Do you want to cater for that?
此外,有些人可能会在电话号码上附加分机号码,可能带有“ext”或类似的缩写。你想迎合那个吗?
Sorry, no code here. Just a list of questions to ask yourself and issues to consider. As others have said, a series of regular expressions can do much of the above but ultimately phone number fields are (mostly) free form text at the end of the day.
抱歉,这里没有代码。只是要问自己的问题和要考虑的问题的列表。正如其他人所说,一系列正则表达式可以完成上述大部分工作,但最终电话号码字段在一天结束时(大部分)是自由格式的文本。
回答by Windows programmer
In some countries you can validate 112 as a valid phone number, but if you stick a country code in front of it it won't be valid any more. In other countries you can't validate 112 but you can validate 911 as a valid phone number.
在某些国家/地区,您可以将 112 验证为有效的电话号码,但如果您在其前面贴上国家/地区代码,它将不再有效。在其他国家/地区,您无法验证 112,但可以将 911 验证为有效电话号码。
I've seen some phones that put Q on the 7 key and Z on the 9 key. I've seen some phones that put Q and Z on the 0 key, and some that put Q and Z on the 1 key.
我见过一些手机将 Q 放在 7 键上,将 Z 放在 9 键上。我见过一些手机将 Q 和 Z 放在 0 键上,还有一些将 Q 和 Z 放在 1 键上。
An area code that existed yesterday might not exist today, and vice-versa.
昨天存在的区号今天可能不存在,反之亦然。
In half of North America (country code 1), the second digit rule used to be 0 or 1 for area codes, but that rule went away 10 years ago.
在北美的一半地区(国家代码 1),区号的第二位数规则曾经是 0 或 1,但该规则在 10 年前就消失了。
回答by Vihung
Thanks for the answers. As stated in the original question, I am much more interested in the formatting of the number into the standard format than I am in determining if it is a valid (as in genuine) phone number.
感谢您的回答。正如原始问题中所述,我对将数字格式化为标准格式更感兴趣,而不是确定它是否是有效的(如真实的)电话号码。
I have some hand crafted code currently that takes a phone number String (as entered by the user) and a source country context and target country context (the country from where the number is being dialed, and the country to where the number is being dialed - this is known to the system) and then does the following conversion in steps
我目前有一些手工编写的代码,它采用电话号码字符串(由用户输入)以及源国家/地区上下文和目标国家/地区上下文(拨打号码的国家/地区以及拨打号码的国家/地区) - 这是系统已知的),然后按步骤进行以下转换
Strip all whitespace from the number
Translate all alpha into digits - using a lookup table of letter to digit (e.g. A-->2, B-->2, C-->2, D-->3) etc. for the keypad (I was not aware that some keypads distribute these differently)
Strip all punctuation - keeping a preceding '+' intact if it exists (in case the number is already in some sort of international format).
Determine if the number has an international dialling prefix for the country context - e.g. if source context is the UK, I would see if it starts with a '00' - and replace it with a '+'. I do not currently check whether the digits following the '00' are followed by the international dialing code for the target country. I look up the international dialing prefix for the source country in a lookup table (e.g. GB-->'00', US-->'011' etc.)
Determine if the number has a local dialing prefix for the country context - e.g. if the source context is the UK, I would look to see if it starts with a '0' - and replace it with a '+' followed by the international dialing code for the target country. I look up the local dialing prefix for the source country in a lookup table (e.g. GB-->'0', US-->'1' etc.), and the international dialing code for the target country in another lookup table (e.g.'GB'='44', US='1')
从数字中去除所有空格
将所有字母转换为数字 - 使用字母到数字的查找表(例如 A-->2、B-->2、C-->2、D-->3)等键盘(我不知道一些键盘以不同的方式分配这些)
去除所有标点符号 - 如果存在,则保持前面的“+”完整(以防数字已经采用某种国际格式)。
确定该号码是否具有国家上下文的国际拨号前缀 - 例如,如果源上下文是英国,我会查看它是否以“00”开头 - 并将其替换为“+”。我目前不检查“00”后面的数字是否后跟目标国家/地区的国际拨号代码。我在查找表中查找源国家/地区的国际拨号前缀(例如 GB-->'00'、US-->'011' 等)
确定该号码是否具有国家上下文的本地拨号前缀 - 例如,如果源上下文是英国,我会查看它是否以“0”开头 - 并将其替换为“+”,然后是国际拨号目标国家/地区的代码。我在查找表中查找源国家/地区的本地拨号前缀(例如 GB-->'0'、US-->'1' 等),并在另一个查找表中查找目标国家/地区的国际拨号代码(例如'GB'='44', US='1')
It seems to work for everything I have thrown at it so far - except for the +44(0)1234-567-890 situation - I will add a special case check for that one.
到目前为止,它似乎适用于我抛出的所有内容 - 除了 +44(0)1234-567-890 情况 - 我将为该情况添加一个特殊情况检查。
Writing it was not hard - and I can add special cases for each strange exception I come across. But I would really like to know if there is a standard solution.
编写它并不难 - 我可以为我遇到的每个奇怪的异常添加特殊情况。但我真的很想知道是否有标准解决方案。
The phone companies seem to deal with this thing every day. I never get inconsistent results when dialing numbers using the PSTN. For example, in the US (where mobile phones have the same area codes as landlines, I could dial +1-123-456-7890, or 011-1-123-456-7890 (where 011 is the international dialing prefix in the US and 1 is the international dialing code for the US), 1-123-456-7890 (where 1 is the local dialing prefix in the US) or even 456-7890 (assuming I was in the 123 area code at the time) and get the same results each time. I assume that internally these dialed numbers get converted to the same E.164 standard format, and that the conversion is all done in software.
电话公司似乎每天都在处理这件事。使用 PSTN 拨号时,我从未得到不一致的结果。例如,在美国(手机与固定电话的区号相同,我可以拨打 +1-123-456-7890 或 011-1-123-456-7890(其中 011 是国际拨号前缀) US 和 1 是美国的国际拨号代码)、1-123-456-7890(其中 1 是美国的本地拨号前缀)甚至 456-7890(假设我当时在 123 区号中)并且每次都得到相同的结果。我假设这些拨打的号码在内部被转换为相同的 E.164 标准格式,并且转换都是在软件中完成的。
回答by Henk
I'm not aware of a standard library or framework available for formatting telephone numbers into E.164.
我不知道可用于将电话号码格式化为 E.164 的标准库或框架。
The solution used for our product, which requires formatting PBX provided caller-id into E.164, is to deploy a file (database table) containing the E.164 format information for all countries applicable. This has the advantage that the application can be updated (to handle all the strange corner cases in various PSTN networks) w/out requiring changes to the production code base.
我们的产品使用的解决方案需要将 PBX 提供的来电显示格式化为 E.164,这是部署一个文件(数据库表),其中包含适用于所有国家/地区的 E.164 格式信息。这样做的优点是可以更新应用程序(以处理各种 PSTN 网络中的所有奇怪的极端情况),而无需更改生产代码库。
The table contains a row for each country code and information regarding area code length and subscriber length. There may be multiple entries for a country depending on what variations are possible with area code and subscriber number lengths.
该表包含每个国家代码的一行以及有关地区代码长度和用户长度的信息。一个国家可能有多个条目,这取决于区号和用户号码长度可能发生的变化。
Using New Zealand PSTN (partial) dial plan as an example of the table..
使用新西兰 PSTN(部分)拨号计划作为表的示例。
CC AREA_CODE AREA_CODE_LENGTH SUBSCRIBER SUBSCRIBER_LENGTH
64 1 7
64 21 2 7
64 275 3 6
We do something similar to what you have described, i.e. strip the provided telephone number of any non-digit characters and then format based on various rules regarding overall number plan length, outside access code, and long distance/international access codes.
我们做了一些与您所描述的类似的事情,即将提供的电话号码去掉任何非数字字符,然后根据有关整体号码计划长度、外部访问代码和长途/国际访问代码的各种规则进行格式化。
回答by Alnitak
To be honest, it sounds like you've got most of the bases covered already.
老实说,听起来您已经涵盖了大部分基础知识。
The +44(0)800 format sometimes (incorrectly) used in the UK is annoying and isn't strictly valid according to E.123, which is the ITU-T recommendation for how numbers should be displayed. If you haven't got a copy of E.123 it's worth a look.
有时(错误地)在英国使用的 +44(0)800 格式很烦人,并且根据 E.123 并非严格有效,这是 ITU-T 建议如何显示数字。如果您还没有 E.123 的副本,那么值得一看。
For what it's worth, the telephone network itself doesn't always use E.164. Often there'll be a flag in the ISDN signalling generated by the PBX (or in the network if you're on a steam phone) which tells the network whether the number being dialled is local, national or international.
就其价值而言,电话网络本身并不总是使用 E.164。通常,PBX 生成的 ISDN 信号中会有一个标志(如果您使用的是 Steam 电话,则在网络中)它会告诉网络所拨打的号码是本地、国内还是国际。
回答by arksoft
This was my solution:
这是我的解决方案:
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(" ", "");
}
I hope this helps someone with the same problem.
我希望这可以帮助有同样问题的人。
Enjoy and use freely.
自由享受和使用。