SQLite3 示例程序 - 表的创建/查找/二进制文件的保存
项目在后面下载, 以下是代码, 有较详细的说明.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <Windows.h>//用到了ShellExecute
#include "sqlite3/sqlite3.h"
/*
说明:
传给sqlite3_exec的回调函数,用来显示查询结果
对每一条查询结果调用一次该回调函数
参数:
pv:由sqlite3_exec传递的初始化参数
argc:表头的列数
col:表头的名字数组指针
argv:表头的数据数组指针
返回值:
1:中断查找
0:继续列举查询到的数据
+-----------------------------------+
| id | pic | data(16进制数据) |
|-----------------------------------|
| 1 | a.jpg | 00 00 00 ... |
|-----------------------------------|
| 2 | b.jpg | XX XX XX |
+-----------------------------------+
对第一行:
argc=3 即 [0]...[2]
argv[0]="1",argv[1]="a.jpg",argv[2]="000000..."
col[0]="id",col[1]="pic",col[2]="data"
*/
int callback(void* pv, int argc, char** argv, char** col)
{
printf("id:%s -> pic:%s\n", argv[0], argv[1]);
return 0;//continue search
}
int main(void)
{
sqlite3* pDB = NULL;//先要为被打开的数据库建立一个sqlite3型的变量
sqlite3_stmt* pStmt = NULL;//sqlite3内部定义的一种"SQL语句"
char* szMsg = NULL;//传递给sqlite3_*,sqlite3_*为其分配内存空间装载错误消息,调用sqlite3_free释放
char buffer[1024];//我用来写SQL语句等其它临时使用的缓冲区
char* file_buffer = NULL;//用来保存JPG的二进制数据
FILE* fp = NULL;//文件指针
int pic_id;//后来在显示查询时用来索引图片ID
size_t file_size;//读取本地的文件的大小 或 从数据库读取的二进制数据的大小
WIN32_FIND_DATA fd;//用来查找的结构体,C库可用_finddata_t,_finfirst,_findnext
HANDLE hFile = INVALID_HANDLE_VALUE;//Win32API FindFirstFile传回的句柄
int ret;//保存返回值
/*函数:sqlite3_open(char* db, sqlite3* pDB)
**参数:db:数据库文件路径,都要使用UTF-8编码(或UTF-16)
** pDB:sqlite3* 型的数据库指针
**返回值:打开的状态:SQLITE_OK 成功
*/
ret = sqlite3_open("./test.db", &pDB);
if(ret != SQLITE_OK) {
fprintf(stderr, "open error.\n");
return 1;
}
//SQL创建表的语句,"integer primary key":主键,唯一,递增; "text":变长文本; "blob":二进制数据类型(都是变长的)
_snprintf(buffer, sizeof(buffer), "create table if not exists testdb (id integer primary key, pic text, data blob);");
//执行一条语句:sqlite3_exec(sqlite3* pDB, char* SQL, 回调函数, 初始化参数, char** ppErrMsg);
//回调函数主要针对查询, 这里就不需要了
if(sqlite3_exec(pDB, buffer, NULL, NULL, &szMsg) != SQLITE_OK) {
fprintf(stderr, "exec error:%s\n", szMsg);
sqlite3_free(szMsg);//释放内部分配的空间
sqlite3_close(pDB);//来用关闭打开的数据库
return 1;
}
printf("scanning pics...\n");
hFile = FindFirstFile("*.jpg", &fd);//查找所有的JPG文件
if(hFile != INVALID_HANDLE_VALUE) {
do
{
printf("add %s...\n", fd.cFileName);
//SQL的插入语句:"insert into 表名 (需要的表头的名字[, ...]) values (对应的值[,...]);"
//对于主键不需要插入, 自动会增加的; 字符串用 单引号 括起来
//对于二进制数据:可以利用sqlite3提供的准备语句完成:即使用 '?' 临时代替值(也可以使用其它符号,参考文档. 因为二进制数据太多), 后面利用 sqlite3_bind_* 绑定相关的数据
//准备语句 sqlite3_prepare(sqlite3* pDB, char* SQL, int LenOfSQL, sqlite3_stmt** ppStmt, char** pzTail)
//LenOfSQL可以传-1,表示到第一个'\0'为止, 最后一个参数不太清楚作用, 记得是指向未使用的SQL, 因为可以指定SQL语句的长度的原因, 我一般不使用
//返回SQLITE_OK表示成功
_snprintf(buffer, sizeof(buffer), "insert into testdb (pic,data) values (\'%s\',?);", fd.cFileName);
if(sqlite3_prepare(pDB, buffer, -1, &pStmt, NULL) != SQLITE_OK) {
fprintf(stderr, "sqlite3_prepare error\n");
continue;
}
//C库读取文件到buffer空间
fp = fopen(fd.cFileName, "rb");
if(!fp) continue;
fseek(fp, 0L, SEEK_END);
file_size = ftell(fp);
rewind(fp);
file_buffer = (char*)malloc(file_size);
if(!file_buffer) {
fclose(fp);
continue;
}
fread(file_buffer, 1, file_size, fp);
fclose(fp);
//这里就是绑定前面的'?'数据的语句, 因为是data blob, 所以这里使用 *_blob 函数
//sqlite3_bind_blob(sqlite3_stmt* pStmt, int which, void* pv, size_t size, XXX)
//pStmt前面准备的"语句". which:第几个未设定数据的"变量"(前面的'?'), 第一个是1, 依次++
//返回SQLITE_OK示成功. 最后一个参数不清楚, 未使用
if(sqlite3_bind_blob(pStmt, 1, file_buffer, file_size, NULL)!=SQLITE_OK) {
fprintf(stderr, "sqlite3_bind_blod error:%s", sqlite3_errmsg(pDB));
}
//现在是真正地完成SQL语句的操作:初始化语句->绑定语句->执行语句
//这里如返回SQLITE_DONE表示完成, 返回值应根据语句选择. SQLITE_ROW 表示查询有了至少有了一条结果
if(sqlite3_step(pStmt) !=SQLITE_DONE) {
fprintf(stderr, "sqlite3_step error:%s", sqlite3_errmsg(pDB));
}
free(file_buffer);
//用来析构前面的操作(准备语句)所分配的空间. 一定要析构掉, 不然空间不会释放(不管是否执行成功)
sqlite3_finalize(pStmt);
}while(FindNextFile(hFile, &fd));//继续查找一下个 JPG 文件
printf("\n");
}
else
{
fprintf(stderr, "no more files found\n");
sqlite3_close(pDB);
return 1;
}
FindClose(hFile);
//SQL的查找语句:select 表头名[,表头名] from 表名 [order by 表头名 desc|asc];
//像查找之类的就不需要 准备->绑定->执行 这样复杂了, 直接一个sqlite3_exec 就可以完成
//查询当然是需要回调的
_snprintf(buffer, sizeof(buffer), "select id,pic from testdb order by id asc;");
if(sqlite3_exec(pDB, buffer, callback, NULL, &szMsg) != SQLITE_OK) {
fprintf(stderr, "sqlite3_exec error:%s\n", szMsg);
sqlite3_close(pDB);
return 1;
}
printf("\n");
printf("Input pic's id to open(q for quit):");
while(scanf("%d", &pic_id) == 1) {
_snprintf(buffer, sizeof(buffer), "select * from testdb where id=%d", pic_id);
//由于是select * , 且我这个表里有二进制数据, 所以这里用准备语句优于直接执行语句
sqlite3_prepare(pDB, buffer, -1, &pStmt, NULL);
//SQLITE_ROW表示查询有结果
//可以使用while语句显示所有数据, 直到sqlite3_step 返回 SQLITE_DONE 表示没有更多的数据为止
//我这里只有至多只有一条, 因为id是唯一的
if(sqlite3_step(pStmt) == SQLITE_ROW) {
//sqlite3_column_bytes 返回表头某一项的大小(字节), 最左边的是0, 在这里:id为0,pic是1,data是2
//pStmt, 准备语句指针
file_size = sqlite3_column_bytes(pStmt, 2); //left most is 0->schema:id,test,blob
//sqlite3_column_text(sqlite3_stmt* pStmt, int iCol);
//iCol:返回哪一项的文本(最好不要把数据类型乱用)
//最左边的iCol是0, pic这里就是1, pic是text文本类型
_snprintf(buffer, sizeof(buffer), "new_%s", sqlite3_column_text(pStmt, 1));
//把读取到的数据保存为新文件
fp = fopen(buffer, "wb");
if(!fp) {
sqlite3_finalize(pStmt);
continue;
}
//sqlite3_column_blob 返回表头某一项的二进制数据指针, 最左边的是0, 这里data是2
fwrite(sqlite3_column_blob(pStmt, 2), 1, file_size, fp);
fclose(fp);
//打开该文件
ShellExecute(NULL, "open", buffer, NULL, NULL, SW_SHOWNORMAL);
}
else//sqlite3_step 不是返回 SQLITE_ROW 表示没有数据
{
printf("no such pic whose id is:%d\n", pic_id);
}
//析构掉
sqlite3_finalize(pStmt);
printf("\nInput pic's id to open(q for quit):");
}
//结束, 关闭, 退出
sqlite3_close(pDB);
return 0;
}
项目下载:sqlite_test.7z