此文已由作者严跃杰授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
Sqlite3是一款C语言实现的小型SQL数据库引擎,它体积小巧但功能强大, 性能表现也非常不错, 因此在客户端及嵌入式应用开发中受到广泛的欢迎。在网易云音乐的开发中,我们就用到了sqlite3,但是不幸的是在开发过程中发现它不支持汉字拼音排序,但这又是必须的功能。今天我们就来看下如何为sqlite3添加汉字拼音排序的功能。
首先我们想到的是既然原生不支持拼音排序,那它是否提供了扩展接口允许我们添加自定义排序功能呢?通过网上查找和文档阅读,果不其然,它为提供了安装比较函数的接口:
int sqlite3_create_collation16( sqlite3* db, const char *zName, int enc, void* pCtx, int(*xCompare)(void*,int,const void*,int,const void*) )int sqlite3_create_collation( sqlite3* db, const char *zName, int enc, void* pCtx, int(*xCompare)(void*,int,const void*,int,const void*) )
前者用来安装UTF-16的比较函数,后者用来安装UTF-8的比较函数。
除了扩展接口,还有一项资源是必须要有的,当然就是汉字拼音库。网上一通猛找,发现没有合适的。后来发现是云音乐资源包里已经有一个现成的汉字拼音库,省却了不少麻烦(共享在附件工程解决方案目录下)。
必要的资源有了,就可以着手实现了。
以下是获取汉字拼音实现
std::wstring LetterHelp::ConvertLetterToPinyin(const std::wstring& chinese) { std::wstring str_pinyin; for(unsigned int i = 0; i < chinese.length(); i++) { wchar_t tch = chinese[i]; const char *pinyin_char = GetLetter((unsigned short)tch); if(pinyin_char) { int len = strlen(pinyin_char); wchar_t* wch = new wchar_t[len]; FUTF82WConvert(pinyin_char,wch, len); str_pinyin.append(wch); } else { str_pinyin.push_back(tch); } } return str_pinyin; }const char* LetterHelp::GetLetter(unsigned short ch) { if(ch >= HANZI_MIN && ch <= HANZI_MAX) return m_pinyin + m_code[ch - HANZI_MIN]; else return nullptr; }
以下比较函数实现:
// 两个都是汉字时比较函数static int pinyin_strcmp(const wchar_t *key1, const wchar_t *key2) { std::wstring str1 = LetterHelp::getInstance()->ConvertLetterToPinyin(key1); std::wstring str2 = LetterHelp::getInstance()->ConvertLetterToPinyin(key2); return wcscmp(str1.c_str(), str2.c_str()); }// 升序排序时将数字,字母排到汉字前static int pinyin_cmplmpl(int nKey1, const void *pKey1, int nKey2, const void *pKey2) { int size = min(nKey1, nKey2); int i = 0, flag = 0, ret = 0;; wchar_t s[2]={0}, d[2]={0}; for (i = 0; i < size; i++) { flag = 0; ret = 0; s[0] = *((wchar_t*)pKey1+i); d[0] = *((wchar_t*)pKey2+i); if ((int)s[0] > HANZI_MAX || (int)s[0] < HANZI_MIN) { flag += 1; } else { flag += 2; } if ((int)d[0] > HANZI_MAX || (int)d[0] < HANZI_MIN) { flag += 4; } else { flag += 8; } switch(flag) { case 5: ret = wcscmp(s, d); if (ret != 0) { return ret; } break; case 9: return -1; case 6: return 1; case 10: ret = pinyin_strcmp(s, d); if (ret != 0) { return ret; } break; } } if (i >= size && nKey1 == nKey2) { return 0; } else if (i >= size && nKey1 < nKey2) { return -1; } else if(i >= size && nKey1 > nKey2) { return 1; } return -1; }// 升序比较函数static int pinyin_cmp_asc( void *NotUsed, int nKey1, const void *pKey1, int nKey2, const void *pKey2) { return pinyin_cmplmpl(nKey1,pKey1,nKey2,pKey2); }// 降序比较函数static int pinyin_cmp_desc( void *NotUsed, int nKey1, const void *pKey1, int nKey2, const void *pKey2) { int ret = pinyin_cmplmpl(nKey1,pKey1,nKey2,pKey2); if (ret == 0) { return ret; } else { return -ret; } }
在打开数据库后安装汉字拼音比较函数
// 打开数据库文件并安装汉字拼音比较函数bool Db::open(const string path) { if (db != nullptr) { cout << "db file: " << path << " has been opened, cannot reopen." << endl; return false; } int ret = sqlite3_open(path.c_str(), &db); if(ret != SQLITE_OK){ cout << "Cannot open Db file: "<< path << ", errmsg: "<< sqlite3_errmsg(db); return false; } unsigned short asc[] = {'p', 'i', 'n', 'y', 'i', 'n', '_', 'a', 's', 'c', 0}; sqlite3_create_collation16(db, asc, SQLITE_UTF16, 0, pinyin_cmp_asc); unsigned short desc[] = {'p', 'i', 'n', 'y', 'i', 'n', '_', 'd', 'e', 's', 'c', 0}; sqlite3_create_collation16(db, desc, SQLITE_UTF16, 0, pinyin_cmp_desc); return true; }
最后如下sql进行测试
select * from person order by name collate pinyin_asc select * from person order by name collate pinyin_desc
测试结果如下
网易云免费体验馆,0成本体验20+款云产品!
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 代码混淆防止APP被反编译指南
【推荐】 pdfjs viewer 开发小结