文をMeCabで単語に分解して、json として返す処理を作ったところ、to_json すると、すべての単語が \\ufffd になってしまうという問題が発生した。

MeCabが返す string に str.force_encoding(Encoding::UTF_8) とすると、ちゃんと to_json することができた。

以下の処理では文字がすべてつぶれてしまう。。

ruby-1.9.2> m = MeCab::Tagger.new
ruby-1.9.2> p m.parse("先週、川崎フロンターレの試合を見に行ったけど、選手がほとんど分からなかったぞ。").split("\n").map{|w| w.split(",")[0].split(' ')[0] }.to_json
"[\"\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\",\"\\ufffd\\ufffd\\ufffd\",\"\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\",\"\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\",\"\\ufffd\\ufffd\\ufffd\",\"\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\",\"\\ufffd\\ufffd\\ufffd\",\"\\ufffd\\ufffd\\ufffd\",\"\\ufffd\\ufffd\\ufffd\",\"\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\",\"\\ufffd\\ufffd\\ufffd\",\"\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\",\"\\ufffd\\ufffd\\ufffd\",\"\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\",\"\\ufffd\\ufffd\\ufffd\",\"\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\",\"\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\",\"\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\\ufffd\",\"\\ufffd\\ufffd\\ufffd\",\"\\ufffd\\ufffd\\ufffd\",\"\\ufffd\\ufffd\\ufffd\",\"EOS\"]"

str.force_encoding(Encoding::UTF_8) することで、正しい文字列の集合を得ることができた

ruby-1.9.2> m = MeCab::Tagger.new
ruby-1.9.2> p m.parse("先週、川崎フロンターレの試合を見に行ったけど、選手がほとんど分からなかったぞ。").split("\n").map{|w| w.split(",")[0].split(' ')[0].force_encoding(Encoding::UTF_8) }.to_json
"[\"\\u5148\\u9031\",\"\\u3001\",\"\\u5ddd\\u5d0e\",\"\\u30d5\\u30ed\\u30f3\\u30bf\\u30fc\\u30ec\",\"\\u306e\",\"\\u8a66\\u5408\",\"\\u3092\",\"\\u898b\",\"\\u306b\",\"\\u884c\\u3063\",\"\\u305f\",\"\\u3051\\u3069\",\"\\u3001\",\"\\u9078\\u624b\",\"\\u304c\",\"\\u307b\\u3068\\u3093\\u3069\",\"\\u5206\\u304b\\u3089\",\"\\u306a\\u304b\\u3063\",\"\\u305f\",\"\\u305e\",\"\\u3002\",\"EOS\"]"

なんで???

ruby 1.9 では Stringが文字コード情報を持っているということで、 str.encoding で文字コード情報を確認

ruby-1.9.2-p180 :020 > m.parse("先週、川崎フロンターレの試合を見に行ったけど、選手がほとんど分からなかったぞ。").split("\n").map{|w| p w.split(",")[0].split(' ')[0].encoding }
#<Encoding:ASCII-8BIT>
#<Encoding:ASCII-8BIT>
#<Encoding:ASCII-8BIT>
#<Encoding:ASCII-8BIT>
#<Encoding:ASCII-8BIT>
#<Encoding:ASCII-8BIT>
#<Encoding:ASCII-8BIT>
#<Encoding:ASCII-8BIT>
#<Encoding:ASCII-8BIT>
#<Encoding:ASCII-8BIT>
#<Encoding:ASCII-8BIT>
#<Encoding:ASCII-8BIT>
#<Encoding:ASCII-8BIT>
#<Encoding:ASCII-8BIT>
#<Encoding:ASCII-8BIT>
#<Encoding:ASCII-8BIT>
#<Encoding:ASCII-8BIT>
#<Encoding:ASCII-8BIT>
#<Encoding:ASCII-8BIT>
#<Encoding:ASCII-8BIT>
#<Encoding:ASCII-8BIT>
#<Encoding:ASCII-8BIT>

MeCab はStringを ASCII で返している。。
なので、文字列はそのままで、文字コード情報をUTF-8 に変換してあげる必要があるようですね。

force_encodingメソッドは、文字列の文字コード情報を引数encodingに切り替えます。引数には文字列かEncodingオブジェクトを指定します。レシーバ自身を変更するメソッドです。戻り値はレシーバ自身です。

force_encodingメソッドは文字コード情報を変更しますが、文字列が持っているバイト列は変更しません。一般的な意味で「文字コードの変換」を行いたいときは、encodeメソッドを使います。


参考: