【数据库】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数据库中无法正常显示。
相关推荐
嵌入式科普1 小时前
嵌入式科普(24)从SPI和CAN通信重新理解“全双工”
c语言·stm32·can·spi·全双工·ra6m5
YashanDB1 小时前
【YashanDB知识库】XMLAGG方法的兼容
数据库·yashandb·崖山数据库
独行soc1 小时前
#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍11基于XML的SQL注入(XML-Based SQL Injection)
数据库·安全·web安全·漏洞挖掘·sql注入·hw·xml注入
lqqjuly2 小时前
特殊的“Undefined Reference xxx“编译错误
c语言·c++
风间琉璃""2 小时前
bugkctf 渗透测试1超详细版
数据库·web安全·网络安全·渗透测试·内网·安全工具
drebander2 小时前
SQL 实战-巧用 CASE WHEN 实现条件分组与统计
大数据·数据库·sql
IvorySQL2 小时前
IvorySQL 4.0 发布:全面支持 PostgreSQL 17
数据库·postgresql·开源数据库·国产数据库·ivorysql
18号房客3 小时前
高级sql技巧进阶教程
大数据·数据库·数据仓库·sql·mysql·时序数据库·数据库架构
Dawnㅤ3 小时前
使用sql实现将一张表的某些字段数据存到另一种表里
数据库·sql
张声录13 小时前
【ETCD】【实操篇(十二)】分布式系统中的“王者之争”:基于ETCD的Leader选举实战
数据库·etcd