阅读SQLite标题

时间:2013-09-07 23:32:02

标签: c file-io sqlite endianness

我试图使用这个(实际的片段)代码解析SQLite数据库文件中的标题:

struct Header_info {
    char *filename;
    char *sql_string;
    uint16_t page_size;
};

int read_header(FILE *db, struct Header_info *header)
{
    assert(db);
    uint8_t sql_buf[100] = {0};

    /* load the header */
    if(fread(sql_buf, 100, 1, db) != 1) {
        return ERR_SIZE;
    }

    /* copy the string */
    header->sql_string = strdup((char *)sql_buf);

    /* verify that we have a proper header */
    if(strcmp(header->sql_string, "SQLite format 3") != 0) {
        return ERR_NOT_HEADER;
    }

    memcpy(&header->page_size, (sql_buf + 16), 2);

    return 0;
}

以下是我正在测试的文件的相关字节:

0000000: 5351 4c69 7465 2066 6f72 6d61 7420 3300  SQLite format 3.
0000010: 1000 0101 0040 2020 0000 c698 0000 1a8e  .....@  ........

遵循this规范,代码对我来说是正确的。

稍后我用这一行打印header->page_size

printf("\tPage size: %"PRIu16"\n", header->page_size);

但该行打印出16,而不是预期的4096.为什么?我几乎肯定这是我忽略的一些基本的事情。

2 个答案:

答案 0 :(得分:2)

这是一个字节序问题。 x86是little-endian,也就是说,在内存中,首先存储最低有效字节。当您将10 00加载到小端架构的内存中时,您会以人类可读的形式获得00 1016而不是4096

因此,您的问题是memcpy不适合读取该值。

请参阅SQLite file format spec的以下部分:

  

1.2.2页面大小

     

从偏移量16开始的双字节值决定了页面大小   数据库。对于SQLite版本3.7.0.1及更早版本,此值为   被解释为big-endian整数,并且必须是两者之间的幂   512和32768,包括在内。从SQLite版本3.7.1开始,一个页面   支持65536字节的大小。值65536不适合a   两字节整数,所以要指定65536字节的页面大小,值为   在偏移16处是0x00 0x01。该值可以解释为a   big-endian 1和思想是一个神奇的数字来代表   65536页面大小。或者可以将双字节字段视为小端   数字,并表示它代表页面大小除以256.这些   页面大小字段的两种解释是等效的。

答案 1 :(得分:2)

这似乎是一个字节序问题。如果您使用的是小端机器:

memcpy(&header->page_size, (sql_buf + 16), 2);

将两个字节10 00复制到uint16_t,它将在较低地址处具有低位字节。

您可以这样做:

header->page_size = sql_buf[17] | (sql_buf[16] << 8);

<强>更新

对于记录,请注意我建议的解决方案无论机器的字节顺序如何都会起作用(请参阅此Rob Pike's Article)。