php read binary stream of C Language structure struct data file in depth analysis

  • 2020-06-12 08:40:45
  • OfStack

Although php was developed in the C language, I was puzzled that php does not provide direct support for the structure struct.
However, php provides pack and unpack functions, which are used to interconvert binary data (binary data) and php internal data:


string pack ( string $format [, mixed $args [, mixed $...]] )  
 //Pack given arguments into binary string according to format.  
array unpack ( string $format, string $data )  
//Unpacks from a binary string into an array according to the given format. 

Among them, $format is similar to the FORMAT of pack in perl, including the following 1 (Chinese is added by me, some inaccurations are welcome to propose) :
a ES20en-ES21en string, i.e. "\0" as the representation of "null character"
A SPACE-padded string, where the space is used as the representation of a "null character"
h Hex string, low nibble first, ascending bit order
H Hex string, high nibble first, descending order
c signed char, signed single byte
C unsigned char, unsigned single byte
s signed short (always 16 bit, machine byte order)
S unsigned short (always 16 bit, machine byte order)
n unsigned short (always 16 bit, big endian byte order)
v unsigned short (always 16 bit, little endian byte order)
i signed integer (machine dependent size and byte order)
I unsigned integer (machine dependent size and byte order)
l signed long (always 32 bit, machine byte order)
L unsigned long (always 32 bit, machine byte order)
N unsigned long (always 32 bit, big endian byte order)
V unsigned long (always 32 bit, little endian byte order)
f float (machine dependent size and representation)
d double (machine dependent size and representation)
x NUL byte, which is useful for skipping bytes when you actually use it
X Back up one byte, back 1 byte
@NUL-ES86en to absolute position, actually useful for jumping from the beginning to a byte
Practical use: the "\0" (string terminator) in C is not a terminator in php, but is part 1 of the string. Therefore, special processing of "\0" is required to achieve a perfect interrotation of struct and php internal data. Such as char name [10]. If the actual data is "62 69 61 6E 00 62 69 616E00", then name should be "bian" at the fifth position in the C language. With unpack, the name in php is "bian\0bian\0".
1 At the beginning, I used the strpos function to find the position of "\0" and then intercepted substr.

But something happened to Faint, I don't know if it's bug of strpos or bug of substr (in fact, I know from test 1 that I don't want to try), some strings are ok, some strings are empty (i.e. $name == "). Very depressed, then found an strtok function, this is no problem.
It is hard for you to read so much, here is a complete example code of php reading 2 base data stream (C language structure struct data) file:
The first is an example of C's struct definition. To demonstrate this, I'll write a simpler one, which should look fine against the $format format table above:


struct BIANBIAN {  
    char name[10];  
    char pass[33];  
    int  age;  
    unsigned char flag;  
};

For example, there is a "ES130en.dat" file, the content of which is composed of the above N BIANBIAN structures. php code read:

    <?php  
     // According to the below struct determine $format , pay attention to int The type has to do with the machine environment, mine 32 position Linux is 4 A length of   
     $format = 'a10name/a33pass/iage/Cflag';  
     // determine 1 a struct How many bytes of length does it take to just read a single structure   
     $length = 10 + 33 + 4 + 1;  
     // You can also use fopen + fread + fclose , but file_get_contents Because you can mmap , more efficient   
     $data = file_get_contents('file.dat', 'r');  
     for ($i = 0, $c = strlen($data); $i < $c; $i += $length) {  
         $bianbian = unpack("$format", $data);  
         //reference Transfer is php 5 Only then supports, if USES php4 Something else must be done   
         foreach ($bianbian as &$value) {  
             if (is_string($value)) {  
                 $value = strtok($value, "\0");  
             }  
         }  
         print_r($bianbian);  
     }  
    ?>  

pack should be the opposite of unpack.
Attach the C language code to generate the structure file:

    #include <stdio.h>  
    #include <string.h>  

    struct example       
    {      
        char name[10];  
        char pass[33];  
        int  age;  
        unsigned char flag;  
    };  

    int main()     
    {  
        example test;  
        example read;     
        FILE *fp;  

        test.age = 111;     
        test.flag = 10;  
        strcpy(test.name, "Hello World!");  
        strcpy(test.pass, "zbl110119");  

        fp = fopen("file.dat", "w+");  
        if (!fp)  
        {  
            printf("open file error!");  
            return -1;  
        }  

        rewind(fp);  
        fwrite(&test, sizeof(example), 1, fp);  

        rewind(fp);  
        fread(&read, sizeof(example), 1, fp);  

        printf("%d, %s\n", read.age, read.name);  

        fclose(fp);  
        return 0;  
    }  


Related articles: