免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
123下一页
最近访问板块 发新帖
查看: 68144 | 回复: 24
打印 上一主题 下一主题

小结Python的中文处理 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-04-15 18:14 |只看该作者 |倒序浏览
最近要处理中文数据,折腾了一天,总算有了些头绪
现把从网上搜集的资料整理一下

  1. Python的中文处理

  2. 一、使用中文字符
  3.     在python源码中如果使用了中文字符,运行时会有错误,解决的办法是在源码的开头部分加入字符编码的声明,下面是一个例子:
  4.       #!/usr/bin/env python
  5.       # -*- coding: cp936 -*-
  6.     Python Tutorial中指出,python的源文件可以编码ASCII以外的字符集,最好的做法是在#!行后面用一个特殊的注释行来定义字符集:
  7.         # -*- coding: encoding -*-
  8. 根据这个声明,Python会尝试将文件中的字符编码转为encoding编码,并且,它尽可能的将指定地编码直接写成Unicode文本。
  9.     注意,coding:encoding只是告诉Python文件使用了encoding格式的编码,但是编辑器可能会以自己的方式存储.py文件,因此最后文件保存的时候还需要编码中选指定的ecoding才行。

  10. 二、中文字符的存储
  11.         >>> str = u"中文"
  12.         >>> str
  13.         u'\xd6\xd0\xce\xc4'
  14.         >>> str = "中文"
  15.         >>> str
  16.         '\xd6\xd0\xce\xc4'
  17.     u"中文"只是声明unicode,实际的编码并没有变。这样子就发生变化了:
  18.         >>> str = "中文"
  19.         >>> str
  20.         '\xd6\xd0\xce\xc4'
  21.         >>> str = str.decode("gb2312")
  22.         >>> str
  23.         u'\u4e2d\u6587'
  24. 更进一步:
  25.         >>> s = '中文'
  26.         >>> s.decode('gb2312')
  27.         u'\u4e2d\u6587'
  28.         >>> len(s)
  29.         4
  30.         >>> len(s.decode('gb2312'))
  31.         2
  32.         >>> s = u'中文'
  33.         >>> len(s)
  34.         4
  35.         >>> s = '中文test'
  36.         >>> len(s)
  37.         8
  38.         >>> len(s.decode('gb2312'))
  39.         6
  40.         >>> s = '中文test,'
  41.         >>> len(s)
  42.         10
  43.         >>> len(s.decode('gb2312'))
  44.         7
  45.     可以看出,对于实际Non-ASCII编码存储的字符串,python可以正确的识别出其中的中文字符以及中文上下文中的标点符号。
  46.     前缀“u”表示“后面这个字符串“是一个Unicode字符串”,这仅仅是一个声明,并不表示这个字符串就真的是Unicode了;就好比某正太声称自己已满18岁,但实际上他的真实年龄并不确定,现在体育界年龄造假可不稀罕幺!
  47.     那么声明成u有什么作用呢?对于Python来说,只要你声明某字符串是Unicode,它就会用Unicode的一套机制对它进行处理。比方说,做字符串操作的时候会动用到内部的Unicode处理函数,保存的时候以Unicode字符(双字节)进行保存。等等。显而易见,对于一个实际上并不是Unicode的字符串,做Unicode动作的处理,是有可能会出问题的。u前缀只适用于你的字符串常量真的是Unicode的情况。

  48. 三、中文字符的IO操作
  49.     用python处理字符串很容易,但是在处理中文的时候需要注意一些问题。比如:
  50.         a = "我们是python爱好者"
  51.         print a[0]
  52. 只能输出“我”字的前半部分,要想输出整个的“我”字还需要:
  53.         b = a[0:2]
  54.         print b
  55. 才行,很不方便,并且当一段文本中同时有中英文如何处理?最好的办法就是转换为unicode。像这样:
  56.         c = unicode(a, "gb2312")
  57.         print c[0]
  58. 这个时候c的下标对应的就是每一个字符,不再是字节,并且通过len(c)就可以获得字符数!还可以很方便的转换为其他编码,比如转换为utf-8:
  59.        d = c.encode("utf-8")

  60. 四、<type ‘str’>和<type ‘unicode’>
  61. <type ‘str’>将字符串看作是字节的序列,而<type ‘unicode’>则将其看作是字符的序列,单个字符可能占用多个字节;字节相对于字符,其在存储层次中更低一些。
  62. str转换为unicode要decode,可以这样想,因为要把字节序列解释成字符序列,字节序列是底层的存放方式,解码(decode)成更高层的字符以便使用;同理,unicode转换为str要encode,就象信息编码(encode)后才存储一样:
  63.         s.decode(encoding)    <type 'str'> to <type 'unicode'>
  64.         u.encode(encoding)    <type 'unicode'> to <type 'str'>
  65.     例如:
  66.         >>> s = 'str'
  67.         >>> type(s)
  68.         <type 'str'>
  69.         >>> type(s.decode())
  70.         <type 'unicode'>
  71.         >>> s = u'str'
  72.         >>> type(s)
  73.         <type 'unicode'>
  74.         >>> type(s.encode())
  75.         <type 'str'>
  76. 处理中文数据时最好采用如下方式:
  77.    1. Decode early(尽早decode, 将文件中的内容转化成unicode再进行下一步处理)
  78.    2. Unicode everywhere (程序内部处理都用unicode)
  79.    3. Encode late (最后encode回所需的encoding, 例如把最终结果写进结果文件)
  80. 下面是一个简单的演示,用re库查询一个中文字符串并打印:
  81.     >>> p = re.compile(unicode("测试(.*)", "gb2312"))
  82.     >>> s = unicode("测试一二三", "gb2312")
  83.     >>> for i in p.findall(s):
  84.                   print i.encode("gb2312")
  85.     一二三

  86. 五、跨平台处理技巧
  87.     如果一个project必须在两个平台上开发,程序应该使用同样的encoding,比如要求所有的文件都使用UTF-8,如果实在不能统一(一般是为了满足许多所谓专家学者莫名其妙的要求),可以退而求其次,用当前系统编码决定文件内的编码:
  88.         import locale
  89.         import string
  90.         import re
  91.         #根据当前系统的encoding构造需要的编码取值
  92.         lang = string.upper(locale.setlocale(locale.LC_ALL, ""))
  93.         textencoding = None
  94.         #检查编码的值是不是满足我们需要的情况
  95.         if re.match("UTF-8", lang) != None:
  96.             # UTF-8编码
  97.             textencoding = "utf-8"
  98.         elif re.match(r"CHINESE|CP936", lang):
  99.             # Windows下的GB编码
  100.             textencoding = "gb18030"
  101.         elif re.match(r"GB2312|GBK|GB18030", lang):
  102.             # Linux下的GB编码
  103.             textencoding = "gb18030"
  104.         else:
  105.             # 其他情况,抛个错误吧
  106.             raise UnicodeError
  107.         fd = file(filename, "r")
  108.         fulltextlist = fd.readlines()
  109.         # 把每一行转换成unicode
  110.         for each in len(fulltextlist):
  111.             fulltextlist[i] = unicode(each, textencoding)
  112.         fd.close()
  113.         # 如果要打印的话,可以用text.encode(encoding)来恢复成多字节编码

  114. 小结
  115.     一个比较一般的Python中文处理的流程:
  116.     * 将欲处理的字符串用unicode函数以正确的编码转换为Unicode                     
  117.     * 在程序中统一用Unicode字符串进行操作                                          
  118.     * 输出时,使用encode方法,将Unicode再转换为所需的编码                        
  119. 有几点要说明一下:
  120.     * 所谓“正确的”编码,指得是指定编码和字符串本身的编码必须一致。这个其实并不那么容易判断,一般来说,我们直接输入的简体中文字符,有两种可能的编码:GB2312(GBK、GB18030)、以及UTF-8
  121.     * encode成本地编码的时候,必须要保证目标编码中存在欲转换字符的内码。encode这种操作一般是通过一个本地编码对应Unicode的编码转换表来进行的,事实上每个本地编码只能映射到Unicode的一部分。但是映射的区域是不同的,比如Big-5对应的Unicode的编码范围和 GBK对应的就不一样(实际上这两个编码有部分范围是重叠的)。所以,Unicode的一些字符(比如本身就是从GB2312转换来的那些),可以映射到 GBK,但未必可以映射到Big-5,如果你想转换到Big-5,很有可能就会出现编码找不到的异常。但UTF-8的码表范围实际上和Unicode是一样的(只是编码形式不同而已),所以,理论上来说,任何本地编码的字符,都可以被转换到UTF-8
  122.     * GB2312、GBK、GB18030本质上是同一种编码标准。只是在前者的基础上扩充了字符数量
  123.     * UTF-8和GB编码不兼容

  124. 参考资料
  125. 1、[url]http://bbs3.chinaunix.net/thread-1389703-1-2.html[/url]
  126. 2、Python的中文处理及其它
  127.    [url]http://www.go4pro.org/?p=38[/url]
  128. 3、Python处理中文的时候的一些小技巧
  129.    [url]http://cocre.com/?p=461[/url]
  130. 4、Unicode In Python, Completely Demystified. Kumar McMillan
  131.    [url]http://farmdev.com/talks/unicode[/url]
  132. 5、python中文处理好方法
  133.    [url]http://www.okpython.com/bbs/viewthread.php?tid=311[/url]
  134. 6、Python的中文处理
  135.    [url]http://hi.baidu.com/mrsz/blog/item/7812a5018c2cf2031d9583d2.html[/url]
