2023-04-26 简述C语言调用sqlite3数据库


老林的C语言新课, 想快速入门点此 <C 语言编程核心突破>



前言

sqlite3是文件型数据库, 小巧, 快, 环境构建容易. 本文介绍sqlite3最基本的C语言API, 有相关需求的人可以看看.


一、直接操作 sqlite3_exec( ) 函数

我们可以用最简单的sqlite3_exec( )函数完成sqlite3的命令输入, 通过调用callback函数操作返回的数据.

以下是函数原型:

SQLITE_API int sqlite3_exec(
  sqlite3*,                                  /* An open database */
  const char *sql,                           /* SQL to be evaluated */
  int (*callback)(void*,int,char**,char**),  /* Callback function */
  void *,                                    /* 1st argument to callback */
  char **errmsg                              /* Error msg written here */
);

它接受五个参数:

sqlite3*:指向 SQLite 数据库的指针。

const char *sql:要执行的 SQL 命令。

int (callback)(void,int,char**,char**):指向回调函数的指针。如果不需要回调函数,则该参数可以为 NULL。

void *:回调函数的第一个参数。

char **errmsg:如果发生错误,则用于存储错误消息的指针。

当调用 sqlite3_exec 函数时,它将执行 SQL 命令并返回一个整数值,表示执行结果。如果执行成功,则返回 SQLITE_OK;如果发生错误,则返回其他值。

回调函数是一个可选参数,当执行 SQL 命令时,可以使用它来处理结果。

callback函数的简单定义, 这里没有使用第一个参数, 但这个参数非常重要, 可以将结果传出.

static int callback(void *data, int argc, char **argv, char **azColName)
{
    for (int i = 0; i < argc; i++)
    {
        printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
    printf("\n");
    return 0;
}

简单示例: 通过回调函数, 将股票代码为SH600000的所有数据进行打印

表的结构:

CREATE TABLE gupiaochi (
    GuPiao text NOT NULL,
    ShiJian text NOT NULL,
    KaiPan real NOT NULL,
    ZuiGao real NOT NULL,
    ZuiDi real NOT NULL,
    ShouPan real NOT NULL,
    ZhangFu_pencent real NOT NULL,
    ZhenFu_pencent real NOT NULL,
    ZongShou int NOT NULL,
    JinE int NOT NULL,
    HuanShou_pencent real NOT NULL,
    ChengJiaoCiShu int NOT NULL
);
#include "sqlite3.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

static int callback(void *data, int argc, char **argv, char **azColName)
{
    static int index;
    printf("%d\n", index++);

    for (int i = 0; i < argc; i++)
    {

        printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
    printf("\n");
    return 0;
}

int main()
{
    SetConsoleOutputCP(CP_UTF8);
    SetConsoleCP(936);

    sqlite3 *dataBase;

    int result = sqlite3_open("gupiao3.db", &dataBase);
    if (result)
    {
        fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(dataBase));
        exit(0);
    }
    else
    {
        fprintf(stderr, "Opened database successfully\n");
    }

    char *zErrMsg = NULL;
    result = sqlite3_exec(
        dataBase, "SELECT * FROM gupiaochi WHERE GuPiao LIKE 'SH600000';",
        callback, 0, &zErrMsg);

    return 0;
}

稍复杂示例: 存入数据:

#include "sqlite3.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

#define BUFFERSIZE 1024
#define ARRSIZE 32

const char *guPiaoMingCheng[] = {
    "SH600000浦发银行", "SH600004白云机场", "SH600009上海机场",
    "SZ300601康泰生物", "SZ300628亿联网络", "SZ300676华大基因"};

const char *GbkToUtf8(const char *src_str)
{
    if (strlen(src_str) >= BUFFERSIZE * 2 - 2)
    {
        return NULL;
    }

    static wchar_t wstr[BUFFERSIZE];
    static char str[BUFFERSIZE * 3];

    memset(wstr, 0, BUFFERSIZE);
    memset(str, 0, (size_t)BUFFERSIZE * 3);

    MultiByteToWideChar(CP_ACP, 0, src_str, -1, wstr, BUFFERSIZE);
    WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, BUFFERSIZE * 3, NULL, NULL);

    return str;
}

