Googleの電話番号を扱うライブラリlibphonenumberを使ってみたのでメモ

Androidアプリの開発で電話番号を扱いたくて、googleのlibphonenumberを使ってみたのでまとめます。

電話番号について

まずは、電話番号そのものについてちょっと調べてみました。
例えば、080-1234-5678なんていう番号があったとします。海外からこの電話番号にかけようとすると、+81-80-1234-5678となります。
これは、ITU-T(International Telecommunication Union Telecommunication Standardization Sector)が勧告するE.164に沿っていて、調べてみると意外と奥が深かったりします。

ドキュメントはここに落ちているので、気合いの入った方は是非読んでみてください。
(E.164 : The international public telecommunication numbering plan)
https://www.itu.int/rec/T-REC-E.164-201011-I/en

とりあえずパースしたい僕としては、ドキュメントのこの図だけ分かっておけば十分そうです。

f:id:t-miliya612:20180414190859p:plain
Figure 1 – International ITU-T E.164-number structure for geographic areas - ITU-T Rec. E.164 (11/2010) The international public telecommunication numbering plan

総務省の資料もありました。これだけで十分でしたね。
(電気通信番号制度概要) http://www.soumu.go.jp/main_sosiki/joho_tsusin/policyreports/joho_tsusin/bango/pdf/061012_4_s8.pdf

結局、必要そうな情報は以下の三点です。

  • 電話番号には世界共通の勧告がある
  • 先頭には1〜3桁のCC(国番号)が必要
    • 国際番号を除けば海外と電話番号が被る可能性がある?
  • 電話番号は最大で15桁

libphonenumber

github.com

ようやく本題です。Google Internationalizationが提供するライブラリで、電話番号をパースするのに必要なことをよしなにやってくれます。
Android限定というわけではなく、JavaC++、JS向けのライブラリのようです。

何ができるの

電話番号のパースをしたい

# パース
fun libphonenumberTest_parse() {
    var msn = "08012345678"
    println(phoneNumberUtil.parse(msn, "JP"))
    msn = "818012345678"
    println(phoneNumberUtil.parse(msn, "JP"))
    msn = "+818012345678"
    println(phoneNumberUtil.parse(msn, "JP"))
    msn = "+81 80 1234 5678"
    println(phoneNumberUtil.parse(msn, "JP"))
    msn = "+81-80-1234-5678"
    println(phoneNumberUtil.parse(msn, "JP"))
    msn = "080(1234)5678"
    println(phoneNumberUtil.parse(msn, "JP"))
    msn = "080.1234.5678"
    println(phoneNumberUtil.parse(msn, "JP"))
}
# => Country Code: 81 National Number: 8012345678
# => Country Code: 81 National Number: 8012345678
# => Country Code: 81 National Number: 8012345678
# => Country Code: 81 National Number: 8012345678
# => Country Code: 81 National Number: 8012345678
# => Country Code: 81 National Number: 8012345678
# => Country Code: 81 National Number: 8012345678

fun libphonenumberTest_parse_withInvalidRegion() {
    val msn = "+818012345678"
    println(phoneNumberUtil.parse(msn, "US"))
}
# => Country Code: 1 National Number: 818012345678 

電話番号のフォーマットをしたい

# フォーマット
fun libphonenumberTest_format() {
    /*
      public enum PhoneNumberFormat {
        E164,
        INTERNATIONAL,
        NATIONAL,
        RFC3966
      }
      */
    val msn = phoneNumberUtil.parse("08012345678", "JP")
    println("E164: " + phoneNumberUtil.format(msn, PhoneNumberUtil.PhoneNumberFormat.E164))
    println("INTERNATIONAL: " + phoneNumberUtil.format(msn, PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL))
    println("NATIONAL: " + phoneNumberUtil.format(msn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL))
    println("RFC3966: " + phoneNumberUtil.format(msn, PhoneNumberUtil.PhoneNumberFormat.RFC3966))
}
# => E164: +818012345678
# => INTERNATIONAL: +81 80-1234-5678
# => NATIONAL: 080-1234-5678
# => RFC3966: tel:+81-80-1234-5678

