嘎里三分熟
  • 首页
  • JMusic
  • TSBay
  • 常用工具
  • About Me
  • 留言板
一行代码一世浮生
  1. 首页
  2. Java基础
  3. 正文

国际手机号码有效性校验

2018年08月03日 7453点热度 24人点赞 0条评论

一、闲谈

    手机号码的格式千变万化,不同国家的手机号码格式有很大的差异,细细研究,会发现很多有意思的事情,
    比如:         你以为手机号码都是数字组成的?当然不,             在以色列,1-800-Flowers 也是一个合法的手机号,             而在埃及,手机号码可能是下面的象形文字哦                 libphonenumber01.png     其实这些有趣的小故事都可以在 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);
        }
    }

        ③ 工具类使用说明

            结构很简单,见下图:
            libphonenumber02.png



四、总结

    ① 由于手机号码的规则一直是处于更新状态的,所以建议jar包的版本要常更新,
        可以常逛逛 maven 仓库:https://repo1.maven.org/maven2/com/googlecode/libphonenumber/     ② 手机号校验千万要加区号,否则会有歧义,如下 10 位长度手机号:
        857 498 4492
        10位手机号按理应该是国外的,但是你如果使用国内的区号来校验会发现,该号码是“贵州毕节”的,
        至于该号码到底是什么,暂未摸清楚。





本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: java libphonenumber 工具 手机号校验
最后更新:2018年08月03日

GoldenJet

爱折腾技术的90后漫威小死忠程序员一枚

点赞
< 上一篇
下一篇 >

文章评论

取消回复

通过电子邮件订阅博客

分类目录
  • BootStrap (2)
  • Bug集中营 (6)
  • Java web (3)
  • JavaScript (7)
  • Java基础 (17)
  • Java工具 (5)
  • Linux (3)
  • Python (3)
  • SpringBoot (14)
  • Spring基础 (8)
  • thymeleaf (1)
  • 娱乐 (3)
  • 小谈 (2)
  • 常用工具 (7)
  • 技术分析集 (5)
  • 技能 (10)
  • 源码 (4)
  • 科普类 (1)
  • 算法 (9)
  • 踩坑记 (5)
文章归档
  • 2020年11月 (1)
  • 2020年7月 (1)
  • 2020年4月 (2)
  • 2020年3月 (1)
  • 2020年1月 (1)
  • 2019年11月 (1)
  • 2019年10月 (1)
  • 2019年9月 (1)
  • 2019年8月 (1)
  • 2019年7月 (2)
  • 2019年5月 (2)
  • 2019年4月 (2)
  • 2019年3月 (3)
  • 2019年2月 (2)
  • 2019年1月 (2)
  • 2018年12月 (2)
  • 2018年11月 (3)
  • 2018年10月 (3)
  • 2018年9月 (2)
  • 2018年8月 (3)
  • 2018年7月 (2)
  • 2018年5月 (1)
  • 2018年4月 (3)
  • 2018年3月 (2)
  • 2018年2月 (3)
  • 2018年1月 (5)
  • 2017年12月 (2)
  • 2017年11月 (3)
  • 2017年10月 (1)
  • 2017年9月 (1)
  • 2017年8月 (1)
  • 2017年7月 (7)
  • 2017年6月 (5)
  • 2017年5月 (1)
  • 2017年4月 (2)
  • 2017年3月 (4)
  • 2017年2月 (2)
小伙伴友链
  • 前端驿站

COPYRIGHT © 2017-2020 嘎里三分熟. ALL RIGHTS RESERVED.

浙ICP备17005575号-1

浙公网安备 33010802009043号