Sqlite是一个用C语言实现的小型SQL数据库引擎。它体积小巧但功能强大,对硬件资源要求很低而且性能表现卓越,非常适合于嵌入式应用环境。最近发现sqlite并不支持中文(拼音/笔画)排序,而这个功能又是我们必需的,所以花了些时间去研究。

  我们知道,计算机中的每一个字符都有一个内码。在默认情况下,计算机排序时,比较两个字符的大小就是比较字符内码的大小,这对于英文来说没有问题,因为英文字母的内码是按字母顺序递增的。对于中文来说,就比较麻烦了:首先,中文的排序方式有多种,比如按内码排序、按拼音排序和按笔画排序,要通过参数指定排序的方式,否则计算机就按内码排序了。其次,汉字的内码顺序即不同于拼音顺序,也不同于按笔画顺序。在GB2312编码中,汉字基本上按拼音排序(据说有例外,不太清楚)。在GBK中,它在GB2312基础上进行了扩充,兼容GB2312中的所有字符,所以不是按拼音排序了。在 Unicode中,汉字的排列似乎更没有什么规律可言了。

  为了解决内码顺序与用户习惯顺序(如拼音顺序)的冲突,在glibc的 locale数据里,要求提供排序方式(collate)的描述。我看了一下 glibc-2.3.5提供的locale数据,在简体中文(zh_CN)的locale数据描述里,关于排序方式的描述如下:

  % ISO 14651 collation sequence

  LC_COLLATE

  copy "iso14651_t1"

  END LC_COLLATE

  也就是说,照抄iso14651_t1的排序方式。打开iso14651_t1文件看了一下,也没有发现关于中文的特殊处理,可以推断glibc默认的排序方式就是按unicode排序。所以不能指望glibc提供中文排序功能,如果SQLite支持了中文排序只能是做了特殊处理。浏览了一下SQLite的代码,这种希望似乎也不大。

  不过,在浏览SQLite代码时还是有些收获,至少知道了它比较数据记录的过程:

  1. sqlite3VdbeExec调用sqlite3BtreeInsert把记录插入到适当的位置。

  2. sqlite3BtreeInsert调用sqlite3BtreeMoveto找到要插入的位置。

  3. sqlite3BtreeMoveto调用sqlite3VdbeRecordCompare比较两条记录的大小。

  4. sqlite3VdbeRecordCompare调用sqlite3MemCompare比较字段的大小。

  5. sqlite3MemCompare调用binCollFunc去做真正的比较。

  6. binCollFunc是一个回调函数,由外层设置的。

  进一步研究,知道了binCollFunc的来源:

  1. struct CollSeq是一个用来比较的对象,它带有一个比较函数和相关上下文。

  2. 通过multiSelectCollSeq找到合适的CollSeq对象。

  3. multiSelectCollSeq调用sqlite3ExprCollSeq查找。

  4. multiSelectCollSeq调用sqlite3CheckCollSeq查找。

  5. 查找标准是SELECT或CREATE TABLE所带的COLLATE子句。

  6. 也就是说可以通过SELECT或CREATE TABLE的参数来决定选择哪个比较函数。

  基于上面这些认识,我们知道比较函数是可以指定的了。接下来的问题是,我们能否自定义比较函数,如何自定义,以及如何安装到SQLite里。很快发现SQLite已经提供了安装比较函数的接口:

  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的比较函数。我们发现,在main.c里已经安装了一些内置的比较函数:

  sqlite3_create_collation(db, "BINARY", SQLITE_UTF8, 0,binCollFunc);

  sqlite3_create_collation(db, "BINARY", SQLITE_UTF16, 0,binCollFunc);

  sqlite3_create_collation(db, "NOCASE", SQLITE_UTF8, 0, nocaseCollatingFunc);

  好了,原理清楚了,我们要做的只是提供一个比较函数,并且安装进去就OK了。为了测试,我写一个按拼音排序的比较函数(按笔画排序类似):

  int pinyin_cmp(

  void *NotUsed,

  int nKey1, const void *pKey1,

  int nKey2, const void *pKey2)

  {

  int n = nKey1 < nKey1 ? nKey1 : nKey2;

  return pinyin_strncmp(pKey1, pKey2, n + 1);

  }

  安装比较函数时要注意,因为我们实现的比较函数是针对UTF-16的,所以名字要用UTF-16编码。但是由于linux下默认的wchar_t是32位的,不能直接用L”pinyin”的方式把ANSI字符串转换成UTF-16字符串,只能按下列方式。

  unsigned short zName[] = {'p', 'i', 'n', 'y', 'i', 'n', 0};

  sqlite3_create_collation16(db, zName, SQLITE_UTF16, 16, pinyin_cmp);

  测试结果正常(红色部分为按拼音排序,蓝色部分为默认排序):

  sqlite> create table person(name text, age int);

  sqlite> insert into person values("张三", 23);

  sqlite> insert into person values("张三丰", 23);

  sqlite> insert into person values("李四", 24);

  sqlite> insert into person values("李四叔", 24);

  sqlite> insert into person values("王五", 25);

  sqlite> insert into person values("王五妹", 25);

  sqlite> insert into person values("赵七", 26);

  sqlite> insert into person values("赵七姑", 26);

  sqlite>

  sqlite> select * from person order by name collate pinyin;

  李四|24

  李四叔|24

  王五|25

  王五妹|25

  张三|23

  张三丰|23

  赵七|26

  赵七姑|26

  sqlite> select * from person order by name;

  张三|23

  张三丰|23

  李四|24

  李四叔|24

  王五|25

  王五妹|25

  赵七|26

  赵七姑|26

  总结:SQLite的架构设计非常优秀,接口定义得也比较合理,支持中文排序变得非常简单。

