こんにちは、高木です。
引き続き例によってPHPでC言語の前処理を行う話題です。今回は、バイナリファイルを読み込んで、その内容で配列を作ってみることにします。配列の名前や記憶クラス指定子などはクライアントコードで用意すればいいので、今回は初期化子だけ作ります。
それでは早速コードを見ていきましょう。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<?php function make_array_from_binary_file(string $path): string|bool { $r = '{' . PHP_EOL; try { $stream = fopen($path, 'r'); if (!$stream) return false; while (!feof($stream)) { $data = fread($stream, 16); $r .= ' '; foreach (unpack('C*', $data) as $c) { $r .= sprintf("0x%02x, ", $c); } $r .= PHP_EOL; } $r .= '};' . PHP_EOL; } finally { if ($stream) fclose($stream); } return $r; } ?> |
make_array_from_binary_file関数は、文字通りバイナリファイルから配列を作ります。引数ではファイルのパスを与えます。成功すれば文字列として初期化子のコードを返し、失敗すればfalseを返します。戻り値の型をstring|boolとしていますが、これはPHPの共用体でstring型かbool型のどちらかを返すという意味です。共用体はPHP 8.0で導入された仕様ですので、古いバージョンを使っている場合はエラーになってしまうので注意してください。
さて、コードをざっと見ていただければわかるように、PHPでもファイルの操作はほとんどC言語と同じです。fopen関数でファイルを開いて、最後はfclose関数で閉じます。C言語のfopen関数はデフォルトはテキストモードですがPHPではバイナリモードがデフォルトになります。バイナリデータの読み込みはfread関数を使っていますが、C言語の同名の関数とは引数や戻り値が異なるので注意が必要です。
今回は16バイトずつ読み込んで、それを1行分の初期化子としています。fread関数は文字列として読み込んだ内容を返しますので、前々回も使ったunpack関数で読み込んだ内容を配列に変換しています。そして、sprintf関数で16進数に変換して並べています。sprintf関数は結果を文字列として返すので、C言語の同名関数より便利ですね。
C言語とは異なり、PHPには例外処理があります。実行途中にどんな例外が投げられるかわかりませんので、$streamがリークすることのないよう、finallyの中でfclose関数を呼ぶようにしています。finallyブロックは例外が投げられても投げられなくても必ず実行されます。
こんな感じで、C言語のプリプロセッサでは(C++のテンプレートでも)決して実現できないようなことが、PHPを使えば可能になります。