Help us understand the problem. What is going on with this article?

[PHP]UTF-8で多言語をファイル出力する際に文字化けする

PHPでUTF-8で中国語をテキストファイルに出力すると、開くアプリケーションによって文字化けが起こる事象に遭遇した。

元のプログラム

<?php

$filePath = 'mojibake.txt';
$value = '你好';

$fh = fopen($filePath, 'w');
fwrite($fh, mb_convert_encoding($value, 'UTF-8'));
fclose($fh);

途中の処理は省略している。

$ cat mojibake.txt
你好%

ちゃんと出力されているし、Macのテキストエディタで開いても正しく表示される。しかしこれをExcelで開くと...
スクリーンショット 2020-10-17 5.08.55.png
こんな感じで文字化けしてしまう。

原因

アプリケーションによって、Unicodeの符号化方式が判別できず、UTF-8・UTF-16・UTF-32を判別できないため、文字化けが起きてしまう。

解決策

上を判別させるために、ファイル出力時の先頭にBOMをつける。

BOMとは

BOMとは、Byte Order Markの略称で、Unicodeの符号化方式を判別するために、ファイルの先頭につける数バイトのマーク。

プログラムがテキストデータを読み込む時、その先頭の数バイトからそのデータがUnicodeで表現されていること、また符号化形式(エンコーディング)としてどれを使用しているかを判別できるようにしたものである。
バイトオーダーマーク - Wikipedia

BOMは以下の通り。

符号化形式 エンディアンの区別 BOM
UTF-8 0xEF 0xBB 0xBF
UTF-16 BE 0xFE 0xFF
UTF-16 LE 0xFF 0xFE
UTF-32 BE 0x00 0x00 0xFE 0xFF
UTF-32 LE 0xFF 0xFE 0x00 0x00

今回はUTF-8なので、0xEF 0xBB 0xBFを先ほどのファイルの先頭につけるようにする。

修正したプログラム

<?php

$filePath = 'mojibake.txt';
$value = '你好';

$fh = fopen($filePath, 'w');

// add BOM
fwrite($fh, "\xEF\xBB\xBF");

fwrite($fh, mb_convert_encoding($value, 'UTF-8'));
fclose($fh);

Excelで開いても、正しく表示されるようになった。
スクリーンショット 2020-10-17 5.35.40.png

BOM付かどうかファイル情報を調べる

ファイルにBOMが付いているかはコマンドで調べることができる。

$ file mojibake.txt
mojibake.txt: UTF-8 Unicode (with BOM) text, with no line terminators

BOMが付いているとwith BOMと表示される。

$ file mojibake2.txt
mojibake2.txt: UTF-8 Unicode text, with no line terminators

BOMがない場合は表示されない。

vetra
開発のめも。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away