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);
がコンソールに出力され、無事に国コードを除いた電話番号が取れた。
めでたしめでたし。
このままじゃ使えねーじゃんと気付く
このコードを作った直後に気づいた。
parseメソッドの引数は、電話番号とリージョンコード。
電話番号は手元にあるけど、リージョンコードが無い。
テストコードは決め打ちで「JP」を入れているから動くが、サービスが稼働したらどの国の電話番号が入ってくるか不明。
何とかして動的にリージョンコードを取得する必要がある。
唯一の希望は、この案件の仕様として電話番号は必ず国コード付きで連携されるということ。
その代わりに絶望は、国コードと電話番号の境界をプログラムで判断する必要があること。
国コードは特殊なものを除いて+記号と1~3桁の数字で構成されており、0パディングは無い。
日本:+818011112222
アメリカ:+18011112222
上記の電話番号だと、人間なら日本は+81ね、アメリカは+1ね、残りは電話番号ね、って知っていれば判断できるけど。
JAVAくんはそんなの知らないから…
これ無理ゲーじゃん?
でも「このライブラリを使うしかねぇ」と思って、何とかしようとライブラリのソースを読み込んだ。
とりあえず「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が出力される)
結局は国コードと電話番号の境界を判断することなく、単純に頭から総当りで解決させた。
コーディングはこの瞬間が堪らねえんだ。
俺は余韻に浸るため、当日の業務を強制終了させた(22時半)