SJISの漢字で第一水準と第二水準だけ入力可能にしたい
題名だけだとかなりアバウトな要件ですが。。。
現在担当しているWEBシステムは、HTMLコンテンツもUTF-8で規定しており、バックエンドのDBもユニコードの設定になっています。
導入先がすべてユニコードでOKと言ってくれれば幸せなのですが、まだまだそうはいかないのが現状です。
今回、導入先の周辺システムがすべてShift_JIS前提で構築されているとのことで、I/Fする際は文字コード変換しなければ
ならないと思っていたのですが、こちらが導入するシステムのDBに格納される漢字について注文がつきました。
「第一水準と第二水準以外の漢字は、DBに格納しないで欲しい」
なんだか、そういうことになってしまったようで。。。
そうは言っても、DBはユニコードで格納するように設定していますので、結局I/F時には文字コード変換が発生しますけど。。。
仕方なくDBに格納する前にチェックして、第一水準と第二水準以外の漢字が含まれていた場合はエラーにするように
コーディングするようになったみたいです。
この要件自体はよくあることだと思っていて、WEBで検索すればかなりの数がヒットすると思っていたのですが、
「そのものズバリ!」というものが検索できませんでした。
要件的には、以下のもの以外がNGということだと思います。
- 半角英数OK
- 全角ひらがな、カタカナOK
- 第一水準、第二水準範囲の漢字OK
とうことで、以下のようなサンプルを作成してみました。
先に断っておきますけど(←多分未来の自分)、各種チェックに使用している境界値は、その時の要件によって変えてくださいね。
例えば下のコードの全角ひらがな、カタカナチェックに使用している境界(0x8140〜0x8490)は相当アバウトです。
String s = "てすと"; boolean isOk = true; byte[] bt = null; try { bt = s.getBytes("Shift_JIS"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } for (int i = 0; i < bt.length; i++) { byte firstByte = bt[i]; String sFirst = Integer.toHexString(firstByte & 0xff); // 半角チェック if (sFirst.compareToIgnoreCase("20") >= 0 && sFirst.compareToIgnoreCase("80") < 0) { // OK. } else { byte secondByte = bt[i + 1]; String doubleByteStr = sFirst.concat(Integer.toHexString(secondByte & 0xff)); // 全角ひらがな、カタカナチェック if (doubleByteStr.compareToIgnoreCase("8140") >= 0 && doubleByteStr.compareToIgnoreCase("8490") <= 0) { // OK. } // 第一・第二水準チェック else if ((doubleByteStr.compareToIgnoreCase("889f") >= 0 && doubleByteStr.compareToIgnoreCase("9872") <= 0) || (doubleByteStr.compareToIgnoreCase("989f") >= 0 && doubleByteStr.compareToIgnoreCase("fc4b") <= 0)) { // OK. } else { isOk = false; break; } i++; } }
こんな感じでOK〜と思っていたら落とし穴がありました。
以下の部分なのですが、
try { bt = s.getBytes("Shift_JIS"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); }
getBytesって、ユニコードには存在するけど、Shift_JISには無いようなコードが含まれていたら何かしら例外が発生すると
思ってテストしていたのですが、無い場合は0x3f(つまり"?")に変換してしまうようで、このままだと全く駄目です。
苦肉の策で以下のようなロジックを追加することにしました。
String uniStr = null; String sjisStr = null; int uni_hatena = 0; int sjis_hatena = 0; try { uniStr = new String(s.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } for (int count = 0; count < uniStr.length(); count++) { if ("?".compareTo(uniStr.substring(count, count + 1)) == 0) uni_hatena++; } try { sjisStr = new String(s.getBytes("Shift_JIS"), "Shift_JIS"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } for (int count = 0; count < sjisStr.length(); count++) { if ("?".compareTo(sjisStr.substring(count, count + 1)) == 0) sjis_hatena++; } if (uni_hatena != sjis_hatena) { System.out.println("NG"); }
なにか、すごくかっこ悪い。。。
そしてこのロジック、正しいのか???
Shift_JIS表については、以下を参考にさせて頂きました。
http://www.seiai.ed.jp/sys/text/java/shiftjis_table.html
http://charset.7jp.net/sjis.html
他にもっといい方法ないか模索中です。