Search code examples
phpbinaryextractunpack

PHP extract data from binary file with structure


enter image description here

I have i file with structure in image. I want to extract data to array from that:

function get_data($file, $number)
{
    if(!$fp = fopen ($file, 'rb')) return 0;
    $fsize = filesize($file);
    if(!$data = fread ($fp, $fsize)) return 0;
    $data_format=
        '@100/'.
        'smember_id/'.
        'cmember_name_length/'.
        'a' . $member_name_length . 'member_name/'.
        'C100other_data/';
    $data = unpack ($data_format, $data);
    fclose($file);
    return $data;
}

How can I get the $member_name_length from the file? I want to create a function that if user input the $number, it returns a array of $number(th) data.

Thank you.


Solution

  • Since you have a variable-length data blocks, you can read them only sequentially, so in order to read n-th block, you need to read all n first blocks:

    function readDataBlock($f) {
        $data = unpack('nmember_id', fread ($f, 2)); // I assume member_id is n, not s
        if ($data['member_id'] == 0xFFFF) {
            throw new \Exception('End of file');
        }
        $data = array_merge($data, unpack('Cmember_name_length', fread ($f, 1)));  //again, it must be C, not c, as I can't imagine negative length.
        $data = array_merge($data, unpack('a*member_name', fread ($f, $data['member_name_length'])));  // be sure you understand how a differs from A
        return array_merge($data, unpack('C100other_data', fread ($f, 100))); // are you sure C100 is what you want here?
    }
    
    
    function get_data($file, $number)
    {
        if(!$fp = fopen ($file, 'rb')) return 0;
    
        fread ($fp, 100);  //skip header
    
        for($n = 0; $n <= $number; $n++) {
            $data = readDataBlock($fp); // read single member
        }
    
        fclose($fp);
        return $data;  //return the n-th member
    }
    

    If the file is small enough to fit into memory, it might be better to read it once and return n-th member from memory:

    $data = [];
    while(true) {
        try {
            $data[] = readDataBlock($fp);
        } catch(\Exception $e) {
            break;
        }
    }
    
    
    function get_data(&$data, $number)
    {
        return $data[$number];
    }