xiaobaoqiu Blog

Think More, Code Less

Libphonenumber

1.简介

最近项目中做短信发送相关的工作.涉及到国内手机和国际手机.因此第一步需要解析电话获取国家码.

使用的库是Google提供的著名开源库libphonenumber.有的App的手机号码归属地等功能就是使用这个实现的。支持Java, C++ 和 JavaScript。

libphonenumber是一个手机号码工具类,提供了手机号码的格式化,解析,校验等功能.典型的比如:

  1. 解析手机号码,获取国际码;
  2. 根据国家代码和手机号,判断手机号是否有效;
  3. 根据国家代码和手机号,判断手机运营商
  4. 根据国家代码和手机号,手机归属地

Git地址: https://github.com/googlei18n/libphonenumber

国家码参考:http://country-code.cl/

2.使用Demo

Maven引入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    <!--手机号解析-->
    <dependency>
        <groupId>com.googlecode.libphonenumber</groupId>
        <artifactId>libphonenumber</artifactId>
        <version>7.2.1</version>
    </dependency>
    <!--手机归属地定位相关-->
    <dependency>
        <groupId>com.googlecode.libphonenumber</groupId>
        <artifactId>geocoder</artifactId>
        <version>2.9</version>
    </dependency>
    <!-- 手机运营商相关 -->
    <dependency>
        <groupId>com.googlecode.libphonenumber</groupId>
        <artifactId>carrier</artifactId>
        <version>1.21</version>
    </dependency>

简单代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
public class LibphonenumberUsage {

    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[] {
            "00861861****515",  //中国
            "008869****7718",   //台湾
            "00658****994",     //新加坡
            "1591****718",      //中国
            "00820****704546",  //Korea
            "1709****155"       //中国170
    };

    private static final String[] countryCodes = new String[]{
            "886",      //台湾
            "65",       //新加坡
            "86",       //中国
            "82",       //Korea
            "86"        //中国170
    };
    private static final String[] phones = new String[]{
            "972****718",        //台湾
            "82****94",         //新加坡
            "1591****718",      //中国
            "1074****46",       //Korea
            "1709****155"       //中国170
    };

    public static final Map<String, String> CHINESE_CARRIER_MAPPER = Maps.newHashMap();
    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) {
//        parsePhone();

//        validPhone();

//        phoneCarrierCase();

        phoneGeoCase();
    }

    /**
     * 电话解析case
     */
    private static void parsePhone() {


        for(String phone : phoneCases) {
            Phonenumber.PhoneNumber pn = doParse(phone);
            System.out.println(phone + " --> " + pn.getCountryCode() + ", " + pn.getNationalNumber());
        }
    }

    /**
     * 解析逻辑
     */
    private static final Phonenumber.PhoneNumber doParse(String phone) {
        try {
            return phoneNumberUtil.parse(phone, DEFAULT_COUNTRY);
        } catch (NumberParseException e) {
            throw new NumberFormatException("invalid phone number: " + phone);
        }
    }

    /**
     * 电话解析case
     */
    private static void validPhone() {
        for (int i = 0; i < countryCodes.length; i++) {
            boolean valid = doValid(countryCodes[i], phones[i]);
            System.out.println(countryCodes[i] + " , " + phones[i] + " --> " + valid);
        }
    }

    /**
     * 手机校验逻辑
     */
    private static boolean doValid(String countryCode, String phoneNumber){
        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);
    }

    private static void phoneCarrierCase() {
        for (int i = 0; i < countryCodes.length; i++) {
            String carrier = doCarrier(countryCodes[i], phones[i]);
            System.out.println(countryCodes[i] + " , " + phones[i] + " --> " + (CHINESE_CARRIER_MAPPER.containsKey(carrier)?CHINESE_CARRIER_MAPPER.get(carrier):carrier));
        }
    }

    /**
     * 手机运营商
     */
    private static String doCarrier(String countryCode, String phoneNumber){
        int ccode = Integer.parseInt(countryCode);
        long phone = Long.parseLong(phoneNumber);

        Phonenumber.PhoneNumber pn = new Phonenumber.PhoneNumber();
        pn.setCountryCode(ccode);
        pn.setNationalNumber(phone);

        //返回结果只有英文,自己转成成中文
        return carrier.getNameForNumber(pn, Locale.ENGLISH);
    }

    private static void phoneGeoCase() {
        for (int i = 0; i < countryCodes.length; i++) {
            String geo = doGeo(countryCodes[i], phones[i]);
            System.out.println(countryCodes[i] + " , " + phones[i] + " --> " + geo);
        }
    }

    /**
     * 手机归属地
     */
    public static String doGeo(String countryCode, String phoneNumber){
        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);
    }
}

3.注意点

  1. 电话解析的时候需要加上00前缀才能解析成功,比如008615912345678能解析,但是8615912345678不能解析成功;

  2. libphonenumber的基础信息库也是不断更新,比如libphonenumber的7.0.2版本不能解析170号段的电话,7.0.3以后就支持了;