SQLite3 示例程序 - 表的创建/查找/二进制文件的保存

陪她去流浪 桃子 2012年10月25日 阅读次数:2815

项目在后面下载, 以下是代码, 有较详细的说明.

#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

这篇文章的内容已被作者标记为“过时”/“需要更新”/“不具参考意义”。

标签:sqlite · 代码片段 · SQL