SQLite BLOB的使用、出现乱码问题并解决

  版权信息:
● 本博客使用CC 3.0协议,转载请保留该信息。
● 原文作者: 戴晓天 @ 云飞机器人实验室
● 原文地址: SQLite BLOB的使用、出现乱码问题并解决

 (*)本文系本博客原创,如需转载请注明原作者!

原文作者:戴晓天,云飞实验室

联系方式:automatic.dai@gmail.com

原文来自:www.yfworld.com

SQLite是轻量级数据库,但其功能齐全,当然也包括了对BLOB对象的操作功能。但今天在做SQLite的BLOB功能测试时,出现了读取出的数据与写入数据不一致的情况,这让我十分费解。

测试程序将一幅图片文件读入内存,写入数据库。之后从数据库中读出,再写出成图片文件。整个程序的执行过程示意如下:

1. 打开数据库

<br />
sqlite3_open(&quot;blob.db&quot;,&amp;db);<br />

 

2. 新建一个表,包含两个字段:name和item,其中name为varchar(128)是文件名,item为blob类型

<br />
sprintf( sql,&quot;CREATE TABLE blob(id varchar(128) UNIQUE,item BLOB)&quot; );<br />
sqlite3_exec(db, sql, 0, 0, &amp;zErrMsg);     // zErrMsg是错误代码<br />

 

3. 准备一次blob传输,其中 ? 为占位符,之后要将其绑定

(*)stat为sqlite_stmt * 类型

<br />
sqlite3_prepare(db, &quot;INSERT INTO blob values (1,?);&quot;, -1, &amp;stat, 0);<br />

 

4. 打开一个对象,以便将其绑定

(*)数据保存在pFile中,数据大小保存在filesize中,在下一步会用到。

<br />
fp = fopen(&quot;test.bmp&quot;, &quot;rb&quot;);</p>
<p>//首先计算文件大小<br />
fseek(fp, 0, SEEK_END);<br />
filesize = ftell(fp);</p>
<p>fseek(fp, 0, SEEK_SET);</p>
<p>//将数据读入到缓冲区<br />
pFile = new char[filesize];<br />
size_t sz = fread(pFile, sizeof(char), filesize, fp);<br />
fclose(fp);<br />

 

5. 绑定数据并执行

<br />
sqlite3_bind_blob(stat, 1, pFile, filesize, NULL);<br />
sqlite3_step(stat);<br />
sqlite3_finalize(stat);  // Release sqlite_stmt<br />

—————————————————————-

以上完成了blob数据的写入,现在将其读出。

6. 进行一次读出操作

<br />
sqlite3_prepare(db, &quot;select * from blob;&quot;, -1, &amp;stat, 0);<br />
sqlite3_step(stat);<br />

 

7. 得到Blob文件的指针和大小

<br />
const void *pBlob = sqlite3_column_blob(stat, 1); // Get blob,1为列号<br />
int blob_size = sqlite3_column_bytes(stat, 1);    // Get size<br />

 

8. 将其写入文件

<br />
fp = fopen(&quot;test_output.bmp&quot;, &quot;w+&quot;);</p>
<p>size_t sz = fwrite(pBlob, sizeof(char), blob_size, fp);<br />

 

至此,一个Blob对象的写入/读出操作就完成了。

—————————————————————-

可是问题来了,得到的图片文件与源样本存在很大差别!

源图片:

最后得到的文件:


仔细观察,可以发现两幅图片还是有几分相似的,而出错的图片好像发生了扭曲一般。

首先,我考虑到是可能是缓冲数组的边界值没有取好,造成了部分文件丢失。可在仔细核对过缓冲区大小后,并没有发现任何问题。打开两个文件的属性进行对照:

源图片:

读取出的图片:


可见读取出的图片反而更大一些,这显然不是丢失了数据,而是增加了数据。好好的会增加什么数据呢?通过UltraEdit打开两个图片的二进制文件,我找到了一些眉目:

源文件:

目的文件:

通过对比,我发现凭空增加的字符是0x0D,这是ASC码中的回车字符。像这样的字符出现了好几处,我想就是这个原因导致了两个文件大小不一致。而且我发现,所有增加的0x0D字符并无周期规律,但都是紧跟在0x0A(换行符)之前的。
这下终于发现了问题的所在:程序将我的文件当成了文本在处理!在WINDOWS的文本格式中,所有的换行符之前都会紧跟一个回车符。

自然而然的,我将问题定位到我不熟悉的SQLite上。我首先怀疑,SQLite在处理我的BLOB对象时,当作了文本处理。但查阅了它的Document之后,我并没有找到sqlite3_column_blob()有关于具体数据类型是纯二进制还是文本的参数。我操作BLOB的方法与网上介绍的方法也并无异处。

我决定将我的程序分块调试,先定位到底是文件操作出了问题,还是数据库方面出了问题;之后再具体去看是写的时候还是读的时候遇到了问题。照着这个思路,我准备开始分块调试。

中间太累了,我睡了一觉。刚要睡着,灵感来了。我起身打开电脑屏幕,添加了一个字母,运行。

<br />
fp = fopen(&quot;test_output.bmp&quot;, &quot;wb+&quot;);<br />

哈,问题解决了。原来,我在读文件操作时用了binary模式,写操作时却粗心没有在模式中加”b”,这样就用文本模式写入了。可见,不一定总是在新地方翻船。有时候,自己熟悉的地方也有存在潜在问题的可能性。

在此分享给大家,一是希望与我有同样问题的可以顺利解决;二是警戒自己不要在这种细节方面出问题。

 

【修订历史】

V1.1    24/03/2015: 修改了文章版式,修正了更新网站版本后的排版问题。将代码从plain text改为通过代码插件高亮显示。

%d bloggers like this: