如何处理二进制文件格式的可移植性问题

clo*_*ey1 5 c linux format file

我正在设计一个二进制文件格式来存储字符串[不终止null以节省空间]和二进制数据.

一世.处理小/大端系统的最佳方法是什么?ia将所有内容转换为网络字节顺序并返回ntohl()/ htonl()工作?

II.在x86,x64和arm上,打包结构的大小是否相同?

III.这种方法有任何固有的弱点吗?

struct __attribute__((packed)) Header {
    uint8_t magic;
    uint8_t flags;
};

struct __attribute__((packed)) Record {
    uint64_t length;
    uint32_t crc;
    uint16_t year;
    uint8_t day;
    uint8_t month;
    uint8_t hour;
    uint8_t minute;
    uint8_t second;
    uint8_t type;
};
Run Code Online (Sandbox Code Playgroud)

测试器代码我正在使用开发格式:

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <limits.h>
#include <strings.h>
#include <stdint.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

struct __attribute__((packed)) Header {
    uint8_t magic;
    uint8_t flags;
};

struct __attribute__((packed)) Record {
    uint64_t length;
    uint32_t crc;
    uint16_t year;
    uint8_t day;
    uint8_t month;
    uint8_t hour;
    uint8_t minute;
    uint8_t second;
    uint8_t type;
};

    int main(void)
    {

        int fd = open("test.dat", O_RDWR|O_APPEND|O_CREAT, 444);
        struct Header header = {1, 0};
        write(fd, &header, sizeof(header));
        char msg[] = {"BINARY"};
        struct Record record = {strlen(msg), 0, 0, 0, 0, 0, 0, 0};
        write(fd, &record, sizeof(record));
        write(fd, msg, record.length);
        close(fd);
        fd = open("test.dat", O_RDWR|O_APPEND|O_CREAT, 444);


        read(fd, &header, sizeof(struct Header));
        read(fd, &record, sizeof(struct Record));
        int len = record.length;
        char c;
        while (len != 0) {
            read(fd, &c, 1);
            len--;
            printf("%c", c);
        }
        close(fd);
    }
Run Code Online (Sandbox Code Playgroud)

Ste*_*mit 7

一世.在我看来,在我看来,将文件定义为一个顺序并转换为"内部"顺序,以及在读/写时(可能使用ntohl等)转换为最佳方法.

II.我不相信包装结构.他们可能会为这些平台的这种方法工作,但没有任何保证.

III.使用fread和fwrite在整个结构上读取和写入二进制文件(在我看来)是一种固有的弱方法.您最大限度地提高了被字大小问题,填充和对齐问题以及字节顺序问题所困扰的可能性.

我喜欢写的是像get16()和put32()这样的小函数,它们一次读写一个字节,因此对字大小和字节顺序的困难本身就不敏感.然后我根据这些来编写直接的putHeader和getRecord函数(等等).

unsigned int get16(FILE *fp)
{
    unsigned int r;
    r = getc(fp);
    r = (r << 8) | getc(fp);
    return r;
}

void put32(unsigned long int x, FILE *fp)
{
    putc((int)((x >> 24) & 0xff), fp);
    putc((int)((x >> 16) & 0xff), fp);
    putc((int)((x >> 8) & 0xff), fp);
    putc((int)(x & 0xff), fp);
}
Run Code Online (Sandbox Code Playgroud)

[PS正如@Olaf在其中一条评论中正确指出的那样,在生产代码中,您需要处理EOF并在这些函数中出错.为了简单起见,我把它们留了下来.]