【数据库】SQLite3 中文存储

SQLite3中文

1. C语言宽字符

C95 开始,C语言提供 <wchar.h><wctype.h> 用于处理宽字符wide characters )。宽字符类型为 wchar_tchar 类型有的操作函数,wchar_t 都有对应的函数。

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <wchar.h>

int main(int argc, char *argv[]) {
    unsigned index = 0;
    char *name = "琉璃";
    unsigned length = strlen(name);

    printf("%s%lu%s\n",
            "The size of data-type named 'char' is ",
            sizeof(char),
            " bytes");
    printf("[%u]:%s(%lu bytes)\n",
            length,
            name,
            sizeof(name));

    for (index = 0; index < length; index++) {
        printf("[%u]%d\n", index, name[index]);
    }

    setlocale(LC_CTYPE, "UTF-8");	/* 设置本地化编码 */
    wchar_t *w_name = L"琉璃";		/* 宽字符串 */
    length = wcslen(w_name);		/* 获取宽字符串长度 */

    printf("%s%lu%s\n",
            "The size of data-type named 'wchar_t' is ",
            sizeof(wchar_t),
            " bytes");
    wprintf(L"[%u]:%ls(%lu bytes)\n",
            length,
            w_name,
            sizeof(w_name));

    for (index = 0; index < length; index++) {
        wprintf(L"[%u]%lc(U+%X)\n", index, w_name[index], w_name[index]);
    }

    return EXIT_SUCCESS;
}
/* The end of source file that is named 'main.c'. */
bash 复制代码
The size of data-type named 'char' is 1 bytes
[6]:琉璃(8 bytes)
[0]-25
[1]-112
[2]-119
[3]-25
[4]-110
[5]-125
The size of data-type named 'wchar_t' is 4 bytes
[2]:琉璃(8 bytes)
[0]琉(U+7409)
[1]璃(U+7483)

由上可以看出如果使用 char 类型也能正常显示,可是长度和数据内容不能正常显示。而 wchar_t 可以解决这问题。

2. SQLite3宽字符

SQLite3 中,字符串类型被称为 TEXT 类型,可以存储任意字符集的字符串数据,包括中文字符。SQLite3 默认使用UTF-8 编码来存储和处理 TEXT 类型数据,因此,可以存储和处理包括中文字符在内的多种字符集数据。

另外SQLite3 中还有一个类型为 BLOB 完全按照输入时的数据存储。

2.1 使用char类型存储

使用 char 存储UTF-8编码 的文本。代码如下:

SQLite3中有表:

sql 复制代码
CREATE TABLE IF NOT EXISTS "COMPANY"(
ID INTEGER PRIMARY KEY,
NAME TEXT NOT NULL,
AGE INTEGER NOT NULL
);
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>

void errorMsg(sqlite3 *db, const char *errStr);

