此文已由作者严跃杰授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
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
{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
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]
flag += 1;
} else {
flag += 2;
}
if ((int)d[0] > HANZI_MAX || (int)d[0]
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
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 <
return false;
}int ret = sqlite3_open(path.c_str(), &db);
if(ret != SQLITE_OK){
cout <
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+款云产品!
更多网易技术、产品、运营经验分享请点击。