乘着放假,帮老大做个系统以减轻他的工作强度。
用什么做,当然是ruby on rails+mysql啦。
又是一番安装、配置。然后开始写代码。很多人遇到的中文问题,我在上一次做练习的时候已经遇到并解决了。
当时发现,当浏览器使用简体中文gb2312时,ruby代码中的中文没有问题,但是从数据库中的中文是乱码。换成utf8,则相反,mysql中的中文正常,ruby中的代码有问题。当时,相关配置都没有做特别调整。后来的解决方案很简单,将ruby代码用utf8的格式存放。我一直在用editplus来编辑,因此只要另存为utf8格式,则一切搞定(提醒一下,用rails以及script\generate自动生成时,默认都是ansi,如果涉及到中文一定要改为utf8)。
这一次驾轻就熟,直接用了上次的经验。因此中文显示一切正常。顶多在布局文件里加上charset设定。
然而一切写完以后,才发现,按照中文字段进行排序,其结果始终很奇怪。几番搜索核查之后,得知是因为utf8中中文字符的顺序是乱得,而非拼音排序!
解决的过程大致有四个阶段:
一、首先当然是google和baidu,下面是被转载最多的文章,基本上用"mysql 中文排序"搜索,前n页都是对此的转载。提供了2种方法
方法1,一种解决方法是对于包含中文的字段加上"binary"属性,使之作为二进制比较,例如将"name char(10)"改成"name char(10)binary"。
方法2 ,如果你使用源码编译MySQL,可以编译MySQL时使用–with–charset=gbk 参数,这样MySQL就会直接支持中文查找和排序了。
这里说的语焉不详,方法2,实验起来太过麻烦,而且我的实验机器是win,如何编译还不知道。而且实际上这意味着需要采用gbk作为中文存储的字符编码。方法1,较为简单,试验之,无效。原因后来才明白。
二、后来查到这个网页,使我有点绝望
http://www.5ivb.com/Info/68/3390242/
我研究了一下,初步了解:用二进制 binary 排序的前提是把 gbk/gb2312 的汉字编码以 utf-8 的格式存进去,所以一个汉字被保存成 4 个字节,而不是真正的 3 个字节。如果数据库中真的以 utf-8 的字符集保存,则二进制 binary 排序会得出错误结果(非拼音排序)。另外,以 utf-8 保存 gbk/gb2312 的编码会带来 char_length() 错误的问题。
基本有结果了,看了 mysql 的文档后,明白了直接的排序办法没有!
让我们期待 utf8_chinese_ci 的发布。
在这之前只能考虑间接的办法了。
三、于是开始决定放弃utf8,修改为gbk或者gb2312
查看了若干网页,试验若干次,终于发现一下几点:
1、我的浏览器没有gbk编码(可能没有安装),它似乎看不了gbk的字符集的东西,那么想来很多浏览器默认也没有gbk编码,所以决定弃用gbk。(这一点没有仔细求证,或有不确之处,只是当时就这么想的)
2、要将我做好的系统整个改成gb2312,需要做以下的事情:
a.将所有的ruby文件改回ansi。
b.在mysql中,设置服务器、数据库、表以及要排序的中文字段的字符集编码均设为gb2312,将排序方式设为gb2312_chinese_ci ,其中,服务器的字符集和排序方式在my.cnf中设置,后面的三个可以使用sql命令alter database或者alter table来设置,当然我是用phpadmin设置的。
c.设置连接和客户端的字符集,其实我究竟设置的是哪一个我也不是很清楚,我设置的是ruby的database.yml
adapter: mysql
database:
username: root
password:
host:
encoding: gb2312
d.上面已经将整个过程中可能配置的编码方式都修改为了gb2312,但是还有重要的最后一点,就是在网页中,输入中文的时候,必须当时网页是采用gb2312显示的,此时你输入进去的字符才是gb2312,否则在后来的显示就会出现乱码。
当然上述的过程中也许有些设置可以不需要,比如,也许只要设置字段的编码和排序方式即可。未加尝试。总之,过程过于复杂,因而我也放弃了这样的想法。当然许多刚刚开始做系统的人当然可以一试。
四、那么对于我的系统,我该怎么办呢?
1、认真地研读了mysql5的文档。看到了mysql的解决方案,那就是convert()函数和cast()函数,一一试来。
@test = Province.find_by_sql("select *, cast( prov_name as CHAR character set gb2312) collate gb2312_chinese_ci as ordername from provinces order by ordername desc")
结果,不行,还是原来的奇怪样子。修改为使用convert函数
@test = Province.find_by_sql("select *, convert( prov_name using gb2312) as ordername from provinces order by ordername desc")
还是一样。原因至今不明
2、最后,决定从ruby下手,查询的结果ruby会放在数组之中,每个数组元素是哈希表形式的一条记录。将查询的结果以新的方式重新排序不就可以了吗?
幸好我们有可爱的iconv。(注意有时候安装ruby时可能没有安装iconv,需要额外安装一下)。这是个负责在字符集间进行转换的函数库。如果要使用它,还需要在controller中加上 require ‘iconv’
下面就是我的实现,当然也实验了多次,查看了半天ruby的文档才得到的结果
conv = Iconv.new("GBK", "utf-8")
ordername from provinces order by ordername desc")
@test1 = Province.find(:all).sort {|x, y| conv.iconv(x.prov_name) <=> conv.iconv(y.prov_name)}
它会按照省名的gbk字符进行排序,但是并不会修改其中的内容,也就是说显示时仍然是utf8格式。
终于搞定了,我也终于写完了,当然这过程我明白了许多其中的机制,字符集是复杂的问题。我会再写一篇来完成的写我的心得。下一篇吧。
我用你的方法做的排序,不过好像有点问题呀,省份的排序最后一个变成了重庆,这是什么原因呀,我数据库是utf8
def sort_by_gbk(collection,method=:name)
conv = Iconv.new("GBK", "utf-8")
return collection.sort {|x, y| conv.iconv(x[method]) <=> conv.iconv(y[method])}
end
这样用的
我的信箱:kaiye85@gmail.com
Kevin —— 2006年12月11日 @12:55 am
抱歉阿,你的情况我也不太明白,也许你可以试试让它打印出来一些中间结果,看看究竟哪里出现了问题,比如conv.iconv(a[method])<=>conv.iconv(b[method]),其中a[:name]为重庆,b[:name]为浙江,看看它的结果是什么。Good Luck!
jenni —— 2006年12月25日 @10:06 am
// ==============
// PHP 解决一法:
// 作者:JES(suenjes@sohu.com)
// 2007.2.7
// ==============
function SortUtf8($x, $y) {
return -strcmp(iconv("UTF-8","GBK",$x), iconv("UTF-8","GBK",$y));
}
usort($chineseStrs, ‘SortUtf8′);
JES —— 2007年02月07日 @10:25 am
用了server/console做了验证
>> conv = Iconv.new("GBK", "utf-8")
=> #<Iconv:0xb6dc1f70>
>> c=%w(浙江 湖南 安徽 广东)
=> ["浙江", "湖南", "安徽", "广东"]
>> b=c.sort
=> ["安徽", "广东", "浙江", "湖南"]
>> b=c.sort {|x,y| conv.iconv(x) <=> conv.iconv(y)}
=> ["安徽", "广东", "湖南", "浙江"]
恩,现在看来这个方法是可行的
blueteeth —— 2009年04月04日 @10:51 pm
事实上,如果要分页的话,使用iconv就意味着每次都要重新读取所有的数据库,而不是使用分页函数直接得到相应的数据
mysql其实已经提供了基础的方法,不需要用iconv进行转换也可以排序
给个例子:
select * from users order by CONVERT(name using gb2312);
我不知道为什么你的select没有成功排序,但是我这个在我的数据库上是实验成功的
blueteeth —— 2009年04月19日 @8:36 am
谢谢,select * from users order by CONVERT(name using gb2312);
的方法最简单,实用。
quickstarcn —— 2009年04月27日 @9:16 am
“重”=zhong……数据库不认识多音字…………
LS的方法排不对的,“相”“株”“华”这几种常用的字貌似都不行……
六翼 —— 2009年11月10日 @3:05 pm
更正一下,“这种”,不是“这几种”……
六翼 —— 2009年11月10日 @3:05 pm
举例:
mysql> select name from vendor_units order by CONVERT(name using gb2312);
+——————————————–+
| name |
+——————————————–+
| 浜松ホトニクス株式会社 |
| ネオアーク株式会社 |
| 相干(北京)商业有限公司 |
| 广州市永大光通信技术发展有限公司 |
| 株式会社アルネアラボラトリ |
| 华中师范大学 |
| 北京爱万提斯科技有限公司 |
| 上海三菲电子科技有限公司 |
| 博创科技股份有限公司 |
| 美国莱特太平洋公司中国技术服务中心 |
| シグマ光機株式会社 |
| 武汉楚天激光(集团)股份有限公司 |
| 武汉光迅科技股份有限公司 |
| 武汉驿路通光讯有限公司 |
| 东隆科技有限公司 |
| 新美亚科技(深圳)有限公司 |
| 古河电气工业株式会社 |
| Alfalight, Inc. |
| Alpes Lasers SA |
……
| Thorlabs, Inc. |
| Wavelength Electronics Inc. |
| Wavelink Corporation |
| Yena |
| YSI(hongkong) limited |
| 駿河精機株式会社 |
| 莱尔德电子材料(深圳)有限公司 |
| 贰陆光学(苏州)有限公司 |
+——————————————–+
134 rows in set
六翼 —— 2009年11月10日 @3:08 pm