int main(int argc, char *argv[]) {
    char *filename = "database.db";
    sqlite3 *db = NULL;
    int rc = SQLITE_OK;

    rc = sqlite3_open_v2(filename, &db, SQLITE_OPEN_READWRITE, NULL);
    if (rc != SQLITE_OK) {
        errorMsg(db, "Can't open database");

        rc = sqlite3_close_v2(db);
        if (rc != SQLITE_OK) {
            perror("Close database failure\n");
        }

        exit(EXIT_FAILURE);
    }

    sqlite3_stmt *stmt = NULL;

    char *sql = "INSERT INTO COMPANY(NAME, AGE) VALUES(?,?)";
    char *name = "琉璃";

    printf("%s%lu\n",
            "The size of 'char' is ", sizeof(char));
    printf("name is %s(%lu bytes)\n",
            name, sizeof(name));

    rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    if (rc == SQLITE_OK) {
        rc = sqlite3_bind_text(stmt, 1, name, -1, NULL);
        rc = sqlite3_bind_int(stmt, 2, 20);

        rc = sqlite3_step(stmt);
        if (rc != SQLITE_DONE) {
            errorMsg(db, "Sqlite3 execute step failure");
        }

        rc = sqlite3_reset(stmt);
        if (rc != SQLITE_OK) {
            errorMsg(db, "Sqlite3 execute reset failure");
        }
    }

    rc = sqlite3_finalize(stmt);
    if (rc != SQLITE_OK) {
        errorMsg(db, "Destruct the object of the type is sqlite_stmt failure");
    }

    char *sql_2 = "SELECT * FROM COMPANY";

    rc = sqlite3_prepare_v2(db, sql_2, -1, &stmt, NULL);
    if (rc == SQLITE_OK) {
        int id = 0;
        unsigned char *name = NULL;
        int age = 0;

        while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
            id = sqlite3_column_int(stmt, 0);
            name = (unsigned char*)sqlite3_column_text(stmt, 1);
            age = sqlite3_column_int(stmt, 2);

            printf("%s%d%s%s%s%d\n",
                    "ID = ", id,
                    " NAME = ", name,
                    " AGE = ", age);
        }
    }

    rc = sqlite3_finalize(stmt);
    if (rc != SQLITE_OK) {
        errorMsg(db, "Destruct the object of the type is sqlite_stmt failure");
    }
    
    rc = sqlite3_close_v2(db);
    if (rc != SQLITE_OK) {
        perror("Close database failure.\n");
    }

    return 0;
}

void errorMsg(sqlite3 *db, const char *errStr) {
    fprintf(stderr, "%s[%d]: %s\n",
            errStr,
            sqlite3_errcode(db),
            sqlite3_errmsg(db));
}
bash 复制代码
The size of 'char' is 1
name is 琉璃(8 bytes)
ID = 1 NAME = 琉璃 AGE = 20
ID = 2 NAME = 琉璃 AGE = 20

$ sqlite3 database.db 
SQLite version 3.43.2 2023-10-10 13:08:14
Enter ".help" for usage hints.
sqlite> SELECT * FROM COMPANY;
1|琉璃|20
2|琉璃|20
sqlite> 

优点:

  • C语言代码少,在SQLite3中也能正常显示。

缺点:

  • 无法正确的操作 char * 中的UTF-8编码

2.2 使用wchar_t类型存储

在SQLite3中没有 wchar_t 类型的存储函数。但有 BLOB 类型的存储函数。

c 复制代码
int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));

其中存储类型为 void * 类型,所以所有类型都可原样存储。

SQLite3中有表:

sql 复制代码
CREATE TABLE IF NOT EXISTS "COMPANY_BLOB" (
ID INTEGER PRIMARY KEY,
NAME BLOB NOT NULL,
AGE INTEGER NOT NULL
);

代码如下:

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <locale.h>
#include <sqlite3.h>

void errorMsg(sqlite3 *db, const char *errStr);

