SQLite3中文

1. C语言宽字符

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

#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'. */
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中有表:

CREATE TABLE IF NOT EXISTS "COMPANY"(
ID INTEGER PRIMARY KEY,
NAME TEXT NOT NULL,
AGE INTEGER NOT NULL
);
#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));
}
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 类型的存储函数。

int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));

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

CREATE TABLE IF NOT EXISTS "COMPANY_BLOB" (
ID INTEGER PRIMARY KEY,
NAME BLOB NOT NULL,
AGE INTEGER NOT NULL
);

代码如下:

#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));
}
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数据库中无法正常显示。
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