2

我试图从 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  .....@  ........

按照这个规范,代码对我来说是正确的。

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

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

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

4

2 回答 2

2

这是一个字节顺序问题。x86 是 little-endian,即在内存中,首先存储最低有效字节。因此,当您在 little-endian 架构上加载10 00到内存中时,您将获得00 10人类可读的形式,而16不是4096.

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

请参阅SQLite 文件格式规范的以下部分 :

1.2.2 页面大小

从偏移量 16 开始的两字节值决定了数据库的页面大小。对于 SQLite 版本 3.7.0.1 和更早版本,此值被 解释为大端整数,并且必须是 512 和 32768(含)之间的 2 的幂。从 SQLite 版本 3.7.1 开始,支持 65536 字节的页面大小。值 65536 不适合两字节整数,因此要指定 65536 字节的页面大小,偏移量 16 处的值是 0x00 0x01。这个值可以被解释为一个大端序 1,并且被认为是一个代表 65536 页面大小的幻数。或者可以将两字节字段视为一个小端序数,并说它表示页面大小除以 256。这两种对页面大小字段的解释是等价的。

于 2013-09-07T23:54:16.073 回答
2

这似乎是一个字节顺序问题。如果您使用的是 little-endian 机器,则此行:

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

将这两个字节复制10 00uint16_t低位字节位于低位地址的一个中。

你可以这样做:

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

更新

作为记录,请注意,无论机器的字节序如何,我提出的解决方案都可以工作(请参阅此Rob Pike 的文章)。

于 2013-09-07T23:54:43.550 回答