1. バイナリファイルの読み込み
  2. 変数の中身確認
  3. バイナリファイルのデータを配列にする

バイナリファイルの読み込み

PHPでバイナリファイルを読み込む方法は、fopen、fseek、fread、fclose関数を使う方法と、file_get_contents関数を使う方法があります。 変数に読み込まれたデータの型はstring型になります。参考 バイナリファイルから読み込んだ場合の変数の型

  • fopen、fseek、fread、fclose関数

  • fopen関数でバイナリファイルを開く場合、第二引数のmodeに、"b"を追加する必要があります。 読み込みだけの場合は"rb"、上書きの場合は"wb"などを指定します。 バイナリファイルの指定位置から、必要な長さだけ取得する場合は、以下のようにします。

    例 バイナリファイル先頭の16バイトから、8バイト取り出す場合
    $offset = 16;  // バイナリファイル先頭の16バイト目から取得する
    $maxlen = 8;   // 8バイト取得する
    
    $fp = fopen($path, 'rb');
    if ($fp === false) {
        throw Exception("Can not open file: $jpg");
    }
    fseek($fp, $offset);
    $data = fread($fp, $maxlen);
    fclose($fp);

    fopen(PHPマニュアル) fread(PHPマニュアル) frseek(PHPマニュアル)

  • file_get_contents

  • file_get_contents関数は、fopen関数などより簡単にファイルを読み込めます。 ファイル全部を読み込む場合は、以下のようにファイルパスだけを指定します。

    $data = file_get_contents($path);
    if ($data === false) {
        throw new Exception("Can not read file: $path");
    }

    一部のデータだけを取得する場合は、第4引数に位置、第5引数に取得する長さを指定します。 例えば、バイナリファイル先頭の16バイト目から、8バイトだけ取り出す場合は以下のようになります。

    $offset = 16;  // バイナリファイル先頭の16バイト目から取得する
    $maxlen = 8;   // 8バイト取得する
    
    $data = file_get_contents($path, false, null, $offset, $maxlen);
    if ($data === false) {
         throw new Exception("Can not read file: $path");
    }

    file_get_contents(PHPマニュアル)

変数の中身確認

変数とバイナリデータの関係の確認

  • 変数に16進数を代入

  • 変数に16進数を代入した場合、型は整数型になります。

    $data = 0xff;
    var_dump($data);      //=>  int(255)
    
    $data = 0xffd8;       // 16進数のffd8を、10進数にすると65496
    var_dump($data);      //=>  int(65496)
    
    $data = 0xffd8f7d6;   // 16進数のffd8を、10進数にすると65496
    var_dump($data);      //=>  int(4292409302)
    
    

  • バイナリファイルから読み込んだ場合の変数の型

  • fread関数やfile_get_contents関数でバイナリファイルを読み込んだ場合、変数にはバイナリデータが格納されますが、型はstring型になります。

    例. 先頭の16バイトが以下のような、jpegの画像ファイルの場合

    $ hexdump -vC -n 16 sakura01.jpg
    00000000  ff d8 ff e0 00 10 4a 46  49 46 00 01 01 01 00 48  |......JFIF.....H|
    16進表示 :  ff d8 ff e0 00 10 4a 46  49 46 00 01 01 01 00 48
    ASCII表示   .  .  .  .  .  .  J  F   I  F  .  .  .  .  .  H
    fread関数での読み込み結果は以下のようになります。
        $jpg = 'sakura01.jpg';
        $fp = fopen($jpg, 'rb');
        if ($fp === false) {
            throw Exception("Can not open file: $jpg");
        }
    
        // 先頭から1バイト
        fseek($fp, 0);
        $data = fread($fp, 1);
        var_dump($data);             // string(1) "?"
        var_dump(bin2hex($data));    // string(2) "ff"
    
        // 先頭から2バイト
        fseek($fp, 0);
        $data = fread($fp, 2);
        var_dump($data);             // string(2) "??"
        var_dump(bin2hex($data));    // string(4) "ffd8"
    
        // 先頭から4バイト
        fseek($fp, 0);
        $data = fread($fp, 4);
        var_dump($data);             // string(4) "????"
        var_dump(bin2hex($data));    // string(8) "ffd8ffe0"
    
        // 先頭から16バイト
        fseek($fp, 0);
        $data = fread($fp, 16);
        var_dump($data);             // string(16) "????JFIFH"
        var_dump(bin2hex($data));    // string(32) "ffd8ffe000104a464946000101010048"
    freadで取得できるデータはバイナリデータですが、型はstringになります。 データが0xFFのような場合、var_dumpで表示すると中身が正しく表示されません。 表示したい場合は、bin2hex関数でバイナリデータをバイナリ文字列に変換します。

    bin2hex (PHPマニュアル)

