PHPにてテキストファイルの文字コードをutf-8に統一する方法
QuizGeneratorの生みの親、作り出しッペの西村です。実は、learningBOXの初期開発には参加しておらず、バージョン2.0のリリース頃から本格参戦しました。この度、文字コードの取り扱いに関するPull Requestを受けまして、文字コードについてもう少し語っておかないといけないなと思い、記事にまとめました。
今回の記事では「PHPにてテキストファイルの文字コードをutf-8に統一する方法」をご紹介いたします。本稿もどうぞよろしくお願いします。
目次はこちら
1. Shift-JISは避けられない
2. 文字化けの回避方法について
3. どうやって文字コードを判定するのか?
4. まとめ
Shift-JISは避けられない
learningBOXやQuizGeneratorのようなシステムでは、CSVなどのテキストファイルを受け取ることがあります。現代のWebシステムにおいて、テキストファイルの文字コードはutf-8を使うべきで、それ以外の文字コードは受け入れたくないところなのですが、現実問題として、Shift_JISのファイルが稀によくアップロードされ、不具合として報告されます。
そこで、QuizGeneratorでは、Shift_JISのファイルを受け取った場合、utf-8に変換した上で、処理を継続するようになっています。
Shift-JISとは
JIS規格として標準化された日本語を含む様々な文字を収録した文字コードの一つです。多くのパソコンで標準の日本語用の文字コードとして使用されています。JIS規格で定められたJISコードを改良したもので、JISコードが7ビットで文字を表すのに対して、シフトJISコードは、すべての文字を2バイト(16ビット)で表します。
mb_convert_encodingは信用できない
PHPにはmb_convert_encodingという関数があり、文字コード変換ができます。一見するとこの関数を使うだけで、文字コードを判定し、utf-8に変換できそうですが、実はこの関数、信用できません。
mb_convert_encoding("あああ", "utf-8", "utf-8, sjis-win") とすると、”あああ”がutf-8の場合はそのままutf-8で、Shift_JISであればutf-8に変換してくれるはずなのですが(少なくとも公式ドキュメントを読む限りそうなのですが)、実際にはとんでもないことをされます。
utf-8で渡した文字列をShift_JISとして強引に解釈して壊し、それをutf-8に変換することで、わけのわからない値が帰ってきます。
文字化けの回避方法について
▼「変換元の文字コードを指定すればmb_convert_encodingはちゃんと動く」
mb_convert_encodingは、変換元の文字コードの指定さえすれば正しく動作します。つまり、Shift_JISの場合だけ、Shift_JISからutf-8に変換し、utf-8であれば何もしないという作戦で、基本的にうまく行きます。
mb_convert_encoding("あああ", "utf-8", "sjis-win")
上記のコードは、”あああ”がShift_JISであれば正常に動作します。もともとutf-8の場合はそのままでOKです。
どうやって文字コードを判定するのか?
mb_detect_encodingという関数がありますが、そもそも、これが正しく動作するなら、mb_convert_encodingを使うだけで解決できます。
標準関数がダメなら自力でやるしかない
utf-8の仕様を満たしているかどうかの判定はそれほど難しくないのでやるだけです。
utf-8でなければどうするの?
utf-8でなければ・・・・それはShift_JISとして扱ってしまいましょう。euc-jpとかutf-16のファイルを上げてくる人まではサポートできません。少なくとも、そういうことする人は、エンコードのことわかっているはずなので、セルフサービスでお願いします。そもそも、utf-8を使うよう指定されているのですから。
もう1つの罠
この記事の中でもShift_JISという言葉を何度も使っていますが、現在、Shift_JISと言われているものは、Shift_JISを拡張したWindows-31J(MS932)である場合が多いです。
しかしながら、PHPでShift_JISを指定してしまうと、オリジナルのShift_JISの仕様に定められた文字以外は化けてしまいます。特別な理由がない限り、Shift_JISではなく、Windows-31Jもしくは、sjis-winを指定してください。公式ドキュメントにはWindows-31Jを使えと書かれているにもかかわらず、sjis-winしか掲載されていないという、変な状態になっていますが、少なくとも、PHP7.3.13では、どちらの指定でも問題なく動作しました。
統一された未来は来るのか?
20年ほど前。私が初めてWebプログラミングを始めたころは、UTF-8は標準的でなく、文字化けは日常茶飯事でした。2020年現在、多くのWebサイトはutf-8が採用され、海外のサイトを閲覧してもほとんど文字化けすることはなくなりました。Webの世界においては、utf-8への統一が完了しつつあるといってもよさそうです。
スマートフォンに関しては、utf-8普及後の世界で生まれたものなので、utf-8前提で作られています。(そのため、Shift_JISなど、それ以外のエンコードのファイルで文字化けしがちです)その一方、Windowsでやり取りされるファイルはShift_JISであることが少なくありません。
まとめ
今回の記事では「PHPにてテキストファイルの文字コードをutf-8に統一する方法」をご紹介しました。我々は、日本発の企業であり、日本人にとっての使いやすさも大切にしていきたいと考えているため、もうしばらく、Shift_JISのことは忘れず開発していこうと思います。(IE11は本当に忘れたいのですが・・)