見出し画像

libphonenumberライブラリを使って国コード付き電話番号をparseする際に、リージョンコードの取得方法で悩んだ話

電話番号操作に便利なライブラリ「libphonenumber」

国コード付き電話番号からノーマルの電話番号に変換する処理を実装した。
その際に「libphonenumber」というクソ便利なライブラリを見つけた。
このライブラリに国コード付き電話番号を突っ込めば、ノーマルの電話番号を取得することが出来るのだ。

以下はJAVAのテストコード。
libphonenumberのバージョンは8.13.11。

String tel = "+818011112222";

PhoneNumberUtil util = PhoneNumberUtil.getInstance();
PhoneNumber.PhoneNumber phoneNumber = util.parse(tel, "JP");
String normalTel = util.format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);

System.out.println(normalTel);

080-1111-2222

がコンソールに出力され、無事に国コードを除いた電話番号が取れた。
めでたしめでたし。

このままじゃ使えねーじゃんと気付く

このコードを作った直後に気づいた。

ここだよ!4行目!

parseメソッドの引数は、電話番号とリージョンコード。
電話番号は手元にあるけど、リージョンコードが無い。
テストコードは決め打ちで「JP」を入れているから動くが、サービスが稼働したらどの国の電話番号が入ってくるか不明。
何とかして動的にリージョンコードを取得する必要がある。

唯一の希望は、この案件の仕様として電話番号は必ず国コード付きで連携されるということ。
その代わりに絶望は、国コードと電話番号の境界をプログラムで判断する必要があること。
国コードは特殊なものを除いて+記号と1~3桁の数字で構成されており、0パディングは無い。

日本:+818011112222
アメリカ:+18011112222

上記の電話番号だと、人間なら日本は+81ね、アメリカは+1ね、残りは電話番号ね、って知っていれば判断できるけど。
JAVAくんはそんなの知らないから…
これ無理ゲーじゃん?

でも「このライブラリを使うしかねぇ」と思って、何とかしようとライブラリのソースを読み込んだ。
とりあえず「CountryCodeToRegionCodeMap.java」という、リージョンコードと国コードが紐づいたマップをソース中に見つけた。

https://github.com/google/libphonenumber/blob/master/java/libphonenumber/src/com/google/i18n/phonenumbers/CountryCodeToRegionCodeMap.java

あとは電話番号から国コードを引っこ抜いて、このマップに当てはめれば自動的にリージョンコードが取れる。
だけど、国コードと電話番号の境界を判別できない問題は解決していなかった・・・

国コードの仕様に助けられた

「CountryCodeToRegionCodeMap.java」をボケーっと眺めてたら、あることに気づいた。
最小値の国コードは「1」、次は「7」、次は「20」。
メチャクチャに間が飛んでいる。
ここで超希望的観測な仮説を立てた。
「ある国コードの数字の組み合わせは、他の国コードの数字の組み合わせと必ず前方一致しない仕様なんじゃね?」
つまり、アメリカが「1」なので、他の国で「1x」とか「1xx」という国コードは存在しない。
日本は「81」で、「8」や「81x」は存在しない。
だからこんな不自然な番号振りになってるんだ。たぶん。

そう思ってマップを見返したら、本当に前方一致しなかった

つまり
「国コード付きの電話番号から+を除く。先頭3桁までを国コードとし、先頭から1桁ずつ国コードの対象範囲を増やしながら、総当たりでマップに対してアクセスする」
このロジックで解決だ。
マップから国コードを元にリージョンコードを取得するには「PhoneNumberUtil.getRegionCodeForCountryCode」という便利メソッドを見つけたのでこれを使えば良い。

String tel = "+818011112222";

int countryCodeOneDigit = Integer.parsInt(tel.replace("+", "").substring(0, 1));
int countryCodeTwoDigit = Integer.parsInt(tel.replace("+", "").substring(0, 2));
int countryCodeThreeDigit = Integer.parsInt(tel.replace("+", "").substring(0, 3));

PhoneNumberUtil util = PhoneNumberUtil.getInstance();

String regionCode1 = util.getRegionCodeForCountryCode(countryCodeOneDigit); 
System.out.println(regionCode1);

String regionCode2 = util.getRegionCodeForCountryCode(countryCodeTwoDigit); 
System.out.println(regionCode2);

String regionCode3 = util.getRegionCodeForCountryCode(countryCodeThreeDigit); 
System.out.println(regionCode3);

これを実行すると

ZZ
JP
ZZ

取れたー!(該当するリージョンコードがない場合はZZが出力される)
結局は国コードと電話番号の境界を判断することなく、単純に頭から総当りで解決させた。

コーディングはこの瞬間が堪らねえんだ。
俺は余韻に浸るため、当日の業務を強制終了させた(22時半)



この記事が気に入ったらサポートをしてみませんか?