在其他语言中使用UTF-8
Using UTF-8 with Other Languages
我们已经提到过的用于PHP的技术同样适用于其他缺乏核心Unicode支持的语言,包括版本为5.6以前的Perl和其他更久远的遗留语言。只要语言能透明地工作于字节流,我们就可以将字符串当成大块的内容未知的二进制数据传送。至于字符串的操作或验证,我们需要交付给专门的类库,比如iconv或者ICU来完成这些苦活。
很多语言现在都带有部分或全部内建的Unicode支持。Perl 5.8.0以后的版本可以透明地工作于Unicode字符串,而5.6.0版本通过使用use utf8 pragma已经能够部分支持Unicode。Perl 6计划有很广泛的Unicode支持,使你能够在字节、码点、音素层次操纵字符串。PHP 6计划将Unicode支持内置到语言中,这应该使移植现有代码成为不是那么痛苦的经历。Ruby 1.8并无明确的Unicode支持,像PHP一样,它把字符串看成8-bit字节的序列。计划在Ruby 1.9/2.0中会有某种程度的Unicode支持。
Java与.NET都有充分的Unicode支持,这意味着你可以跳过本章中那些烦人的补救方法,直接与语言里的字符串打交道。不过,即使是天生的Unicode字符串,你也永远需要确保你收到的来自外界的数据对于你所选择的编码是有效的。当你企图操作没有正确编码的字符串时,你的语言默认行为可能会是抛出一个错误,所以需要在输入边界过滤字符串,或
者在程序内部准备好捕获可能的异常。选择一本关于你所选择的语言并且专注于如何使用Unicode字符串的书看看是很值得的。
在MySQL中使用UTF-8
Using UTF-8 with MySQL
像PHP一样,只要你的方法支持原始字节流,那么它就支持UTF-8。MySQL确实支持字节流,因此,存储和获取UTF-8编码的字符串与存储简单的ASCII文本一样。
如果我们能够读取和写入数据,那还有什么可说的?但就像PHP一样,还是存在一些重要的问题。排序,你常常在数据库层而非编码层进行,也需要和Unicode数据打交道。很幸运,就像我们已经讨论过的,UTF-8可以二进制排序,然后得到按码点顺序排列的结果。这意味着MySQL的常规排序能很好地作用于UTF-8的数据,只要你确定你定义字段时使用BINARY属性(对于CHAR和VARCHAR字段)并使用BLOB代替TEXT类型即可。
与PHP一样,我们担心的是字符串操作。通常,只要将逻辑从SQL语句移到代码层,您就可以避免大部分的字符串操作。注意避免使用这种类型的SQL语句:
SELECT SUBSTRING(name, 0, 1) FROM UserNames;
而是将相同的逻辑移到业务逻辑层:
<?php
$rows = db_fetch_all("SELECT name FROM UserNames;");
foreach($rows as $k => $v){
$rows[$k]['name'] = mb_substr($v['name'], 0, 1);
}
>
如果你曾在SQL语句中使用substring来实现选择或关联,那在某些情况下会导致问题的产生,因为现在你再也不能执行这项操作了。至于可选的方案,要么是数据库内部有字符集支持(过一会儿我们将详细谈论),要么是重新安排数据来简化查询。比如,原来你对一组记录在某个字段的第一个字母执行一个substring操作,现在你可以将第一个字母(作为一组标准化的码点)存储在一个单独的字段中,直接使用该字段,以避免在数据库内部执行任何字符串操作。
MySQL 也有另一组字符串操作函数,它在幕后使用这些函数,它们很容易被忽略。为了创建全文索引,MySQL 需要切开输入字符串,形成不同的单词来单独索引。没有UTF-8的支持,Unicode字符串会被错误地分开,这样进行索引,会返回一些实在是很古怪且无法预料的结果。
和明确的文字符串操作函数不同,不从头改写文本索引,就没有办法把文本索引逻辑移到代码层。因为文本索引是一段颇为复杂的代码,而有人已经以MySQL的全文索引的方式为我们实现了这段代码,花时间来自己实现是对时间的很大浪费。幸好,MySQL 4.1版本使我们免于做这些工作,它具有多字符集和排序规则支持,包括UTF-8。在创建表时,可以为每列指定字符集,你也可以为服务器、数据库或表预设默认的字符集,免得每次当建立一个新列时都需要指定。然后这一列的数据就会按相应格式存储,可以使用通常的字符串操作函数,全文索引也能正确工作。将字符串长度从按描述字节改为按字符来计算也有很多优点。在4.1版本以前,一个MySQL的CHAR(10)的列类型是指10字节,所以你可以存储2到10个UTF-8字符。在4.1版本,CHAR(10)是指10个字符,因此可能需要10个或更多的字节。如果您担心空间不够,就应该避免使用CHAR类型(改为使用VARCHAR),因为对于每个字符可能要3字节的10个字符而言,CHAR(10)的列实际上需要30个字节。MySQL对于UTF-8目前有对于每个字符最多3个字节的限制。这意味着它不能存储U+FFFF以上的码点。这对大多数的人来说恐怕不是一个问题:这个区域包含音乐符号、古老的波斯文字、爱琴文化的数字等奇怪内容。但还是值得把有一些码点不能存储这个事实记在心里,而且在数据过滤代码中你也许希望考虑到这些。






