前回、JavaでASCIIの制御文字の取り扱いについて記事にしました。
そして今回は、前回の対策を応用して、もっと厄介なサロゲートペア文字をJavaでゴニョゴニョする方法を2つ程ご紹介したいと思います。
サロゲートペア文字とは?
簡単に説明すると、全世界で文字コードを統一するため、Unicodeという文字コードが一般的に主流になっています。
通常1文字を2バイトで表現するのですが、2バイト(65536文字)では文字が足りなくなってしまったため、1文字を4バイトで表現する方法として「サロゲートペア」が誕生しました。
サロゲートペアは何が問題なの?
今ではそんなに意識することはなくなりましたが、古いシステムや古いデータを取り扱う際に問題が発生してしまいます。
と言うのも、昔はUnicode以外の文字コードも多く使われていました。
それ故にバグ、いわゆる文字化けが発生してしまうのです。
これによって、データの不整合が起きたり、プログラムが想定したとおりに動かなかったりします。
サロゲートペア文字一覧
実際、サロゲートペア文字にはどのような文字があるのか、Qiitaの記事をご覧ください。
サロゲートペア文字を置換・削除する方法
前回のASCII制御文字の方法を応用して、2つの方法でサロゲートペア文字を変換していきます。
以下の2つの例では、「𩸽(ほっけ)」サロゲートペア文字を「口(くち)」という漢字に置換します。
1. 正規表現を利用する
前回ご紹介したJavaの正規表現一覧が掲載されている海外のサイトを見ると「Unicode Categories」にサロゲートペア文字を判定する正規表現がありました。
・・・
p{Cs} or p{Surrogate}: one half of a surrogate pair in UTF-16 encoding.
・・・
p{Cs}
かp{Surrogate}
どちらでも同じ動きをするようですね。
これをreplaceAll()
で使うとこんな感じです。
String str = "𩸽";
str.replaceAll("\p{Cs}", "口");
そして、変換してみた結果がこれ。
口口
2文字になった!?
なるほど…。
前述の通り、通常1文字を2バイトで表現しているので、1文字4バイトであるサロゲートペア文字は2文字扱いになってしまうようです。
そこで、上位サロゲートと下位サロゲートに分けて変換することにします。同じく上記のサイトを見ると「Unicode Blocks」の92.と94.に上位サロゲート(InHigh_Surrogates
)と下位サロゲート(InLow_Surrogates
)を判定する正規表現がありました。
この辺りです。
・・・
92. p{InHigh_Surrogates}: U+D800–U+DB7F
93. ・・・
94. p{InLow_Surrogates}: U+DC00–U+DFFF
・・・
上位サロゲートだけ、または下位サロゲートだけ変換してしまうと、どちらかの2バイト分が残ってしまうので、片方は削除します。
String str = "𩸽";
str.replaceAll("\p{InHigh_Surrogates}", "口");
str.replaceAll("\p{InLow_Surrogates}", "");
上記の例では、上位サロゲートを置換し、下位サロゲートは空文字にしました。
当然、結果は1文字で思った通りになりました。
口
ただ削除するだけの場合は、前者の方法で1発で便利ですね。
置換する場合は後者の方が応用が利くかもしれません。
2. 1文字ずつ判定する
続いて、こちらも前回の応用で、1文字ずつループしてサロゲートペア文字を判定します。
判定方法はJavaのCharacterクラスに用意されている以下の関数を使います。
- isSurrogate()
- isHighSurrogate()
- isLowSurrogate()
詳しくはこの辺りを参考にしてね!
前回同様、関数を作ってみました!
public static String replaceSurrogate(String str) {
if (str == null) {
return str;
}
StringBuffer sb = new StringBuffer();
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i); if (Character.isSurrogate(str.charAt(i))) {
sb.append("口");
}
String result = sb.toString();
return result;
}
もちろん、これだけだと正規表現と同じように「口口」2文字になってしまいますね。
ですので、こちらも同じように上位サロゲートのみ置換、下位サロゲートは空文字にしてしまいましょう。
以下、抜粋。
・・・
if (Character.isHighSurrogate(str.charAt(i))) {
sb.append("口");
}
if (Character.isLowSurrogate(str.charAt(i))) {
sb.append("");
}
・・・
または、上位サロゲートを置換した後、ループを進めて下位サロゲートを無視する方法もありますね!
・・・
if (Character.isHighSurrogate(str.charAt(i))) {
sb.append("口");
i++;
}
・・・
以上、こんな感じでいかがでしたでしょうか。
方法は1つじゃないですね。
前回も書きましたが、正規表現は遅いらしいですが、コンマ何秒の世界なので自分は好きな方で良いと思います。
- Original:https://minory.org/java-surrogate.html
- Source:Minory
- Author:管理者
Amazonベストセラー
Now loading...