更详细地介绍sunpinyin的用户辞典
〔来龙去脉〕
struct UserDict { UserDict () : m_db(NULL) {} ~UserDict (); bool load(const char* fname); unsigned addWord(CSyllables &syllables, const wstring& word); bool _createTable(); bool _createIndexes(); sqlite3* m_db; }; bool UserDict::load(const char* fname) { int rc = sqlite3_open(fname, &m_db); if (rc != SQLITE_OK) { sqlite3_close(m_db); return false; } return _createTable() && _createIndexes(); } UserDict::~UserDict() { if (m_db) { sqlite3_close(m_db); m_db = NULL; } } unsigned UserDict::addWord(CSyllables &syllables, const wstring& word) { assert(m_db); assert(syllables.size() >= 2 && syllables.size() <= MAX_USRDEF_WORD_LEN); sqlite3_stmt *stmt; const char *sql_str = "INSERT INTO dict (len, i0, f0, t0, i1, f1, t1, i2, f2, t2, i3, f3, t3, i4, f4, t4, i5, f5, t5, utf8str) \ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"; const char *tail; sqlite3_prepare(m_db, sql_str, strlen(sql_str), &stmt, &tail); int i = 1; sqlite3_bind_int(stmt, i++, syllables.size()); CSyllables::iterator it = syllables.begin(); CSyllables::iterator ite = syllables.end(); for (; it != ite; ++it) { sqlite3_bind_int(stmt, i++, it->initial); sqlite3_bind_int(stmt, i++, it->final); sqlite3_bind_int(stmt, i++, it->tone); } while (i <= MAX_USRDEF_WORD_LEN * 3 + 1) sqlite3_bind_int(stmt, i++, 0); char buf[MAX_USRDEF_WORD_LEN * 6 + 1]; WCSTOMBS(buf, word.c_str(), sizeof(buf) - 1); sqlite3_bind_text(stmt, i, (const char*)buf, strlen(buf), NULL); unsigned ret = (SQLITE_DONE == sqlite3_step(stmt)) ? INI_USRDEF_WID + sqlite3_last_insert_rowid(m_db) : 0; sqlite3_finalize(stmt); return ret; } bool UserDict::_createTable() { assert(m_db); char *zErr = NULL; int rc = SQLITE_OK; const char *sql_str = "CREATE TABLE IF NOT EXISTS dict( \ id INTEGER PRIMARY KEY, len INTEGER, \ i0 INTEGER, i1 INTEGER, i2 INTEGER, i3 INTEGER, i4 INTEGER, i5 INTEGER, \ f0 INTEGER, f1 INTEGER, f2 INTEGER, f3 INTEGER, f4 INTEGER, f5 INTEGER, \ t0 INTEGER, t1 INTEGER, t2 INTEGER, t3 INTEGER, t4 INTEGER, t5 INTEGER, \ utf8str TEXT, UNIQUE (i0, i1, i2, i3, i4, i5, utf8str));"; rc = sqlite3_exec(m_db, sql_str, NULL, NULL, &zErr); if (rc != SQLITE_OK) { if (zErr != NULL) { fprintf(stderr, "SQL error: %s\n", zErr); sqlite3_free(zErr); } return false; } return true; } bool UserDict::_createIndexes() { assert(m_db); char *zErr = NULL; int rc = SQLITE_OK; const char * sql_str = "CREATE INDEX IF NOT EXISTS index_0 ON dict (len, i0, i1, i2, i3, i4, i5);"; rc = sqlite3_exec(m_db, sql_str, NULL, NULL, &zErr); if (rc != SQLITE_OK) { if (zErr != NULL) { fprintf(stderr, "SQL error: %s\n", zErr); sqlite3_free(zErr); } return false; } return true; } TSyllable (*py2i)(const char*) = CPinyinData::encodeSyllable; int main(int argc, char** argv) { if (argc != 2) return 1; FILE* fp = fopen(argv[1], "rt"); if (!fp) return 1; UserDict ud; if (!ud.load("userdict")) return 1; // 文件可以不存在 char s[256]; CSyllables slbs; // typedef vector<TSyllable> CSyllables setlocale(LC_CTYPE, ""); while (fgets(s, sizeof(s), fp)) { printf("%s", s); static const char D[] = " \t\r\n"; char* hz = strtok(s, D); if (!hz) continue; uint32_t wcs[16] = {}; // wstring不是basic_string<wchar_t> int n = mbstowcs((wchar_t*)wcs, hz, sizeof(wcs)/sizeof(wcs[0])); slbs.resize(n); for (int i = 0; i < n; i++) { char* pinyin = strtok(NULL, D); slbs[i] = py2i(pinyin); } ud.addWord(slbs, wcs); } puts(""); fclose(fp); return 0; } // g++ -I/usr/include/sunpinyin-2.0 jiaci.cpp -lsunpinyin -lsqlite3 #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <sqlite3.h> #include <ime-core/userdict.h> #include <pinyin/pinyin_data.h>
lsof看不到/home/user/.sunpinyin/userdict被谁打开,可能它是隔一会存一次吧。killall fcitx后再改。
i代表initial,大概是声母。辅音的英语是consonant.
f代表final,大概是韵母。元音的英语是vowel.
t代表tone,声调。
CREATE TABLE IF NOT EXISTS dict(
id INTEGER PRIMARY KEY, len INTEGER,
i0 INTEGER, i1 INTEGER, i2 INTEGER, i3 INTEGER, i4 INTEGER, i5 INTEGER,
f0 INTEGER, f1 INTEGER, f2 INTEGER, f3 INTEGER, f4 INTEGER, f5 INTEGER,
t0 INTEGER, t1 INTEGER, t2 INTEGER, t3 INTEGER, t4 INTEGER, t5 INTEGER,
utf8str TEXT, UNIQUE (i0, i1, i2, i3, i4, i5, utf8str))
CREATE INDEX IF NOT EXISTS index_0 ON dict (len, i0, i1, i2, i3, i4, i5)
id INTEGER PRIMARY KEY 创建自增主键,提供快速的单条记录查找。
创建复合索引,支持范围查询和等值查询的高效执行。
sql_str = sqlite3_mprintf(
"SELECT id, utf8str FROM dict WHERE len=%i%q%q%q;",
length, i_conditions.c_str(), f_conditions.c_str(), t_conditions.c_str());
那些conditions是and who=who拼接起来的。
sqlite3_mprintf具有一些专为SQLite设计的特性和安全增强。
%q类似%s,但会自动转义其中的单引号字符。这有助于防止SQL注入攻击。q代表query.
和苦哈哈地自己撸用户辞典代码相比,这也太爽了,人还是带journal的。
注意AI说:是的,你可以将sqlite3_prepare_v2移到函数外部,像编译正则表达式那样只执行一次,这在需要重复执行相同SQL语句的场景中能显著提升性能。
(这个锅我不背,我是扒的别人的代码)
AI还说:在sqlite3_prepare中,如果模式发生改变(例如表结构被修改),已编译的语句可能会在后续执行时出现错误,而sqlite3_prepare_v2通过返回更具体的错误代码来应对这种情况。

浙公网安备 33010602011771号