const char *Utf8ToGbk(const char *src_str)
{
    if (strlen(src_str) >= BUFFERSIZE * 3 - 3)
    {
        return NULL;
    }

    static wchar_t wstr[BUFFERSIZE];
    static char str[BUFFERSIZE * 3];

    memset(wstr, 0, BUFFERSIZE);
    memset(str, 0, (size_t)BUFFERSIZE * 3);

    MultiByteToWideChar(CP_UTF8, 0, src_str, -1, wstr, BUFFERSIZE);
    WideCharToMultiByte(CP_ACP, 0, wstr, -1, str, BUFFERSIZE * 3, NULL, NULL);

    return str;
}

static int callback(void *data, int argc, char **argv, char **azColName)
{
    return 0;
}

int main()
{
    SetConsoleOutputCP(CP_UTF8);
    SetConsoleCP(936);

    sqlite3 *dataBase;

    char *zErrMsg = NULL;

    int result = sqlite3_open("gupiao3.db", &dataBase);

    if (result)
    {
        fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(dataBase));
        exit(0);
    }
    else
    {
        fprintf(stdout, "Opened database successfully\n");
    }

    for (int i = 0; i != sizeof(guPiaoMingCheng) / sizeof(char *); ++i)
    {
        char path[BUFFERSIZE] = "E:\\C++\\other\\gupiao\\";
        strcat(path, guPiaoMingCheng[i]);

        // 含有中文, Windows只认GBK编码路径, 需要进行转换
        FILE *dataFile = fopen(Utf8ToGbk(path), "r");

        if (dataFile)
        {
            result = sqlite3_exec(dataBase, "BEGIN TRANSACTION;", callback, 0,
                                  &zErrMsg);

            static char line[BUFFERSIZE];

            fgets(line, BUFFERSIZE, dataFile);

            static char guPiao[ARRSIZE];
            strncpy(guPiao, guPiaoMingCheng[i], 8);

            static char shiJian[ARRSIZE];
            static char kaiPanJiaGe[ARRSIZE];
            static char zuiGaoJiaGe[ARRSIZE];
            static char zuiDiJiaGe[ARRSIZE];
            static char shouPanJiaGe[ARRSIZE];
            static char zhangFu[ARRSIZE];
            static char zhenFu[ARRSIZE];
            static char zongShou[ARRSIZE];
            static char jinE[ARRSIZE];
            static char huanShou[ARRSIZE];
            static char chengJiaoCiShu[ARRSIZE];

            while (fgets(line, BUFFERSIZE, dataFile))
            {
                if (sscanf(GbkToUtf8(line), "%s%s%s%s%s%s%s%s%s%s%s", shiJian,
                           kaiPanJiaGe, zuiGaoJiaGe, zuiDiJiaGe, shouPanJiaGe,
                           zhangFu, zhenFu, zongShou, jinE, huanShou,
                           chengJiaoCiShu) != EOF)
                {
                    static char sqlCommand[BUFFERSIZE];
                    sprintf(sqlCommand,
                            "INSERT INTO gupiaochi VALUES ('%s', '%s', %s, %s, "
                            "%s, %s, %s, %s, %s, %s, %s, %s );",
                            guPiao, shiJian, kaiPanJiaGe, zuiGaoJiaGe,
                            zuiDiJiaGe, shouPanJiaGe, zhangFu, zhenFu, zongShou,
                            jinE, huanShou, chengJiaoCiShu);

                    sqlite3_exec(dataBase, sqlCommand, callback, 0, &zErrMsg);
                }
            }
            fclose(dataFile);

            result = sqlite3_exec(dataBase, "COMMIT TRANSACTION;", callback, 0,
                                  &zErrMsg);
            if (result != SQLITE_OK)
            {
                fprintf(stderr, "%s\nSQL error: %s\n", guPiaoMingCheng[i],
                        zErrMsg);
                sqlite3_free(zErrMsg);
            }
            else
            {
                fprintf(stdout, "Records created successfully\n");
            }
        }
    }

    sqlite3_close(dataBase);

    return 0;
}

