shift-jis と utf-8 の混在問題に関する記事(リンクリスト)に戻る
先日の「BOMの有無を判別し、UTFを読み分ける PHP7.4 のサンプルコード」に引き続き、
こんどは「書き込み」版を掲載する。
shift-jis と、BOM有りの UTF-8, UTF-16LE, UTF-16BE, UTF-32LE, UTF-32BE をテキストファイルに書き出すサンプルソースコードだ。
BOMなし UTF には対応していない。
改行コードは Windows の「CR+LF」と、Linux の「LF」の両方に対応しており、
自動で現在の実行環境に合わせるように出来ている。
前回の「読み込み」版と同様に、コンソールから動作させる CLI プログラムだ。
読み込み処理と違って、こちらは PHP ライブラリのバグ対応などないので、比較的簡単である。
全ソースを掲載した後、コードの解説を行う。
PHP7.4用の全サンプルソースコード
<?php
//--- require_once begin ---
mb_language("Japanese");
//Ecoding Name
define('SJIS', 'Windows-31J');
define('UTF8', 'UTF-8');
define('UTF16LE', 'UTF-16LE');
define('UTF16BE', 'UTF-16BE');
define('UTF32LE', 'UTF-32LE');
define('UTF32BE', 'UTF-32BE');
mb_internal_encoding(UTF8);
//mb_http_output(UTF8);
//Encoding BOM length.
define('BOM_UTF8', "xefxbbxbf");
define('BOM_UTF16LE', "xffxfe");
define('BOM_UTF16BE', "xfexff");
define('BOM_UTF32LE', "xffxfex00x00");
define('BOM_UTF32BE', "x00x00xfexff");
//Encoding CR+LF
define('CRLF_SJIS', "x0Dx0A");
define('CRLF_UTF8', "x0Dx0A");
define('CRLF_UTF16LE', "x0Dx00x0Ax00");
define('CRLF_UTF16BE', "x00x0Dx00x0A");
define('CRLF_UTF32LE', "x0Dx00x00x00x0Ax00x00x00");
define('CRLF_UTF32BE', "x00x00x00x0Dx00x00x00x0A");
//Encoding LF
define('LF_SJIS', "x0A");
define('LF_UTF8', "x0A");
define('LF_UTF16LE', "x0Ax00");
define('LF_UTF16BE', "x00x0A");
define('LF_UTF32LE', "x0Ax00x00x00");
define('LF_UTF32BE', "x00x00x00x0A");
//--- require_once end ---
function WriteBOMTextFile($fileName, $textArray, $encoding){
$allLen = 0;
$internalEncoding = mb_internal_encoding();
$bom = '';
$crlf = '';
$isCrlf = TRUE;
//Check CR+LF.
if(PHP_EOL == CRLF_UTF8){
$isCrlf = TRUE;
}
else{
$isCrlf = FALSE;
}
//Open File.
$fp = fopen($fileName, 'w');
if($fp == FALSE){
echo "Error fopen($fileName) !" . PHP_EOL;
return ;
}
//Init BOM.
switch ($encoding){
case UTF8:
$bom = BOM_UTF8;
if($isCrlf)
$crlf = CRLF_UTF8;
else
$crlf = LF_UTF8;
break;
case UTF16LE:
$bom = BOM_UTF16LE;
if($isCrlf)
$crlf = CRLF_UTF16LE;
else
$crlf = LF_UTF16LE;
break;
case UTF16BE:
$bom = BOM_UTF16BE;
if($isCrlf)
$crlf = CRLF_UTF16BE;
else
$crlf = LF_UTF16BE;
break;
case UTF32LE:
$bom = BOM_UTF32LE;
if($isCrlf)
$crlf = CRLF_UTF32LE;
else
$crlf = LF_UTF32LE;
break;
case UTF32BE:
$bom = BOM_UTF32BE;
if($isCrlf)
$crlf = CRLF_UTF32BE;
else
$crlf = LF_UTF32BE;
break;
default:
$bom = '';
if($isCrlf)
$crlf = CRLF_SJIS;
else
$crlf = LF_SJIS;
break;
}
//Write BOM.
if(strlen($bom) > 0){
$len = fwrite($fp, $bom);
if($len == FALSE){
echo "Error fwrite(bom) !" . PHP_EOL;
fclose($fp);
return FALSE;
}
}
//Loop Of Row.
foreach ($textArray as $text){
//Convert Encoding.
$text = mb_convert_encoding($text, $encoding, $internalEncoding);
//Write Text.
$len = fwrite($fp, $text . $crlf);
if($len == FALSE){
echo "Error fwrite(text) !" . PHP_EOL;
fclose($fp);
return FALSE;
}
$allLen += $len;
}
//Close File.
fclose($fp);
return $allLen;
}
//Init parameters.
$textArray = array(
"ABCDE.12345."
,"!#$%.あいうえお."
,"アイウエオ.アイウエオ."
,"日本漢字,欄廊俠俱."
,"China产乡."
,"Taiwan說姊."
,"Korea희편."
);
//$encoding = SJIS; //SJISでは英語以外の外国語は文字化けします。
$encoding = UTF8;
//$encoding = UTF16LE;
//$encoding = UTF16BE;
//$encoding = UTF32LE;
//$encoding = UTF32BE;
$fileName = 'output.txt';
//Call Function.
$wlen = WriteBOMTextFile($fileName, $textArray, $encoding);
if($wlen == FALSE){
echo "WriteBOMTextFile Error !" . PHP_EOL;
}
else{
echo "Write size = $wlen" . PHP_EOL;
}
//All End.
サンプルコードの解説
前回同様、ソースの始めの部分の「//— require_once begin —」から「//— require_once end —」で囲まれた部分は、別のソースファイルに移動して「require_once()」関数などでインクルードして使用できるように、コードを書いている。
前回の「読み込み」版との内容と重複する部分もあるので、インクルードを統合する場合は、重複分は共有してください。
同じ定数の値は同じです。
ヘッダー定数
読み込み版と同様に「//Ecoding Name」の部分は、文字エンコーディングの名前を定義している。
//Encoding CR+LF
define('CRLF_SJIS', "x0Dx0A");
define('CRLF_UTF8', "x0Dx0A");
define('CRLF_UTF16LE', "x0Dx00x0Ax00");
define('CRLF_UTF16BE', "x00x0Dx00x0A");
define('CRLF_UTF32LE', "x0Dx00x00x00x0Ax00x00x00");
define('CRLF_UTF32BE', "x00x00x00x0Dx00x00x00x0A");
これは Windows版の改行コードを定義している。
//Encoding LF
define('LF_SJIS', "x0A");
define('LF_UTF8', "x0A");
define('LF_UTF16LE', "x0Ax00");
define('LF_UTF16BE', "x00x0A");
define('LF_UTF32LE', "x0Ax00x00x00");
define('LF_UTF32BE', "x00x00x00x0A");
こちらは Linux版の改行コードだ。
Mac も同様。
メイン処理 WriteBOMTextFile 関数
メイン処理の WriteBOMTextFile 関数を定義している。
引数で指定したファイル名のファイルに、第二引数の文字列配列の内容を、文字列ごとに改行して、書き込む。
第三引数で文字エンコーディング名を指定する。
返値に書き込みバイト数を返す。
例外処理は書いていない。
一部、エラーの時はメッセージを表示して終了するようにしてはいる。
function WriteBOMTextFile($fileName, $textArray, $encoding){
$fileName は、書き込みファイル名
$textArray は、文字列配列であり、この配列の文字列ごとに改行して書き込む。
$encoding は、書き込みテキストの文字エンコーディングである。
基本的な処理の枠組みは以下のような処理になる。
ファイルを開いて、BOMを書き込み、第二引数の文字列配列の数だけループする。
ループでは文字列ごとに文字エンコーディングを変換し、文字列の末尾に改行コードを追加して、行を書き込む。
ループが終わればファイルをクローズする。
$fp = fopen($fileName, 'w');
$len = fwrite($fp, $bom);
foreach ($textArray as $text){
$text = mb_convert_encoding($text, $encoding, $internalEncoding);
$len = fwrite($fp, $text . $crlf);
$allLen += $len;
}
fclose($fp);
return $allLen;
詳細の解説をする。
$internalEncoding = mb_internal_encoding();
内部文字エンコーディングを取得する。
ソースの先頭で「mb_internal_encoding(UTF8);」と宣言しているので、この値は「UTF8」である。
その他は、必要な変数の初期値である。
//Check CR+LF.
if(PHP_EOL == CRLF_UTF8){
$isCrlf = TRUE;
}
else{
$isCrlf = FALSE;
}
現在実行している環境が、Windowsの改行か、Linux や Mac の改行が判定している。
//Open File.
$fp = fopen($fileName, 'w');
if($fp == FALSE){
echo "Error fopen($fileName) !" . PHP_EOL;
return ;
}
ファイルを開いている。開けなければ終了する。
//Init BOM.
switch ($encoding){
case UTF8:
$bom = BOM_UTF8;
if($isCrlf)
$crlf = CRLF_UTF8;
else
$crlf = LF_UTF8;
break;
case UTF16LE:
$bom = BOM_UTF16LE;
if($isCrlf)
$crlf = CRLF_UTF16LE;
else
$crlf = LF_UTF16LE;
break;
case UTF16BE:
$bom = BOM_UTF16BE;
if($isCrlf)
$crlf = CRLF_UTF16BE;
else
$crlf = LF_UTF16BE;
break;
case UTF32LE:
$bom = BOM_UTF32LE;
if($isCrlf)
$crlf = CRLF_UTF32LE;
else
$crlf = LF_UTF32LE;
break;
case UTF32BE:
$bom = BOM_UTF32BE;
if($isCrlf)
$crlf = CRLF_UTF32BE;
else
$crlf = LF_UTF32BE;
break;
default:
$bom = '';
if($isCrlf)
$crlf = CRLF_SJIS;
else
$crlf = LF_SJIS;
break;
}
第三引数で指定された、文字エンコーディングに合わせて、BOMの値と改行コードを設定している。
//Write BOM.
if(strlen($bom) > 0){
$len = fwrite($fp, $bom);
if($len == FALSE){
echo "Error fwrite(bom) !" . PHP_EOL;
fclose($fp);
return FALSE;
}
}
BOMを書き込む。
shift-jis の場合は書き込まない。
//Loop Of Row.
foreach ($textArray as $text){
//Convert Encoding.
$text = mb_convert_encoding($text, $encoding, $internalEncoding);
//Write Text.
$len = fwrite($fp, $text . $crlf);
if($len == FALSE){
echo "Error fwrite(text) !" . PHP_EOL;
fclose($fp);
return FALSE;
}
$allLen += $len;
}
第二引数で指定された、文字列配列の配列数だけループして、文字列を一行づつファイルに書き込む。
文字列ごとに、文字エンコーディングを変換し、末尾に改行コードを追加し、書き込む。
書き込みバイト数は累積加算していく。
//Close File.
fclose($fp);
return $allLen;
}
ファイルを閉じて終了する。
書き込みバイト数を返す。これはBOMのバイト数も含む。
これ以降は呼び出し側の処理だ。
//Init parameters.
$textArray = array(
"ABCDE.12345."
,"!#$%.あいうえお."
,"アイウエオ.アイウエオ."
,"日本漢字,欄廊俠俱."
,"China产乡."
,"Taiwan說姊."
,"Korea희편."
);
//$encoding = SJIS; //SJISでは英語以外の外国語は文字化けします。
$encoding = UTF8;
//$encoding = UTF16LE;
//$encoding = UTF16BE;
//$encoding = UTF32LE;
//$encoding = UTF32BE;
$fileName = 'output.txt';
WriteBOMTextFile 関数の呼び出し用の引数を用意する。
$fileName 書き込みファイル名。
$textArray 書き込む内容の文字列配列。一文字列が一行になる。
$encoding 書き込み文字エンコーディングを指定する。
//Call Function.
$wlen = WriteBOMTextFile($fileName, $textArray, $encoding);
if($wlen == FALSE){
echo "WriteBOMTextFile Error !" . PHP_EOL;
}
else{
echo "Write size = $wlen" . PHP_EOL;
}
//All End.
WriteBOMTextFile関数を呼び出し、書き込みバイト数を表示して終了する。
エラーの場合は、エラーメッセージを表示する。
以上、読み込みと違って、簡単だったと思う。
要点は文字エンコーディングごとの改行コードの違いに対応している点だ。
このコードがお役に立てば幸いだ。