一、闲谈
手机号码的格式千变万化,不同国家的手机号码格式有很大的差异,细细研究,会发现很多有意思的事情,比如: 你以为手机号码都是数字组成的?当然不, 在以色列,1-800-Flowers 也是一个合法的手机号, 而在埃及,手机号码可能是下面的象形文字哦
其实这些有趣的小故事都可以在 google 开源的库中找到:地址所以,很显然,本文所提及的解决方案,其实就是 google 的这个类库的使用方法。
二、设计
关于手机号校验的设计,会涉及到几个点: ① 手机号码数据类型
上文也提过,手机号码也会出现字符哦,所以,肯定不能使用 int 等数字类型,况且手机号码也基本不会用来进行计算操作。 ② 长度
手机号码长度其实是依据各个国家的设计而不相同的, 比如大陆就是11位手机号,而瑞士,据说会有8位的号码。那么应该使用多长的字段来保存呢?其实手机号保留 20 位长度,目前是比较安全的,所以,数据库设计为 varchar 50 是妥妥没问题的。
③ 页面设计
其实最佳的体验是分为两个框,一个下拉框用来选择区号,另一个输入框用来输入手机号。 ④ 手机号校验
诚然,如果强制要求有验证码校验,那么是最佳的,但时如果没有的话,那么在此推荐 google 的手机号码校验类库:libphonenumber 比如亚马逊aws的注册页面,就没有进行号码的规制校验,但时强制使用了验证码校验。关于 libphonenumber,该类库提供了 Java、JS等实现,上手简单,功能强大。而且还提供了运营商查询、归属地查询等功能。
三、类库的使用
① jar包引入
如果是使用 maven 来构建地址,需要引入如下几个库<!--手机号解析--> <dependency> <groupId>com.googlecode.libphonenumber</groupId> <artifactId>libphonenumber</artifactId> <version>8.9.9</version> </dependency> <!--手机归属地定位相关--> <dependency> <groupId>com.googlecode.libphonenumber</groupId> <artifactId>geocoder</artifactId> <version>2.99</version> </dependency> <!-- 手机运营商相关 --> <dependency> <groupId>com.googlecode.libphonenumber</groupId> <artifactId>carrier</artifactId> <version>1.9</version> </dependency> <dependency> <groupId>com.googlecode.libphonenumber</groupId> <artifactId>prefixmapper</artifactId> <version>2.99</version> </dependency>
② 工具类代码
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberToCarrierMapper;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
import com.google.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
* @Author: Jet
* @Description: 国际手机号校验
* @Date: 2018/5/9 9:20
*/
public class LibphonenumberUtil {
private static final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
private static PhoneNumberToCarrierMapper carrier = PhoneNumberToCarrierMapper.getInstance();
private static PhoneNumberOfflineGeocoder geocoder = PhoneNumberOfflineGeocoder.getInstance();
private static final String DEFAULT_COUNTRY = "CN";
private static final String[] phoneCases = new String[] {
"8618611234515", //中国 true
"00886912347718", //台湾
"006581234994", //新加坡
"15911234718", //中国
"008201234704546", //Korea
"17091234155" //中国170
};
public static final Map<String, String> CHINESE_CARRIER_MAPPER = new HashMap<>();
static {
CHINESE_CARRIER_MAPPER.put("China Mobile", "中国移动");
CHINESE_CARRIER_MAPPER.put("China Unicom", "中国联通");
CHINESE_CARRIER_MAPPER.put("China Telecom", "中国电信");
}
public static void main(String[] args) {
System.out.println(doGeo("17811981865", "86"));
}
/**
* @Author: Jet
* @Description ① 可以加区号,也可以不加,区号默认86
* ② 区号前面的“+”和“00”占位可加可不加
* ② 手机号中间可以增加“-”
* @param phone “+8617717031234 +008617717031234 8617717031234 177-1703-1234”
* @Date: 2018/5/9 9:21
*/
public static boolean doValidUniversal(String phone) {
Phonenumber.PhoneNumber phoneNumber = doParse(phone);
return phoneNumber.hasNationalNumber() && doValid(phoneNumber.getNationalNumber() + "", phoneNumber.getCountryCode() + "");
}
/**
* @Author: Jet
* @Description 电话解析逻辑
* @param phone “+8617717031234 +008617717031234 8617717031234 177-1703-1234””
* @return 电话实体类 Phonenumber.PhoneNumber
* @Date: 2018/5/9 9:21
*/
public static Phonenumber.PhoneNumber doParse(String phone) {
try {
return phoneNumberUtil.parse(phone, DEFAULT_COUNTRY);
} catch (NumberParseException e) {
throw new NumberFormatException("invalid phone number: " + phone);
}
}
/**
* @Author: Jet
* @Description 手机校验逻辑
* @param phoneNumber 手机号
* @param countryCode 手机区号
* @Date: 2018/5/9 9:21
*/
public static boolean doValid(String phoneNumber, String countryCode){
int ccode = Integer.parseInt(countryCode);
long phone = Long.parseLong(phoneNumber);
Phonenumber.PhoneNumber pn = new Phonenumber.PhoneNumber();
pn.setCountryCode(ccode);
pn.setNationalNumber(phone);
return phoneNumberUtil.isValidNumber(pn);
}
/**
* @Author: Jet
* @Description 手机运营商
* @param phoneNumber 手机号
* @param countryCode 手机区号
* @return 能转成中文则返回中文,否则返回英文的
* @Date: 2018/5/9 9:21
*/
public static String doCarrier(String phoneNumber, String countryCode){
int ccode = Integer.parseInt(countryCode);
long phone = Long.parseLong(phoneNumber);
Phonenumber.PhoneNumber pn = new Phonenumber.PhoneNumber();
pn.setCountryCode(ccode);
pn.setNationalNumber(phone);
//返回结果只有英文,自己转成成中文
String carrierEn = carrier.getNameForNumber(pn, Locale.ENGLISH);
return CHINESE_CARRIER_MAPPER.containsKey(carrierEn)?CHINESE_CARRIER_MAPPER.get(carrierEn):carrierEn;
}
/**
* @Author: Jet
* @Description 手机归属地
* @param phoneNumber 手机号
* @param countryCode 手机区号
* @Date: 2018/5/9 9:21
*/
public static String doGeo(String phoneNumber, String countryCode){
int ccode = Integer.parseInt(countryCode);
long phone = Long.parseLong(phoneNumber);
Phonenumber.PhoneNumber pn = new Phonenumber.PhoneNumber();
pn.setCountryCode(ccode);
pn.setNationalNumber(phone);
return geocoder.getDescriptionForNumber(pn, Locale.CHINESE);
}
} ③ 工具类使用说明
结构很简单,见下图:
四、总结
① 由于手机号码的规则一直是处于更新状态的,所以建议jar包的版本要常更新,可以常逛逛 maven 仓库:https://repo1.maven.org/maven2/com/googlecode/libphonenumber/ ② 手机号校验千万要加区号,否则会有歧义,如下 10 位长度手机号:
857 498 4492
10位手机号按理应该是国外的,但是你如果使用国内的区号来校验会发现,该号码是“贵州毕节”的,
至于该号码到底是什么,暂未摸清楚。



文章评论