この間起こったこと
先日、HTMLファイルを取り込んで文字を色々するみたいなことをやっていた時のこと。
String str1 = "hoge hoge";
String str2 = anyElement; // HTMLファイルから取り込んだ"hoge hoge"
System.out.println(str1.equals(str2));
// false
なんとfalseが出力されました。え、false...?
初めはString#equalsのバグか?と思ったのですが、まあそんな訳ないと思うので
色々調べてみました。
試しにcharで見てみる
ということで先ほどの二つの文字列をString#getBytesでUTF-8にしたら以下のようになりました。
str1 : [104, 111, 103, 101, 32, 104, 111, 103, 101]
str2 : [104, 111, 103, 101, -62, -96, 104, 111, 103, 101]
ん?
-62, -96 ...!?
なんやこれ!!!!
 (0xC2, 0xA0);
ノーブレークスペース - Wikipedia
↑0xC2, 0xA0の2バイトで表されるのは「ノーブレークスペース」というやつらしい。
どうもHTMLファイル上のnbspみたいなやつは、UTF-8では通常の半角スペース(0x20)ではなく
0xC2 0xA0の2バイトで表現されるようです。
標準出力に出してもただの半角スペースにしか見えません。罠だ...
通常の半角スペースじゃないと困る
そうなると文字列の処理上困ることがあります。
以下は半角スペースが全てノーブレークスペースの場合です。
String hoge = "a b c".split(" ");
// hoge = ["a b c"]
// ["a", "b", "c"]じゃないの...?
String fuga = "a b c".replaceAll(" ", "d");
// fuga = "a b c"
// "adbdc"じゃないの...?
罠だ...
しかし、例えば以下のようにすれば問題ありません。
public static final byte[] NBSP = {(byte)0xC2, (byte)0xA0};
String hoge = "a b c".split("[ |" + new String(NBSP) + "]");
// hoge = ["a", "b", "c"]
String fuga = "a b c"
.replaceAll("[ |" + new String(NBSP) + "]", "d")
// fuga = "adbdc"
replaceAllもsplitも引数には正規表現をとるので、
[(半角スペース)|(ノーブレークスペース)]とするとどっちかは引っ掛かります。
まとめ
もう誰もノーブレークスペースで悩みませんように...