Ruby
UTF-8
コマンドプロンプト
windows7
Windows-31J

Windows7におけるUTF-8の文字化けの対処法

↓これをやってみようかと思っていたのですが、本日はそれ以前のお話です。

「Rubyで英語記事に含まれてる英単語を数えて出現数順にソートする」をカッコよく書いてみた - Qiita

よっしゃー作るぞーと、試しにinput.txtをtext変数に入力して、出力。

そしたら出ました、文字化け。


文字化けはなぜ起こるのか

GitHubからダウンロードしてきたテキストファイルは当然の如くUTF-8形式なんですよね。

そしてWindowsではご存知の通り、Windows-31Jという素敵な文字コードが採用されているわけです。

コンピュータは二進数のビットを行ったり来たりさせているだけですから、文字コードとはそのビット列の解釈の仕方です。

その解釈の仕方がそれぞれの文字コードで違うため、読み手の意図しない文字に解釈されてしまって文字化けが起こるのです。ざっくりすぎますね。


extract_word.rb

# encoding: utf-8

text = File.read("../test/input.txt")
p text.encoding
p text


#<Encoding:Windows-31J> 

"?ソInterior design and decorating resource Houzz is the overall best Android ap
p of the year, according to Google, which this evening announced the results of
the first-ever Google Play Awards at its developer conference, Google I/O. While
in previous years, the company had rounded up large numbers of apps for 窶廝est
 (略)
d, the smart airfare prediction engine Hopper won 窶廝est Standout Startup窶\x9D
and millennial-focused investing app Robinhood grabbed the win for 窶廝est Use
of Material Design.窶\x9D\n\n"

んー、エンコードすらできていない、、、?


解決編

調べてみると僕の手元にあるinput.txtが勝手にBOM有りUTF-8ファイルになっていたらしいのです、、、え、爆弾???


バイトオーダーマーク (英: byte order mark) あるいはバイト順マーク(バイトじゅんマーク)は通称BOM(ボム)といわれる、Unicodeの符号化形式で符号化したテキストの先頭につける数バイトのデータのことである。このデータを元にUnicodeで符号化されていることおよび符号化の種類の判別に使用する。

出典: フリー百科事典『ウィキペディア(Wikipedia)』


BOMの情報が付加されていたから、それも文字の情報として誤って解釈してしまって文字化けが起きていたということですね。

なんと、Windowsのメモ帳やExcelでは頼んでもいないのにご丁寧にBOMを付けてくれるらしいのです!

……おっと、こんな厭味な言い方をしてはいけませんね。Microsoft社に悪気はないんです、たぶん。

ちなみにLinuxやMacではBOMを付けられる心配はありません。いいなー。

解決方法は、File.readの第二引数に'r:BOM|UTF-8'を追加するだけ。

こうしてBOM有りであることを明記することで、BOMのデータを読み飛ばしてくれるようになるそうです。


extract_word.rb

# encoding: utf-8

text = File.open("../test/input.txt", 'r:BOM|UTF-8').read
p text.encoding
p text


#<Encoding:UTF-8>

"Interior design and decorating resource Houzz is the overall best Android app o
f the year, according to Google, which this evening announced the results of the
first-ever Google Play Awards at its developer conference, Google I/O. While in
previous years, the company had rounded up large numbers of apps for \u201CBest
 (略)
axy of Heroes.\n\nFrom the startup world, the smart airfare prediction engine Ho
pper won \u201CBest Standout Startup\u201D and millennial-focused investing app
Robinhood grabbed the win for \u201CBest Use of Material Design.\u201D\n\n"

惜しい!

ところどころにUnicodeの名残があるのですが、お前はどっからきた???

まあでもあとは使ってる記号も少ないから、

 u201C → 0xE2 0x80 0x9C(e2809c)

 u201D → 0xE2 0x80 0x9D(e2809d)

 u2019 → 0xE2 0x80 0x99(e28099)


に変換しちゃえばいいだけなんですけど。なんか『力技』って感じで嫌じゃないですか。

コマンドプロンプトが怪しい、、、と睨んだらドンピシャっぽかったです。

$chcpでコマンドプロンプトの文字コードを確認すると、932(Shift-JIS)になっています。

