乘着放假,帮老大做个系统以减轻他的工作强度。

用什么做,当然是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格式。

终于搞定了,我也终于写完了,当然这过程我明白了许多其中的机制,字符集是复杂的问题。我会再写一篇来完成的写我的心得。下一篇吧。


9条评论

  1. 我用你的方法做的排序,不过好像有点问题呀,省份的排序最后一个变成了重庆,这是什么原因呀,我数据库是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

  2. 抱歉阿,你的情况我也不太明白,也许你可以试试让它打印出来一些中间结果,看看究竟哪里出现了问题,比如conv.iconv(a[method])<=>conv.iconv(b[method]),其中a[:name]为重庆,b[:name]为浙江,看看它的结果是什么。Good Luck!

  3. // ==============

    // 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′);

  4. 用了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)}

    => ["安徽", "广东", "湖南", "浙江"]

    恩,现在看来这个方法是可行的

  5. 事实上,如果要分页的话,使用iconv就意味着每次都要重新读取所有的数据库,而不是使用分页函数直接得到相应的数据

    mysql其实已经提供了基础的方法,不需要用iconv进行转换也可以排序

    给个例子:

    select * from users order by CONVERT(name using gb2312);

    我不知道为什么你的select没有成功排序,但是我这个在我的数据库上是实验成功的

  6. 谢谢,select * from users order by CONVERT(name using gb2312);

    的方法最简单,实用。

  7. “重”=zhong……数据库不认识多音字…………

    LS的方法排不对的,“相”“株”“华”这几种常用的字貌似都不行……

  8. 更正一下,“这种”,不是“这几种”……

  9. 举例:

    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

发表评论

评论也有版权!

click to change验证码