转载于:https://www.cnblogs.com/pengyingh/articles/2379599.html

iPhone开发之SQLite 实现中文排序的教程相关推荐

  1. iPhone开发之SQLite

    现在网站开发和软件开发,数据库的支持是少不了的:在iPhone开发中,作为数据持久化的解决方案中,SQLite是不错的选择,它既轻量占用资源少,又可以方便嵌入到程序中,在一些嵌入式设备中有着广泛使用. ...

  2. 详解iPhone开发之Objective-C和 C 混编

    详解iPhone开发之Objective-C和 C 混编 2011-07-29 15:47 佚名 互联网 字号:T | T 本文介绍的是详解iPhone开发之Objective-C和C混编,介绍了ip ...

  3. 【无限互联】iOS开发视频教程— 2.8 iPhone开发之swtch语句

    核心内容 1. switch语句语法 2. 防止case穿透,与break结合使用 视频地址:iPhone开发之swtch语句

  4. iphone开发之Google地图实现…

    原文地址:iphone开发之Google地图实现 学习随笔 作者:若水一叶 摘自博文:http://tergol.blog.163.com/blog/static/170695028201081961 ...

  5. Android自动手绘,Android应用开发之Android 实现手绘功能教程

    本文将带你了解Android应用开发Android 实现手绘功能教程,希望本文对大家学Android有所帮助. 布局文件如下. Activity代码如下,其中线的颜色,宽度等属性都可以修改. pack ...

  6. ANDROID开发之SQLite详解

    SQLite简介 Google为Andriod的较大的数据处理提供了SQLite,他在数据存储.管理.维护等各方面都相当出色,功能也非常的强大.SQLite具备下列特点: 1.轻量级 使用 SQLit ...

  7. IOS开发之sqlite封装

    上一节实现了最基本的增删改查,所有操作数据库的方法都写在控制器里,这样会有一个问题,如果修改CURD(增删改查)操作方法会非常麻烦,这一节我们对CURD进行封装,在控制器里直接调用封装好的工具. 下面 ...

  8. android开发之 SQLite(数据库)

    SQLite数据库存储:SQLite是一款轻量级的关系型数据库,它的运算速度非常快, 占用资源很少,通常只需要几百 K的内存就足够了,因而特别适合在移动设备上使用. 第一: 创建一个数据库.(Andr ...

  9. iphone开发之C++和Objective-C混编

    C++和Objective-C混编(官方文档翻译) 原文网址: http://developer.apple.com/iphone/library/documentation/Cocoa/Concep ...

最新文章

  1. APT 信息收集——shodan.io ,fofa.so、 MX 及 邮件。mx记录查询。censys.io查询子域名。...
  2. [笔记] systemverilog学习笔录
  3. CentOS下Storm 1.0.0集群安装详解
  4. win7电脑上的文件打开方式选错了怎么办
  5. 奔腾双核linux服务器,Dell推出双核心奔腾服务器
  6. Office365下部署SharePoint站点集
  7. 计算机IP掩码的与运算,计算机IP地址与子网掩码如何进行AND运算
  8. 【计算方法】数值积分
  9. 数字孪生CIM智慧城市BIM,城市cim可视化解决方案公司
  10. 海明贴近度matlab,Matlab学习系列23.-模糊聚类分析原理及实现.docx
  11. 机器学习中,矩阵转置的求导运算
  12. pmp十大知识领域,49个过程的4W1H
  13. Excel快速排查重复数据的几种方法?
  14. FPGA数字IC的Verilog刷题解析基础版02——T触发器(异步复位和同步复位)
  15. 盘点Windows10系统的使用小技巧二 —— 磁贴
  16. visual studio2017快捷键的定制和导出导入的技巧与按键失灵分享[玩eclipse的很容易上手]...
  17. idea程序参数program arguments,vm arguments,Environment variable
  18. 初生牛犊,希望早日变成大牛!
  19. 云主机怎么安装mysql_华为云主机安装Mysql
  20. Oracle基础教程

热门文章

  1. 令人肝肠寸断的100个签名
  2. 腾讯技术解读|CDG—金融科技和腾讯广告AMS的神秘武器
  3. 解套操作的主要技巧谈
  4. VM虚拟机20G磁盘扩展到40G的Linux操作记录
  5. java 消息签名_微信公众平台消息体签名及加解密实例(Java)
  6. 根据百度地图进行IP定位获取地址
  7. 基于RxJava2.0+Retrofit2.0超大文件分块(分片)上传(带进度)
  8. 什么是 游戏引擎 ?各个主流引擎的区别
  9. 萨摩耶数科林建明:坚守“终局思维” 让金融科技发展行稳致远
  10. 怎样循序渐进、有效地学习JavaScript(转)