Shift-JISはWindows-31Jの親みたいなもので、ここでは同じものと考えてしまいましょう。

(追記:scivolaさんからCP 932とWindows-31Jは同じものだとご指摘いただきました!

そして、Shift-JISとWindows-31Jは厳密に言うと違うものです、申し訳ございませんでした、、、

詳しくはこちらをご覧ください)

$chcp 6500165001(UTF-8)に変更しましょう。

コマンドプロンプトはまず疑うべきポイントでしたね、うっかりしていました。

$chcp

現在のコード ページ: 932
$chcp 65001
--------------------------------------------------------------------------------
$chcp
Active code page: 65001

$ruby extract_word.rb
#<Encoding:UTF-8>
"Interior design and decorating resource Houzz is the overall best Android app o
f the year, according to Google, which this evening announced the results of the
first-ever Google Play Awards at its developer conference, Google I/O. While in
previous years, the company had rounded up large numbers of apps for “Best of”
 (略)
e startup world, the smart airfare prediction engine Hopper won “Best Standout S
tartup” and millennial-focused investing app Robinhood grabbed the win for “Best
Use of Material Design.”\n\n"

できたー。


まとめ

文字化けが起きたらどこかで文字コードが噛み合っていないということなので、どちらかに統一しなくてはなりません。

今回の場合、テキストファイルがそもそもUTF-8で記述されているわけですから、そちらに合わせるのが無難でしょう。

入力から出力に至るまでの一連の流れに関わる全員で「UTF-8を使うんだぞ!」という合意が必要です。

「いや!俺はWindows-31Jだ!」という頑固者がひとりでもいるから文字化けが起こるのです。

今回の場合、チェック項目はとりあえず、この3点でしょうか。


  • テキストファイル

  • rbファイル

  • コマンドプロンプト

そして、WindowsユーザさんはUTF-8のテキストやxls,xlsx,xlsmファイルに付いているBOMにご用心。

File.open("xxx.txt", 'r:BOM|UTF-8')でBOMを避けて開きましょう。


でも、ひとつ疑問が、、、

この'r:BOM|UTF-8'って、どういう意味なのでしょう。。。

いや、僕のググりがまだ甘いだけなんですけどね。。。

試しに'r:UTF-8'でやってみたらどうなるのでしょう、、、?


extract_word.rb

# encoding: utf-8

text = File.open("../test/input.txt", 'r:UTF-8').read
p text.encoding
p text


(Active code page: 65001)

$ruby extract_word.rb
#<Encoding:UTF-8>

"a?≫i??Interior design and decorating resource Houzz is the overall best Android
app of the year, according to Google, which this evening announced the results
of the first-ever Google Play Awards at its developer conference, Google I/O. Wh
ile in previous years, the company had rounded up large numbers of apps for ca¶a
 (略)
n\nFrom the startup world, the smart airfare prediction engine Hopper won ca¶a≫?
est Standout Startupca¶a?≫and millennial-focused investing app Robinhood grabbed
the win for ca¶a≫?est Use of Material Design.ca¶a??n\n"

(現在のコード ページ: 932)

$ruby extract_word.rb
#<Encoding:UTF-8>
"\uFEFFInterior design and decorating resource Houzz is the overall best Android
app of the year, according to Google, which this evening announced the results
of the first-ever Google Play Awards at its developer conference, Google I/O. Wh
ile in previous years, the company had rounded up large numbers of apps for \u20
 (略)
rs Galaxy of Heroes.\n\nFrom the startup world, the smart airfare prediction eng
ine Hopper won \u201CBest Standout Startup\u201D and millennial-focused investin
g app Robinhood grabbed the win for \u201CBest Use of Material Design.\u201D\n\n
"

Windows-31Jだと'r:BOM|UTF-8'と結果は変わらないのですが、UTF-8に変更すると見たこともない文字化けが起きてしまっています。

……えーっと、これは一体どういうことなのでしょうか。笑

Qiitaの記事で丸投げ、なんてあっていいものなのか分かりませんが、

分かる方、教えていただけたら嬉しいです!

(追記:コメントでscivolaさんが解説してくださっています! 本記事の何百倍も有益なのでそちらをご覧ください笑)


参考

https://qiita.com/KitaitiMakoto/items/2e2d425e439d4b16a279