将二进制数据(从文件)读入结构

rig*_*gon 12 c zip byte

我正在从文件中读取二进制数据,特别是从zip文件中读取.(要了解有关zip格式结构的更多信息,请参阅http://en.wikipedia.org/wiki/ZIP_%28file_format%29)

我创建了一个存储数据的结构:

typedef struct {
                                            /*Start Size            Description                                 */
    int signatute;                          /*   0  4   Local file header signature = 0x04034b50                */
    short int version;                      /*  ?4  2   Version needed to extract (minimum)                     */
    short int bit_flag;                     /*  ?6  2   General purpose bit flag                                */
    short int compression_method;           /*  ?8  2   Compression method                                      */
    short int time;                         /*  10  2   File last modification time                             */
    short int date;                         /*  12  2   File last modification date                             */
    int crc;                                /*  14  4   CRC-32                                                  */
    int compressed_size;                    /*  18  4   Compressed size                                         */
    int uncompressed_size;                  /*  22  4   Uncompressed size                                       */
    short int name_length;                  /*  26  2   File name length (n)                                    */
    short int extra_field_length;           /*  28  2   Extra field length (m)                                  */
    char *name;                             /*  30  n   File name                                               */
    char *extra_field;                      /*30+n  m   Extra field                                             */

} ZIP_local_file_header;
Run Code Online (Sandbox Code Playgroud)

返回的大小sizeof(ZIP_local_file_header)为40,但如果使用sizeof运算符计算每个字段的总和,则总大小为38.

如果我们有下一个结构:

typedef struct {
    short int x;
    int y;
} FOO;
Run Code Online (Sandbox Code Playgroud)

sizeof(FOO)返回8,因为内存每次分配4个字节.因此,要分配x保留4个字节(但实际大小为2个字节).如果我们需要另一个short int,它将填充先前分配的剩余2个字节.但是,因为我们有一个int它将被分配加上4个字节,空的2个字节被浪费.

要从文件中读取数据,我们可以使用以下函数fread:

ZIP_local_file_header p;
fread(&p,sizeof(ZIP_local_file_header),1,file);
Run Code Online (Sandbox Code Playgroud)

但由于中间有空字节,因此无法正确读取.

如何通过ZIP_local_file_header浪费无字节来顺序有效地存储数据?

Joh*_*ode 11

为了满足底层平台的对齐要求,结构可以在成员之间具有"填充"字节,以便每个成员从正确对齐的地址开始.

有几种解决方法:一种是使用适当大小的成员分别读取标题的每个元素:

fread(&p.signature, sizeof p.signature, 1, file);
fread(&p.version, sizeof p.version, 1, file);
...
Run Code Online (Sandbox Code Playgroud)

另一种方法是在结构定义中使用位字段 ; 这些不受填充限制.缺点是位字段必须是unsigned intint或,从C99开始_Bool; 您可能必须将原始数据转换为目标类型以正确解释它:

typedef struct {                 
    unsigned int signature          : 32;
    unsigned int version            : 16;                
    unsigned int bit_flag;          : 16;                
    unsigned int compression_method : 16;              
    unsigned int time               : 16;
    unsigned int date               : 16;
    unsigned int crc                : 32;
    unsigned int compressed_size    : 32;                 
    unsigned int uncompressed_size  : 32;
    unsigned int name_length        : 16;    
    unsigned int extra_field_length : 16;
} ZIP_local_file_header;
Run Code Online (Sandbox Code Playgroud)

如果文件是用big-endian编写的,但你的系统是little-endian,你可能还需要在每个成员中进行一些字节交换.

需要注意的是nameextra field不是结构定义的一部分; 当你从文件中读取时,你不会读取名称和额外字段的指针值,你将会读取名称和额外字段的实际内容.由于在阅读标题的其余部分之前您不知道这些字段的大小,因此在阅读上述结构之后,您应该推迟阅读它们.就像是

ZIP_local_file_header p;
char *name = NULL;
char *extra = NULL;
...
fread(&p, sizeof p, 1, file);
if (name = malloc(p.name_length + 1))
{
    fread(name, p.name_length, 1, file);
    name[p.name_length] = 0;
}
if (extra = malloc(p.extra_field_length + 1))
{
    fread(extra, p.extra_field_length, 1, file);
    extra[p.extra_field_length] = 0;
}
Run Code Online (Sandbox Code Playgroud)

  • @Ricardo - 您应该将指针传递给*原始*结构类型**中定义的结构成员**或**使用位域并传递整个结构的地址.你不能取位字段的地址. (2认同)

Adr*_*ith 9

C struct只是将相关的数据分组在一起,它们没有在内存中指定特定的布局.(正如a的宽度int也没有定义.)Little-endian/Big-endian也没有定义,并且取决于处理器.

不同的编译器,不同体系结构或操作系统上的相同编译器等,将以不同的方式布局结构.

由于要读取的文件格式是根据哪个字节去哪里定义的,因此结构虽然看起来非常方便和诱人,但却不是正确的解决方案.您需要将文件视为a char[]并拉出所需的字节并移动它们以使数字由多个字节组成,等等.

  • struct成员将按其声明的顺序排列.从6.7.2.1开始,第13段:"在一个结构对象中,非位字段成员和位字段所在的单元具有的地址*按声明的顺序增加*.指向结构的指针对象,适当转换,指向其初始成员(或者如果该成员是位字段,然后指向它所在的单位),反之亦然.结构对象中可能有未命名的填充,但不是在其开头".强调我的. (7认同)

Oli*_*rth 5

该解决方案是特定于编译器的,但是例如在GCC中,您可以通过附加__attribute__((packed))到定义来强制它更紧密地打包结构.请参阅http://gcc.gnu.org/onlinedocs/gcc-3.2.3/gcc/Type-Attributes.html.