int main(int argc, char *argv[]) {
    char *filename = "database.db";
    sqlite3 *db = NULL;
    int rc = SQLITE_OK;

    rc = sqlite3_open_v2(filename, &db, SQLITE_OPEN_READWRITE, NULL);
    if (rc != SQLITE_OK) {
        errorMsg(db, "Can't open database");

        rc = sqlite3_close_v2(db);
        if (rc != SQLITE_OK) {
            perror("Close database failure\n");
        }

        exit(EXIT_FAILURE);
    }

    sqlite3_stmt *stmt = NULL;

    setlocale(LC_CTYPE, "UTF-8");
    char *sql_3 = "INSERT INTO COMPANY_BLOB (NAME, AGE) VALUES (?,?)";
    wchar_t *w_name = L"琉璃";

    printf("%s%lu\n",
            "The size of 'wchar_t' is ", sizeof(wchar_t));
    wprintf(L"w_name is %ls(%lu bytes)\n",
            w_name, sizeof(w_name));

    rc = sqlite3_prepare_v2(db, sql_3, -1, &stmt, NULL);
    if (rc == SQLITE_OK) {
        rc = sqlite3_bind_blob(stmt, 1, w_name, sizeof(w_name) + 1, NULL);
        rc = sqlite3_bind_int(stmt, 2, 20);

        rc = sqlite3_step(stmt);
        if (rc != SQLITE_DONE) {
            errorMsg(db, "Sqlite3 execute step failure");
        }

        rc = sqlite3_reset(stmt);
        if (rc != SQLITE_OK) {
            errorMsg(db, "Sqlite3 execute reset failure");
        }
    }

    rc = sqlite3_finalize(stmt);
    if (rc != SQLITE_OK) {
        errorMsg(db, "Destruct the object of the type is sqlite_stmt failure");
    }

    char *sql_4 = "SELECT * FROM COMPANY_BLOB";

    rc = sqlite3_prepare_v2(db, sql_4, -1, &stmt, NULL);
    if (rc == SQLITE_OK) {
        int id = 0;
        wchar_t *name = NULL;
        int age = 0;

        while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
            id = sqlite3_column_int(stmt, 0);
            name = (wchar_t*)sqlite3_column_blob(stmt, 1);
            age = sqlite3_column_int(stmt, 2);

            wprintf(L"%s%d%s%ls%s%d\n",
                    "ID = ", id,
                    " NAME = ", name,
                    " AGE = ", age);
        }
    }

    rc = sqlite3_finalize(stmt);
    if (rc != SQLITE_OK) {
        errorMsg(db, "Destruct the object of the type is sqlite_stmt failure");
    }
        
    rc = sqlite3_close_v2(db);
    if (rc != SQLITE_OK) {
        perror("Close database failure.\n");
    }

    return 0;
}

void errorMsg(sqlite3 *db, const char *errStr) {
    fprintf(stderr, "%s[%d]: %s\n",
            errStr,
            sqlite3_errcode(db),
            sqlite3_errmsg(db));
}
bash 复制代码
The size of 'wchar_t' is 4
w_name is 琉璃(8 bytes)
ID = 1 NAME = 琉璃 AGE = 20
ID = 2 NAME = 琉璃 AGE = 20

$ sqlite3 database.db 
SQLite version 3.43.2 2023-10-10 13:08:14
Enter ".help" for usage hints.
sqlite> SELECT * FROM COMPANY_BLOB;
1|	t|20
2|	t|20

优点:

  • C语言编码中 wchar_t 类型可以正确的操作UTF-8编码

缺点:

  • SQLite数据库中无法正常显示。
相关推荐
cookqq12 分钟前
mongodb源码分析session异步接受asyncSourceMessage()客户端流变Message对象
数据库·sql·mongodb·nosql
呼拉拉呼拉23 分钟前
Redis故障转移
数据库·redis·缓存·高可用架构
什么都想学的阿超26 分钟前
【Redis系列 04】Redis高可用架构实战:主从复制与哨兵模式从零到生产
数据库·redis·架构
pp-周子晗(努力赶上课程进度版)1 小时前
【MySQL】视图、用户管理、MySQL使用C\C++连接
数据库·mysql
斯特凡今天也很帅1 小时前
clickhouse常用语句汇总——持续更新中
数据库·sql·clickhouse
超级小忍2 小时前
如何配置 MySQL 允许远程连接
数据库·mysql·adb
吹牛不交税2 小时前
sqlsugar WhereIF条件的大于等于和等于查出来的坑
数据库·mysql
hshpy3 小时前
setting up Activiti BPMN Workflow Engine with Spring Boot
数据库·spring boot·后端
文牧之4 小时前
Oracle 审计参数:AUDIT_TRAIL 和 AUDIT_SYS_OPERATIONS
运维·数据库·oracle
篱笆院的狗4 小时前
如何使用 Redis 快速实现布隆过滤器?
数据库·redis·缓存