特效快速专治Java、Python中文问题

Java篇

  1. 有一个GB2312的字节串(byte[]),print输出乱码……

    特效方:

    byte[] chinese = …; //
    String unicode = new String(chinese, “GB2312”);

    如果要转回字节串:

    byte[] uni_chinese = unicode.getBytes();

    简析:Java的标准输出函数只认Unicode的,直接输出当然乱码了;转成String,虽然String能够存储Unicode,但是你存进去的还是GB2312,没有经过重新编码,当然乱码了。其他如Big5等同用此方。

  2. 有一个String,里面编了的却是GB2312,怎么输出都得不到正确的数据……特效方:

    string = new String( string.getBytes(“iso-8859-1”), “GB2312”);

    简析:和1类似,iso-8859-1是单字符编码,读出最原始的数据后再按GB2312来解释,转换成Unicode存入String。

  3. 写入非UTF-8的文件(流)……

    处方一:转为相应编码的字节串后如常调用OutputStream.write(bytes);

    处方二:

    Writer ow = new OutputStreamWriter(new FileOutputStream(“2.txt”), “GB2312”);
    ow.write(string);
    ow.close();

    简析:注意OutputStreamWriter的ctor参数。

Python篇

  1. 带中文的源文件运行报错SyntaxError: Non-ASCII character ‘…’ in file …

    特效方:

    在源文件的第一行或第二行添加上

    # -*- coding: utf-8 -*-


    简析:这是指定源文件的编码,当然不一定是utf-8,这需要你自己根据实际填写。如果是Windows下记事本默认的保存格式,则是gbk了。注意,源文件声明的是什么编码,程序里面定义的“字节串”就是什么编码的了。

  2. 有一个GB2312的字节串,print输出乱码……特效方:

    chinese = …read_from_some_where
    uni_chinese = chinese.decode(“gb2312”)
    print uni_chinese

    uni_chinese不是字节串了,是Unicode串。如果要重新转为字节串:

    utf_chinese = uni_chinese.encode(“utf8”)

    连用的话:

    utf_chinese = chinese.decode(“gb2312”).encode(“utf8”)

    注意这里的”utf8″要跟源代码的编码一致(注1),否则依然可能乱码。

    总之:对字节串用decode()变成Unicode串,对Unicode串用encode()变成字节串。

  3. 我的源代码编码是 # -*- coding:gb2312 -*-,上面的方法不管用,print任何Unicode串都出错:UnicodeEncodeError: ‘ascii’ codec can’t encode characters in position x-x: ordinal not in range(128)特效方:

    print uni-str.encode(“gb2312”)

    简析:能是能用了,但是太碍眼了点。这个故事告诉我们别用gb2312来编码python源码,尽量使用UTF-8。(别告诉我你用Win98)

  4. print格式化输出时候出错!也是UnicodeEncodeError: ‘ascii’ codec can’t encode characters in position x-x: ordinal not in range(128) ,代码是

    tu = (u”中文”,”中文”,”1234″)
    print u”%s %s %s” % tu

    特效方:

    没救!字节串和Unicode串不能同时格式化到一个串内print,除非手动转到相应格式。

  5. 其他问题……如读写文件、网络流,不过都大同小异,读写文件可以指定打开的编码,Python帮你自动转换……其他如unicode对象、codecs模块就不详了,如需深入推荐阅读How to Use UTF-8 with Python

总结篇

似乎相比之下,Python处理中文比Java更为麻烦,这和人们对两种语言的第一印象可是截然相反……也许可以认为是Python太过灵活所致,变量都不用声明类型,那个是字节串哪个是Unicode串可真不好分,不过这也是弱类型语言的特点吧。

为什么会有中文问题?如果所有程序都是处理源文件里面写的文字,那怎么也不可能出现问题,但是我们的程序经常要从文件、从网络、从数据库里面把数据提取出来,这些数据的格式编码常常不是我们能够控制的……

中文问题不算大问题,但遇到的时候常常急死人,找资料都是长篇大论说上半天理论才说怎么怎么解决,甚至还说不清。本文旨在用尽可能简单的语言来指出解决方法。

常用的编码简介——摘自字符,字节和编码

分类 编码标准 说明
单字节字符编码 ISO-8859-1 最简单的编码规则,每一个字节直接作为一个 UNICODE 字符。比如,[0xD6, 0xD0] 这两个字节,通过 iso-8859-1 转化为字符串时,将直接得到 [0x00D6, 0x00D0] 两个 UNICODE 字符,即 “ÖД。

反之,将 UNICODE 字符串通过 iso-8859-1 转化为字节串时,只能正常转化 0~255 范围的字符。

ANSI 编码 GB2312,
BIG5,
Shift_JIS,
ISO-8859-2 ……
把 UNICODE 字符串通过 ANSI 编码转化为“字节串”时,根据各自编码的规定,一个 UNICODE 字符可能转化成一个字节或多个字节。

反之,将字节串转化成字符串时,也可能多个字节转化成一个字符。比如,[0xD6, 0xD0] 这两个字节,通过 GB2312 转化为字符串时,将得到 [0x4E2D] 一个字符,即 ‘中’ 字。

“ANSI 编码”的特点:
1. 这些“ANSI 编码标准”都只能处理各自语言范围之内的 UNICODE 字符。
2. “UNICODE 字符”与“转换出来的字节”之间的关系是人为规定的。

UNICODE 编码 UTF-8,
UTF-16, UnicodeBig ……
与“ANSI 编码”类似的,把字符串通过 UNICODE 编码转化成“字节串”时,一个 UNICODE 字符可能转化成一个字节或多个字节。

与“ANSI 编码”不同的是:
1. 这些“UNICODE 编码”能够处理所有的 UNICODE 字符。
2. “UNICODE 字符”与“转换出来的字节”之间是可以通过计算得到的。

参考资料

  1. 深入浅出java中文问题 (一、二、三、四篇)——[超长,有耐心的看]
  2. Python中文问题研究——[还是上两文章的作者写的,虽然说点出重点,例子也正中要害,但那一段说明绝对把人读得犹如腾云驾雾,该作者应该是写学校教材出身的,囧。]
  3. Java 编程技术中汉字问题的分析及解决——[IBM 开发中心的资料,条理清晰,可惜代码排版一般。推荐阅读]
  4. Java字符编码转换过程说明——[着重在HTML、XML、Servlet、Swing等等各种环境的字符编码问题。此文被转载得面目全非,这是相对完整(有图说明)和排版较好的]
  5. 字符,字节和编码——[条理相当清晰的文章,主要关于是C++和Java对字符编码的处理,推荐阅读。]
  6. Python的ASCII, GB2312, Unicode , UTF-8——[条理清晰,但不甚明确,特别缺少程序例子]
  7. How to Use UTF-8 with Python——[英文文章,但解释很详细,包括python的多种环境的应用]
  8. Unicode in Python —— [英文文章,最Dirty的资料,用最极端的例子来演示encode和decode,保证让你刻骨铭心]
文章分类 Java, Programming, Python 标签: , , ,

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*