バイナリファイルのデータを配列にする

fread関数やfile_get_contents関数で取得したデータをbyte単位やlong単位などの配列にする場合、str_split関数か、unpack関数で行います。

参考 unpack関数を使ったバイナリファイル用のサンプルクラス

  • string型の配列で取得(str_split)

  • str_split関数は第二引数に指定したバイト数でデータを分割します。 そのため、バイト単位のデータが入った配列にしたい場合は、第二引数に1を指定します。

    $data = file_get_contents($path);
     $ar = str_split($data, 1);   // 1バイトごとのstring型データが入った配列となります

  • 文字列で16進を表すバイナリ文字列の配列

  • 文字列で16進を表すバイナリ文字列("ff", "1a"など)でバイト単位の配列にしたい場合は、バイナリファイルから読み込んだデータをbin2hex関数でバイナリ文字列に変換後、str_split関数で2バイトごとに分割します。

    $fp = fopen($jpg, 'rb');
    if ($fp === false) {
        throw Exception("Can not open file: $jpg");
    }
    fseek($fp, 0);
    $data = fread($fp, 16);
    
    $ar = str_split(bin2hex($data), 2);   // $ar = array('ff', 'd8', 'ff', 'fe', '00', .... )となります
    注. bin2hex関数でバイナリ文字列に変換後してstr_split関数で分割する場合、1バイト分は2文字になりますので、str_split関数の第二引数に2を指定します。

    bin2hex (PHPマニュアル)

  • int型の配列で取得(unpack)

  • unpack関数で書式に"C*"を指定すると、int型のバイト単位(unsigned char)で配列取得できます。 ただし取得した配列は、0からでなく1から始まる配列となります。

    $data = file_get_contents($path);
    $ar = unpack("C*", $data);   // $ar = array(1 => 255(0xff), 2 => 221(0xdd), 3 => 255(0xff) ...); 

    int型の2バイト単位(unsigned short)で配列を取得したい場合は、"S*"、"n*"、"v*"のどれかを指定します。

    $data = file_get_contents($path);
    $ar = unpack("S*", $data);   // $ar = array(1 => 65496(0xffd8), 2 => 65504(0xffe0), 3 => 16(0x0010) ...); 

  • 連続するデータから異なる長さの複数データ取り出して配列にする

  • 連続するデータから異なる長さの複数データを取り出したい場合は、unpack関数で可能です。

    例 以下のようなjpeg画像ファイルの先頭6バイトを、ビッグエンディアンバイトオーダーで2バイト、4バイトと取り出す

    $ hexdump -vC -n 16 sakura01.jpg
    00000000  ff d8 ff e0 00 10 4a 46  49 46 00 01 01 01 00 48  |......JFIF.....H|
    ビッグエンディアンバイトオーダー(上記データで、0xffd8と0xffe00010)で取り出す場合、unpack関数のフォーマト指定は2バイトに"n"、4バイトに"N"を指定します。 unpack関数のフォーマットでは、複数指定する場合"/"で区切るので、"/"で区切って "nfirst/Nsecond"と指定します。 結果は連想配列で返ってきて、最初の2バイトのキーが"first"、次の4バイトのキーが"second"になります。
    $path = 'sakura01.jpg';
    $fp = fopen($path, 'rb');
    if ($fp === false) {
        throw Exception("Can not open file: $fpathIn");
    }
    
    fseek($fp, 0);
    $data = fread($fp, 4);
    
    // unpack('nfirst/nsecond, $data)の 'n'は16bit、'N'は32bitのデータで、ビッグエンディアンバイトオーダーで扱う
    $val = unpack("nfirst/Nsecond", $data);
    var_dump($val);
    
    //  実行結果
    //  array(2) {
    //    ["first"]=>   int(65496)       (0xffd8)
    //    ["second"]=>  int(4292870160)  (0xffe00010)
    //  }