以上代码将如下格式的数据存入数据库:

时间  开盘  最高  最低  收盘  涨幅  振幅  总手  金额  换手  成交次数  
2017-07-1415.67  18.94  15.67  18.94  +38.85  23.97  5500  106878  0.014  14  

由于sqlite3只支持utf8, 需要将所有gbk编码的字符进行转换.

二、使用 sqlite3_prepare_v2( ), sqlite3_bind_text(), sqlite3_step( ) 分步调用

除了直接进行命令操作, 还可以进行分步操作, 这使得效率提升.

SQLITE_API int sqlite3_prepare_v2(
  sqlite3 *db,            /* Database handle */
  const char *zSql,       /* SQL statement, UTF-8 encoded */
  int nByte,              /* Maximum length of zSql in bytes. */
  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
  const char **pzTail     /* OUT: Pointer to unused portion of zSql */
);

该函数用于编译并预处理一条 SQL 语句,将其转换为 SQLite 内部的字节码表示。该函数接受四个参数:

db:数据库句柄,指向 SQLite 中打开的一个数据库。

zSql:要编译的 SQL 语句,以 UTF-8 编码。

nByte:zSql 的最大长度,以字节为单位。如果该值为 -1,则 SQLite 将自动计算 zSql 的长度。

ppStmt:一个指向 sqlite3_stmt 指针的指针,用于返回编译后的语句句柄。

pzTail:一个指向 const char 指针的指针,用于返回 zSql 中未使用的部分。

如果执行成功,sqlite3_prepare_v2 函数将返回 SQLITE_OK,同时将编译后的语句句柄存储在 ppStmt 所指向的指针中。

该句柄可以用于执行 SQL 语句,并且在使用完毕后需要通过 sqlite3_finalize 函数进行释放。

如果编译失败,该函数将返回相应的错误码,可以通过调用 sqlite3_errmsg 函数获取错误信息。

需要注意的是,sqlite3_prepare_v2 函数只对 SQL 语句进行编译和预处理,并不执行该语句。如果要执行 SQL 语句,应该使用 sqlite3_step 函数。

使用sqlite3_bind_text绑定sqlite3_prepare_v2中zSql的占位符

int sqlite3_bind_text(sqlite3_stmt*,	//sql句柄
					  int,				//要绑定的占位符序号, 占位符序号从1开始
					  const char*,		//命令字符串
					  int,				//命令字符串长度, -1为自动计算长度
					  void(*)(void*)	//通常为NULL
					  );

sqlite3_prepare_v2准备一个命令, 用sqlite3_bind_text填充占位符 “?”

    sqlite3_stmt *pstmt;
    const char *sql = "SELECT* FROM gupiaochi WHERE GuPiao LIKE ?;";
    result = sqlite3_prepare_v2(dataBase, sql, (int)strlen(sql), &pstmt, NULL);

    result = sqlite3_bind_text(pstmt, 1, "SH600000", 8, NULL);

使用sqlite3_step进行分布查询:

函数原型, 查询一步

int sqlite3_step(sqlite3_stmt*);