复制代码

[ 本帖最后由 pFreeStyle 于 2009-4-16 09:24 编辑 ]

Python的中文处理.pdf

108.54 KB, 下载次数: 1092

PDF版

Python的中文处理.pdf

124.4 KB, 下载次数: 1108

添加了异常处理的介绍

论坛徽章:
0
2 [报告]
发表于 2009-04-15 21:13 |只看该作者

回复 #1 pFreeStyle 的帖子

保存,以备参考..

论坛徽章:
0
3 [报告]
发表于 2009-04-15 21:17 |只看该作者
不错,总结的很好,最近正在研究这个东西,^_^

论坛徽章:
0
4 [报告]
发表于 2009-04-16 09:19 |只看该作者

添加:六、异常处理


  1. 六、异常处理

  2.     编码encoding发生在Unicode字符串转换为字节序列时,而解码decoding发生在字节序列转换为Unicode字符串时(encoding always takes a Unicode string and returns a bytes sequence, and decoding always takes a bytes sequence and returns a Unicode string)。

  3. UnicodeDecodeError
  4. UnicodeDncodeError通常发生在将str字符串解码为特定Unicode字符串时。由于不同的编码只能映射部分str字符串到对应的Unicode字符,所以遇到一些字符时解码会失败。

  5. UnicodeEncodeError
  6.     UnicodeEncodeError通常发生在将Unicode字符串编码为特定字节序列时。由于不同的编码只能映射部分Unicode字符到对应的str字符串,所以遇到一些字符时编码会失败。

  7. 处理python编码转换时的UnicodeDecodeError异常

  8.     python提供的unicode转换不像iconv或是mbstowcs之类的方便。如果转换一段unicode("1234中文",'ascii') 到utf8,会直接出现UnicodeDecodeError的错误。如果在你能预知字串符的编码的时候,比如你用unicode('1234中文', 'gbk') 就不会出现错误;不过很多时候,会出现CJK混合的情况,如果要做到将一段CJK文件转换成unicode可能就行不通了。好在python的codecs提供了register_error这个功能:
  9.         register_error(name, error_handler)
  10. 原理很简单,不过要先看unicode是如何处理异常的。unicode这个函数是将一段string按输入的编码转换成目标的编码,如果出现了不与输入编码相符的,会出现一个UnicodeDecodeError的异常,通常有三种处理方法:strict、replace、ignore;默认是 strict,就是直接raise UnicodeDecodeError。通过register_error,我们也可以有自己的处理方法,如果遇到与输入的编码不符的时候,我们就自己识别,比如GBK、BIG5、JP的字符。
  11.     def cjk_replace(exc):
  12.         if not isinstance(exc, UnicodeDecodeError):
  13.             raise TypeError("don't know how to handle %r" % exc)

  14.         if exc.end + 1 > len(exc.object):
  15.             raise TypeError('unknown codec ,the object too short!')

  16.         ch1 = ord(exc.object[exc.start:exc.end])
  17.         newpos = exc.end + 1
  18.         ch2 = ord(exc.object[exc.start + 1:newpos])
  19.         sk = exc.object[exc.start:newpos]


  20.         if 0x81<=ch1<=0xFE and (0x40<=ch2<=0x7E or 0x7E<=ch2<=0xFE): # GBK
  21.             return (unicode(sk,'cp936'), newpos)

  22.         if 0x81<=ch1<=0xFE and (0x40<=ch2<=0x7E or 0xA1<=ch2<=0xFE): # BIG5
  23.             return (unicode(sk,'big5'), newpos)

  24.         raise TypeError('unknown codec !')

  25.     codecs.register_error("cjk_replace", cjk_replace)
  26. 我们的cjk_replace现在只能处理GBK与BIG5的,因为我对编码也不是特别了解,只是大概知道GBK与BIG5的,不太了解JP的。在 cjk_replace这个函数里,我们对不认识的文字进行手工识别,如果认识的编码,就用正确的方法,并返回编码后的内容与新的pos,比如“1234中文”,在pos为4的时候,会调用我们的cjk_replace,我们会返回一个从gbk转换成utf8的“中”字,并返回下个正确的位置“文”的起始位置。当然了,处理“文”的时候,还会再调用一次。下面看看是如何使用的:
  27.         filedata = open('test.txt','r).read()    #gbk and big5 file
  28.         data = unicode(filedata,'ascii','cjk_replace').encode('utf8')

  29. 7、 UnicodeDecodeError
  30.       [url]http://wiki.python.org/moin/UnicodeDecodeError[/url]
  31. 8、 UnicodeEncodeError
  32.       [url]http://wiki.python.org/moin/UnicodeEncodeError[/url]
  33. 9、 如何处理python编码转换时的UnicodeDecodeError异常
  34.       [url]http://blog.chinaunix.net/u/8873/showart_1009737.html[/url]
  35. 10、codecs — Codec registry and base classes
  36.        [url]http://docs.python.org/library/codecs.html[/url]

复制代码

论坛徽章:
1
天秤座
日期:2014-04-27 07:42:20
5 [报告]
发表于 2009-04-16 10:21 |只看该作者
这贴实在是太好了,由衷地表示我对楼主的崇敬之情…………(此处省略三百万字)

论坛徽章:
0
6 [报告]
发表于 2009-04-17 08:25 |只看该作者
感谢分享
中文的话只要你捣鼓成utf8格式就好办了

论坛徽章:
0
7 [报告]
发表于 2009-04-17 22:20 |只看该作者
你蛮叽歪的

就你厉害

支持一下

论坛徽章:
0
8 [报告]
发表于 2009-06-02 12:51 |只看该作者
很有用的,先保留!

论坛徽章:
0
9 [报告]
发表于 2009-06-03 18:14 |只看该作者
很用心的总结,谢谢楼主了

论坛徽章:
0
10 [报告]
发表于 2009-06-04 09:50 |只看该作者
lz的总结有点问题:
1 str=u"中文",type(str)应该是unicode,因此len(u"中文")应该等于2;不知道lz使用的是什么版本的python;我的是Linux 下的2.6.2。
2 关于编码之间的关系和编码与unicode的关系的讨论,用词似乎有些不妥
3 python中文处理,不一定非得转换到unicode下面;如果能保证在处理过程中,都使用同一中编码,那么使用字符串就可以了,不一定非得使用unicode string。
其他的总结的都很好!赞一个先
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP