はるか昔、Shift JIS で随分と苦しんだ気がするのに、Unicode (というか Java の char なので UTF-16BE かな?)を使っていてもサロゲートペアに苦しめられている気がする。
とりあえず、サロゲートペアを無かったことにしたい気分なので、U+FEFF(word breakしない幅ゼロの空白)に置き換えて見ようと思う。
正規表現でやってみる
サロゲートペアの範囲([\\uD800-\\uDFFF]
)を指定したら上手くマッチしてくれなかったので、コードポイントで U+10000 〜 U+10FFFF の範囲を指定しています。
http://d.hatena.ne.jp/miyakawa_taku/20160208/1454942816 で以下の記述をみつけたので、おそらくはマッチしないのが正しい振る舞いなのだろう。
UTF-16を使う場合、先行するサロゲートと後続するサロゲートのペアからなるシーケンスは、マッチにおいて単一のコードポイントとして扱わなければならない。
実際に正規表現で置き換えてみた。
String text = "[\uD83D\uDC27] ペンギン";
String replacement = "\uFEFF";
Pattern pattern = Pattern.compile(String.format("[\\x{%X}-\\x{%X}]",
Character.MIN_SUPPLEMENTARY_CODE_POINT,
Character.MAX_CODE_POINT));
String replaced = pattern.matcher(text).replaceAll(replacement);
System.out.println(" text = " + text); // text = [🐧] ペンギン
System.out.println("replaced = " + replaced); // replaced = [] ペンギン
Character.isSurrogate を使ってみる
単純に char[]
を for
文で回してみる。
ちなみに、これが一番早かった。toCharArray のなかが arraycopy なので、for 文で1つずつ char[i] に値をいれていくより早いのだろうと思う。
char[] replaced = text.toCharArray();
for (int i = 0; i < replaced.length; i++) {
if (Character.isSurrogate(replaced[i])) {
replaced[i] = '\uFEFF';
}
}
System.out.println(" text = " + text); // text = [🐧] ペンギン
System.out.println("replaced = " + new String(replaced)); // replaced = [] ペンギン
別解。個人的にはこっちのほうが好みな書き方。
char[] replaced = new char[text.length()];
for (int i = 0; i < replaced.length; i++) {
char ch = text.charAt(i);
replaced[i] = Character.isSurrogate(ch) ? '\uFEFF' : ch;
}
System.out.println(" text = " + text);
System.out.println("replaced = " + new String(replaced));