電話番号のタイプを知りたい: getNumberType

固定電話、携帯電話、フリーダイヤル、、、など、電話番号のタイプ判別ができます

fun libphonenumberTest_getNumberType() {
    val mobile = phoneNumberUtil.parse("08012345678", "JP")
    val fixed = phoneNumberUtil.parse("81312345678", "JP")
    val free = phoneNumberUtil.parse("0120123456", "JP")
    println("mobile: " + phoneNumberUtil.getNumberType(mobile))
    println("fixed: " + phoneNumberUtil.getNumberType(fixed))
    println("free: " + phoneNumberUtil.getNumberType(free))
}
# => mobile: MOBILE
# => fixed: FIXED_LINE
# => free: TOLL_FREE

電話番号の一致をチェックしたいisNumberMatch

2つの番号が同一かどうか、その信頼性を判断します。どのスケールでなら一致しているかを判断してくれるみたいですね。

fun libphonenumberTest_isNumberMatch() {
    println(phoneNumberUtil.isNumberMatch("+81334311234", "+81334311234"))
    println(phoneNumberUtil.isNumberMatch("+81334311234", "0334311234"))
    println(phoneNumberUtil.isNumberMatch("+81334311234", "34311234"))
    println(phoneNumberUtil.isNumberMatch("+81334311234", "+818012345678"))
    println(phoneNumberUtil.isNumberMatch("+81334311234", "0"))
}
# => EXACT_MATCH
# => NSN_MATCH
# => SHORT_NSN_MATCH
# => NO_MATCH
# => NOT_A_NUMBER

電話番号のバリデーションをしたい: isPossibleNumber, isValidNumber

電話番号の長さだけを見て、電話番号として成立しているかどうかを判断します。
isPossibleNumberは電話番号の長さを、isValidNumberは加えてprefixまで見ているらしく、前者の方が速いみたいです。

fun libphonenumberTest_isPossibleNumber() {
    val msn = phoneNumberUtil.parse("08012345678", "JP")
    val justNumber = phoneNumberUtil.parse("00000", "JP")
    val just15Numbers = phoneNumberUtil.parse("000000000000000", "JP")
    println(phoneNumberUtil.isPossibleNumber(msn))
    println(phoneNumberUtil.isPossibleNumber(justNumber))
    println(phoneNumberUtil.isPossibleNumber(just15Numbers))
}
# => true
# => false
# => true

fun libphonenumberTest_isValidNumber() {
    val msn = phoneNumberUtil.parse("08012345678", "JP")
    val justNumber = phoneNumberUtil.parse("00000", "JP")
    val just15Numbers = phoneNumberUtil.parse("000000000000000", "JP")
    println(phoneNumberUtil.isValidNumber(msn))
    println(phoneNumberUtil.isValidNumber(justNumber))
    println(phoneNumberUtil.isValidNumber(just15Numbers))
}
# => true
# => false
# => false

終わりに

今回紹介したものはほんの一部で、他にもダミーの電話番号を生成してくれたり、文章中から電話番号を抽出してくれたり、など至れり尽くせりなライブラリになっています。
電話番号というと、個人情報の中でもなかなか扱いづらく収集しづらいものに分類されてしまいがちなので、なかなかさわる機会ってないのがちょっと残念です。
ついでに、先日、キャリアがRCS準拠のサービスを始めるとのニュースがありました。

www.nttdocomo.co.jp

news.kddi.com

www.softbank.jp

これ使って遊べたら面白そうだなぁ。

おまけ

電話番号がどの企業に割り当てられているか、が一覧になっています。携帯の電話番号だとMNPのために判断が難しくなっていますが。。。 (電気通信番号指定状況) http://www.soumu.go.jp/main_sosiki/joho_tsusin/top/tel_number/number_shitei.html