我试图使用这个(实际的片段)代码解析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.为什么?我几乎肯定这是我忽略的一些基本的事情。
答案 0 :(得分:2)
这是一个字节序问题。 x86是little-endian,也就是说,在内存中,首先存储最低有效字节。当您将10 00
加载到小端架构的内存中时,您会以人类可读的形式获得00 10
,16
而不是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)。