Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

python中文处理太坑爹了

108 views
Skip to first unread message

gss

unread,
Dec 23, 2011, 3:37:12 AM12/23/11
to
用python也有好几年了,但是中文处理总会遇到这样那样的问题。
今天又被搞了一次。
有点凌乱了。
一开头的coding=gbk
sys.setdefaultencoding
a.decode('utf8').encode('gbk')
print file.write 也有编码问题
今天搞json, loads dump 也有问题,
这些设定字符编码的地方,各有各得逻辑

这里行哪那里不行的,真是坑爹啊
--

[m [35m※ 来源:·水木社区 http://newsmth.net·[FROM: 61.135.152.*] [m

Secluded Life

unread,
Dec 23, 2011, 3:39:49 AM12/23/11
to
这是你自己没有搞清楚编码问题,Python的基本支持是完整的

【 在 Irregular (gss) 的大作中提到: 】
: 用python也有好几年了,但是中文处理总会遇到这样那样的问题。
: 今天又被搞了一次。
: 有点凌乱了。
: ...................

--

[m [1;36m※ 来源:·水木社区 newsmth.net·[FROM: 219.239.238.*] [m

C/Py

unread,
Dec 23, 2011, 4:27:51 AM12/23/11
to
coding: UTF-8
再把你编辑器的默认编码改成UTF8不就行了
【 在 Irregular (gss) 的大作中提到: 】
: 用python也有好几年了,但是中文处理总会遇到这样那样的问题。
: 今天又被搞了一次。
: 有点凌乱了。
: ...................

--
五月天山雪 无花只有寒
笛中闻折柳 春色未曾看
晓战随金鼓 宵眠抱玉鞍
愿将腰下剑 直为斩楼兰


[m [1;37m※ 来源:·水木社区 newsmth.net·[FROM: 182.146.25.*] [m

老鱼

unread,
Dec 23, 2011, 4:56:58 AM12/23/11
to
你没有搞清楚各种编码声明的含义。

【 在 Irregular (gss) 的大作中提到: 】
: 用python也有好几年了,但是中文处理总会遇到这样那样的问题。
: 今天又被搞了一次。
: 有点凌乱了。
: ...................

--
灭绝人性啊


[m [1;33m※ 来源:·水木社区 newsmth.net·[FROM: 125.78.148.*] [m

那啥那啥都被抢注了

unread,
Dec 23, 2011, 6:51:21 AM12/23/11
to
源码文件用coding: utf-8指定编码并把文件保存为utf-8格式
为了作为__main__模块执行时不出不必要的错误,统一使用\n换行
如果需要在源码内嵌入汉字等非ascii字符,建议使用u前缀作为unicode字符串,这样在print时可以可以不做编码转换,否则如果控制台编码与源文件编码不一致就必须显式转换,通过locale.getdefaultlocale()可以获取系统语言和编码
涉及文件路径的最好转换为sys.getfilesystemencoding()编码
unicode字符串在写入文件时必须转换为某种字节编码
json默认支持utf-8编码
尽量用utf-8和unicode,除了前面说的文件路径和写入文件等少数情况
【 在 Irregular (gss) 的大作中提到: 】
: 用python也有好几年了,但是中文处理总会遇到这样那样的问题。
: 今天又被搞了一次。
: 有点凌乱了。
: ...................

--

[m [31m※ 来源:·水木社区 http://newsmth.net·[FROM: 14.153.126.*] [m

cndenis

unread,
Dec 23, 2011, 7:06:21 AM12/23/11
to
这个是针对py2吗,还是py3也一样?
【 在 pyer 的大作中提到: 】
: 源码文件用coding: utf-8指定编码并把文件保存为utf-8格式
: 为了作为__main__模块执行时不出不必要的错误,统一使用\n换行
:如果需要在源码内嵌入汉字等非ascii字符,建议使用u前缀作为unicode字符串,这样在print时可以可以不做
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
编码转换,否则如果控制台编码与源文件编码不一致就必须显式转换,通过locale.getdefaultlocale()可以获取系统语言和编码
: ...................
--

[m [31m※ 来源:·水木社区 http://newsmth.net·[FROM: 121.33.190.*] [m

wincss

unread,
Dec 23, 2011, 7:19:18 AM12/23/11
to
py3就没有u前缀这一说

【 在 cndenis (cndenis) 的大作中提到: 】
: 这个是针对py2吗,还是py3也一样?
: :如果需要在源码内嵌入汉字等非ascii字符,建议使用u前缀作为unicode字符串,这样在print时可以可以不做
: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
: ...................

--

╋╋╋╋┻┻╋┻╋╋┻┻╋┻┻╋┻┻╋
┫┃┃┣┓┏┫ ┃┃┏━┫ ━┫ ━╋
┫ ┣┛┗┫┃ ┃┗━╋━ ┣━ ┣
╋┳┳╋┳┳╋╋┳╋┳┳╋┳┳╋┳┳╋


[m [1;37m※ 来源:·水木社区 newsmth.net·[FROM: 119.253.36.*] [m

cndenis

unread,
Dec 23, 2011, 7:20:36 AM12/23/11
to
嗯,无怪乎我对这个没印像……
【 在 wincss 的大作中提到: 】
: py3就没有u前缀这一说
: 【 在 cndenis (cndenis) 的大作中提到: 】
: : 这个是针对py2吗,还是py3也一样?

那啥那啥都被抢注了

unread,
Dec 23, 2011, 7:22:07 AM12/23/11
to
py3相当于自动对所有字符串加u前缀
【 在 cndenis (cndenis) 的大作中提到: 】
: 这个是针对py2吗,还是py3也一样?
: :如果需要在源码内嵌入汉字等非ascii字符,建议使用u前缀作为unicode字符串,这样在print时可以可以不做
: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
: ...................

--

[m [31m※ 来源:·水木社区 http://newsmth.net·[FROM: 14.153.126.*] [m

Secluded Life

unread,
Dec 23, 2011, 9:01:01 AM12/23/11
to
我来讲一下字符问题我的理解吧,虽然我对Python的编码处理的具体细节还不太清楚,不过临时稍微看了一下,和Perl的原理也差不多

最重要的是必须区分“字符”和“字节”的不同,“字符”是抽象的,而“字节”是具体的

比如一个“中”字,在不同编码中用如下字节表示:

GBK Big5 UTF-8 UTF-16LE
\xD6\xD0 \xA4\xA4 \xE4\xB8\xAD \x2D\x4E

所谓“抽象”的“字符”的“中”,并不是指“\xD6\xD0”或“\xA4\xA4”或任何字节,应该把它理解成:GBK编码中“\xD6\xD0”字节所指代的那个字符(语言学中的能指→所指),或者UTF-8编码中“\xE4\xB8\xAD”所指代的那个字符,但并不是这些具体字节本身

问题是,抽象的字符要作为数据进行存储和传递,就必须有具体的形式,也就是说你在程序内部实现中,要存储“中”这个字符,你必须采用某些特定的字节。你可以用“\xD6\xD0”,也可以用“\xE4\xB8\xAD”,也可以用“\x2D\x4E”,Python在Windows下采用的是UTF-16LE(?),也就意味着它的“字符”的载体编码是UTF-16LE

sys.setdefaultencoding(name)
Set the current default string encoding used by the Unicode implementation.

文档上是这么写的,如果我的理解没错的话,这个函数的作用就是改变“字符”的载体编码,sys.setdefaultencoding('gbk')以后,“中”这个字符在程序内部就不是用“\x2D\x4E”来承载,而是用“\xD6\xD0”来承载了

Python2.x里的str和unicode有什么区别呢?从字面意义上看容易混淆,实际上,你可以把它理解成str是“字节串”,unicode是“字符串”(string总是翻译成“字符串”,在这里就很容易把人绕晕),看下面的例子:

# -*- coding: gb2312 -*-

s = "张三李四"
print len(s) #=> 8
u = s.decode('gbk')
print len(u) #=> 4

我的脚本编码用的是GBK,而不是UTF-8,你会看到len(s)是8,这是这四个汉字所用的实际8个“字节”,而len(u)是4,这就表示这里有4个“字符”

encode和decode是什么意思呢?所谓编码,就是把意义转换成符号;而解码,就是把符号还原成意义。在这里,encode应该理解成把抽象的字符转换成具体的字节,而decode是把具体的字节还原成抽象的字符

现在的问题是:str类和unicode类都同时具有encode和decode方法,这是一个让我很不以为然的设定。如果按照字节与字符的区分,encode方法是应该只归unicode类所有,decode方法是只归str类所有的,因为“意义”只能转换成“符号”,“意义”再还原成“意义”这本身就没有意义。

假如我们这样:

# -*- coding: gb2312 -*-

s = "张三李四"
u = s.decode('gbk') # 没问题,字节解码为字符,符号还原为意义
s2 = s.encode('gbk')
# 出错了!字节没法再编码成字节,除非s全部是ASCII字符,但是这样s2和s是完全等同的,这个操作有什么意义?
u2 = u.decode('gbk')
# 又出错了!也只能u只包含ASCII字符,u2和u也是完全等同,这个操作也没有意义

在这里提一下Perl的处理方式,我不知道Python处理编码的原理是否是直接得自Python,还是说这是各门语言共同的做法(但是Ruby又不是这样做的),总之Python2.x是有缺陷的

Perl里只有一种string,它实际也区分字符串和字节串(以UTF-8作为底层的承载编码),但不像Python2.x分str和unicode,而是string内部有一个utf8的flag,这个flag是on的时候,这个string就是一个“字符”串,这个flag是off的时候就是一个“字节”串,它的编码、解码函数如下:

$octets = encode(ENCODING, $string [, CHECK])

$string = decode(ENCODING, $octets [, CHECK])

$octets就是字节串,$string就是字符串,也就是说,encode只对$string起作用,而decode只对$octets起作用,不像Python是str和unicode两类两个方法都有,但是其实各有一个是没用的。Larry Wall是语言学家,他设计的这一套字符、字节关系是完全符合语言学中的“能指-所指”理论的,而GvR恐怕就对语言学不在行了,Python的处理就不怎么精妙了。

再来说一下file.write为什么有编码问题:

# -*- coding: gb2312 -*-


s = "张三李四"
u = s.decode('gbk')

f = open('text.txt','w')
f.write(u) # 出错!
f.write(u.encode('gbk')) # 这样才行

出错的原因很简单,你想输出的是“字符”,而不是“字节”。上面说过,“字符”是抽象的,你是没有办法把一个抽象的东西写到文件里去的。虽然抽象的字符下面肯定是有具体的承载字节的,但是Python似乎并不愿意把unicode底层的字节跟IO搅在一起,这就导致f.write(a_unicode)的失败,当然a_unicode假如只包含ASCII字符,这个可以成功,然而这是一种捷径,是一条让人越来越糊涂的捷径

然后再是u标记的意义是什么?很简单,就是自动完成字节→字符的转换

# -*- coding: gb2312 -*-

s_or_u1 = "张三李四"
print type(s_or_u1) #=> <type 'str'>

s_or_u2 = u"张三李四"
print type(s_or_u2) #=> <type 'unicode'>

u"张三李四"就相当于"张三李四".decode(a_enc),这里的a_enc就是#coding行设定的gb2312

不得不说,(不知是不是从Perl得来的)这套字符处理方式很晦涩,字符、字节区分的概念实在不太容易理解,而Python本身的细节处理也没有做好,Perl做得很干净了,都不容易理解,Python没做干净更不行了。另外再附赠简单介绍Ruby的字符处理方式,跟Perl完全不同:

Ruby中没有字符、字节的区分,一切字符串都是“带有一个编码属性的字节串”。因为没有抽象的字符,所以就没有字节→字符的转换,也就根本没有、也不需要decode方法,Ruby的String类只有encode方法。因为没有抽象的“字符”概念,Ruby的编码问题应该比Perl、Python容易理解。没有“字符”的还有一个好处是:处理多字节文本无需经过中间转换。你要在Perl里处理中文字符,来源文件是GBK编码的,实际都得先转换成UTF-8,Perl才能处理:Python要先转化成UTF-16才能处理。对于海量文本来说,这一转换过程肯定是要耗费一定的资源的。而Ruby不需要这种转换,直接就能处理GBK或其他编码了。可能这样做也是考虑了日文的实际,日文的shift-jis(?)是本土编码,根本都不跟ASCII兼容,不像GBK是跟ASCII兼容的,这样做就不必转换就能处理土著编码的文档了。如果说Perl的字符-字节区分是语言学家的学院派做法的话,Ruby就是契合了多字节字符处理需要的实用派做法。



【 在 Irregular (gss) 的大作中提到: 】
: 用python也有好几年了,但是中文处理总会遇到这样那样的问题。
: 今天又被搞了一次。
: 有点凌乱了。
: ...................

--

那啥那啥都被抢注了

unread,
Dec 23, 2011, 9:55:32 AM12/23/11
to
一个文本文件的内容是\xe7\xbc\x96\xe7\xa0\x81
Ruby不用考虑编码的话认为这是两个字还是3个字,甚至是6个字?
【 在 FenRagwort (Secluded Life) 的大作中提到: 】
: 我来讲一下字符问题我的理解吧,虽然我对Python的编码处理的具体细节还不太清楚,不过临时稍微看了一下,和Perl的原理也差不多
: 最重要的是必须区分“字符”和“字节”的不同,“字符”是抽象的,而“字节”是具体的
: 比如一个“中”字,在不同编码中用如下字节表示:
: ...................

--

[m [32m※ 来源:·水木社区 http://newsmth.net·[FROM: 14.153.124.*] [m

Secluded Life

unread,
Dec 23, 2011, 10:10:09 AM12/23/11
to
我说了是“带有一个编码属性的字节串”
字符串作为一个对象,具有encoding属性,可以通过a_str.encoding得到,这个编码是什么,ruby就以该种编码去理解这串字节。
IO的对象都是字节,perl和python都必须进行转码(除非文件正好就是字符的底层载体编码),而ruby可以自由选择,可以不转,将字节原样处理,也可以转换成任意一种有效编码

【 在 pyer 的大作中提到: 】
: 一个文本文件的内容是\xe7\xbc\x96\xe7\xa0\x81
: Ruby不用考虑编码的话认为这是两个字还是3个字,甚至是6个字?
: 【 在 FenRagwort (Secluded Life) 的大作中提到: 】
: ...................
--

[m [34m※ 来源:·水木社区 http://newsmth.net·[FROM: 221.130.33.*] [m

C/Py

unread,
Dec 23, 2011, 10:15:00 AM12/23/11
to
我的理解:

encode/decode 带有一个from->to 的意思

encode: from: 符号 to: 字节序列
decode: from: 字节序列 to: 符号

[32mdecode [m 就是把一个字节序列按照指定的编码来解析,得到它的符号
[32mencode [m 就是把一个符号按照特定的编码方式得到表示这个符号的字节序列

在python里面, [32m符号 [m用它在unicode字符集中的序列号来表示。

假设当前系统的默认编码为utf-8
[33m
In [1]: s = "一" ; s ; s.decode('utf-8')
Out[1]: '\xe4\xb8\x80'
Out[1]: u'\u4e00' [m

其中的 \u4e00 就是汉字“一”在unicode中的序列号




【 在 FenRagwort (Secluded Life) 的大作中提到: 】
: 我来讲一下字符问题我的理解吧,虽然我对Python的编码处理的具体细节还不太清楚,不过临时稍微看了一下,和Perl的原理也差不多
: 最重要的是必须区分“字符”和“字节”的不同,“字符”是抽象的,而“字节”是具体的
: 比如一个“中”字,在不同编码中用如下字节表示:
: ...................

--
Darkness guides me...


[36m※ 修改:·lins05 于 Dec 23 23:14:59 2011 修改本文·[FROM: 125.66.75.*] [m
[m [1;32m※ 来源:·水木社区 newsmth.net·[FROM: 125.66.75.*] [m
0 new messages