查询一行, 打印一行, 并存入数组.

    int index = 0;
    while (sqlite3_step(pstmt) == SQLITE_ROW)
    {
        const unsigned char *guPiaoName = sqlite3_column_text(pstmt, 0);
        const unsigned char *time = sqlite3_column_text(pstmt, 1);
        double kaiPan = sqlite3_column_double(pstmt, 2);

        printf("%d\t| %s\t| %s\t| %f\n", index, guPiaoName, time, kaiPan);

        strcpy(guPiaoShuJuArr[index].GuPiao, (const char *)guPiaoName);
        strcpy(guPiaoShuJuArr[index].ShiJian, (const char *)time);
        guPiaoShuJuArr[index++].KaiPan = kaiPan;
    }

以下函数获取查询一行后 iCol 列中的数据, 需要确定数据类型.

const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
double sqlite3_column_double(sqlite3_stmt*, int iCol);
int sqlite3_column_int(sqlite3_stmt*, int iCol);

完整示例:

#include "sqlite3.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

#define STRINGSIZE 16

typedef struct
{
    char GuPiao[STRINGSIZE];
    char ShiJian[STRINGSIZE];
    double KaiPan;
} GuPiaoShuJu;

// 获取查询的第一个值, 存入 charArr
static int getCount(void *charArr, int argc, char **argv, char **azColName)
{
    strcpy(charArr, argv[0]);
    printf("%s\n", argv[0]);
    return 0;
}

int main()
{
    SetConsoleOutputCP(CP_UTF8);
    SetConsoleCP(936);

    sqlite3 *dataBase;

    int result = sqlite3_open("gupiao3.db", &dataBase);
    if (result)
    {
        fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(dataBase));
        exit(0);
    }
    else
    {
        fprintf(stderr, "Opened database successfully\n");
    }

    char *zErrMsg = NULL;

    char num[STRINGSIZE] = "";

    result = sqlite3_exec(
        dataBase,
        "SELECT COUNT(*) FROM gupiaochi WHERE GuPiao LIKE 'SH600000';",
        getCount, num, &zErrMsg);

    int dataNum = atoi(num);
    GuPiaoShuJu *guPiaoShuJuArr = malloc(sizeof(GuPiaoShuJu) * dataNum);
    memset(guPiaoShuJuArr, 0, sizeof(GuPiaoShuJu) * dataNum);

    sqlite3_stmt *pstmt;
    const char *sql = "SELECT* FROM gupiaochi WHERE GuPiao LIKE ?;";
    result = sqlite3_prepare_v2(dataBase, sql, (int)strlen(sql), &pstmt, NULL);

    result = sqlite3_bind_text(pstmt, 1, "SH600000", 8, NULL);

    int index = 0;
    while (sqlite3_step(pstmt) == SQLITE_ROW)
    {
        const unsigned char *guPiaoName = sqlite3_column_text(pstmt, 0);
        const unsigned char *time = sqlite3_column_text(pstmt, 1);
        double kaiPan = sqlite3_column_double(pstmt, 2);

        printf("%d\t| %s\t| %s\t| %f\n", index, guPiaoName, time, kaiPan);

        strcpy(guPiaoShuJuArr[index].GuPiao, (const char *)guPiaoName);
        strcpy(guPiaoShuJuArr[index].ShiJian, (const char *)time);
        guPiaoShuJuArr[index++].KaiPan = kaiPan;
    }

    sqlite3_finalize(pstmt);
    sqlite3_close(dataBase);
    free(guPiaoShuJuArr);
    
    return 0;
}

编译参数:

 E:\msys64\clang64\bin\gcc.exe -ggdb addvalue4_01.c -o addvalue4_01 -L. -lsqlite3 

总结

sqlite3是使用最多的数据库, 因其短小精悍, 功能不弱, 为开发者所喜爱. 使用不难, 如果会sql语句以及C语言,很容易上手.

另外chitgpt现在也比较好用了, 查函数原型, 基本差不离, 这回没有出现自造函数的现象, 挺好.


老林的C语言新课, 想快速入门点此 <C 语言编程核心突破>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不停感叹的老林_<C 语言编程核心突破>

不打赏的人, 看完也学不会.

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值