生活周遭

In My Circle

Subscribe to RSS feed

Posts tagged with "python"

Unicode 与 UTF-8, GBK

,

原本是为了回复[python-chinese]邮件列表中的一个关于socket上传输unicode的问题,写得比较长,觉得应该记下来:

我想这里有几个概念要澄清~ 这样才能有比较好的交流的基础,而不是说同一个名字,却各自指的不是同一个东西。我也只是班门弄斧,希望大家指正。

unicode是一个字符集,不是一种编码,她只是定义了一个字符空间,把字符集中的字符映射到了相应的整数上,并为每一个字符提供了唯一的名字,比如,'中' U+4E2D。但unicode并没有定义编码方式(codec),也就是从字符集到字节序列的映射方式。有专门为unicode定义的编码方式,比如utf-8,utf-16(be/le),他们定义了unicode字符到字节序列的映射,也就是编码。utf-16简单的用16bit两个字节来编码(encode)unicode,utf-8用1倒3个或者更多的字节编码(encode)一个unicode字符。这些codec同时也定义了如何从一个字节序列中decode出unicode字符序列的方式。所以,unicode是一个抽象的字符概念,并不是一个字节流,她到字节流的映射靠codec来完成。python中,用unicode对象来表示抽象的unicode字符,我觉得到这个程度就可以了,不必关心其中是不是靠整数来表示的。关键是string对象就是字节流,python的unicode对象不同于string对象,抽象的unicode对象到字节流的string对象之间需要靠codec来转换。(在py3k中可能要统一unicode和string对象,再
议)
而gbk也是一个字符集,但同时他也定义了gbk这种codec(一种mbcs的codec),它定义了一个gbk字符(英文或者中文)到字节序列(一个或者两个字节)的映射。因为unicode的集合比gbk大,使gbk的超集,所以,可以把gbk编码的字节流转换成unicode字符序列,也可以把一部分的unicode字符(gbk子集的部分)编码为gbk的字节序列。所以,gbk也可以作为unicode的一种codec来用。

比如:(我现在在windows下面,所以默认的编码就是gbk的)
>>> sgbkzhong = '中' #输入默认gbk编码的string对象
>>> sgbkzhong #这是个以gbk编码的两个字节的字节流
'\xd6\xd0'
>>> len(sgbkzhong) #所以她的长度是2
2
>>> uzhong=unicode(sgbkzhong,'gbk') #可以将这个字符序列decode成unicode对象
>>> uzhong #这个unicode是U+4E2D
u'\u4e2d'
>>> len(uzhong) #这个unicode字符序列的长度是1
1
>>> sutf8zhong=uzhong.encode('utf-8')
>>> sutf8zhong
'\xe4\xb8\xad'
>>> len(sutf8zhong) #编码成utf-8的字节流,长度是3
3
>>> sutf16bezhong=uzhong.encode('utf-16be')
>>> sutf16bezhong
'N-'
>>> len(sutf16bezhong) #编码成utf-16be的字节流,长度是2
2
>>> sutf16bezhong.encode('hex') #我们看一下'N-'对应的hex
'4e2d'
>>>

以上说明的是python中unicode和string(字节流)之间的关系。而socket接口只能传输字节流,她并不关心你的内容是什么,自然也不能传什么抽象的unicode对象,她只能传输将unicode编码过的字节流。所以,python的socket接口只能处理string对象,如果不是string对象,会被首先转换成string对象。


具体到你这个问题,

On Thu, 23 Nov 2006 13:34:45 +0800, limodou
<limodou@gmail.com> wrote:

> On 11/23/06, 隋刚 <blackjacksg@gmail.com> wrote:
>> 实在是头大了,我又尝试了一下,因为是跟vc写的客户端(使用了unicode编码)进
>> 行通讯,分析了一下里面的数据,发现在每一个客户端发过来的字符前面加入了
>> '00',而不管我怎么转换,发过去的都是8位的ASCII字符,看看有什么好的解决办
>> 法没有?
> 那它可能使用的是utf-16-be编码吧。如:
> >>> a.encode('utf-16-be')
> '\x00a'

我想limodou已经替你解决了,我的理解和他一样,你的vc的客户端使用的应该是utf-16be的编码,不知道你使用这种方法后的结果怎么样了。

至于你之前的代码,为什么直接send unicode对象,对方却接收到的是ascii字符串,原因是send()函数会把你的unicode对象用str()转换成string对象,使用的codec是你指定的'utf-8',因为都是英文字符,所以,utf-8编码的结果是和ascii一致的。

On Mon, 20 Nov 2006 17:58:49 +0800, 隋刚
<blackjacksg@gmail.com> wrote:

> 请问使用python的socket module发送unicode的信息怎么发送,我试着转换了一
> 下,发送到服务器端还是ascii的,可以看看我的源程序,谢谢。
> import socket
> import sys
> reload(sys)
> sys.setdefaultencoding('utf-8')
> host = "192.168.3.111"
> port = 8888
> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
> s.connect((host, port));
> str = u'<?xml version=\"1.0\" ?><Request type=\"Distribute\"/>'
> s.send(str)
> date = s.recvfrom(4096)
> s.close()
> print repr(date)


顺便推荐一下这篇FAQ,对Unicode讲解得很通透,让我明白了很多原先很混乱的概念。
UTF-8 and Unicode FAQ for Unix/Linux
这儿有一个捎老一点的中文版

Pyrex, 和Python的C扩展

,

这原本是回复到[python-chinese]邮件列表的,但是没有回复进去,可能是因为功过Gmane news服务器的原因。先转到这儿:

倒是这两天我也在看这方面的问题~
一般编写Python C/C++ Module,或者为C/C++ lib编写Wrapper,我所看到的有以下几种方式: 1. 直接使用C Python提供的API 2. SWIG 3. Boost.Python 4. Pyrex

首先直接使用Python C API是很不方便的。上面几位高人已经谈过了SWIG和Boost.Python,我就不多说了,只是个人感觉易用性而言Boost.Python > SWIG,但SWIG功能强大,支持的也不只是Python一种脚本语言。以上这两种方式对C++的支持都比较友好。

这里着重推荐一下Pyrex。 Pyrex可以说是扩展Python的一大创新,她使用Python like的语法来编写Python的C Module,自动翻译成C语言代码,进而编译获取C代码的高效率。而且,配合Python的Distutils,使得构建过程简单到了只需要setup.py的程度。我觉得其简化python扩展的编写方式,已经和Boost.python,SWIG不在一个意义级别上了。

这里贴一段来自pyrex的示例,用于搜索质数:

# primes.pyx
#
#  Calculate prime numbers
#
 
def primes(int kmax):
        cdef int n, k, i
        cdef int p[1000]
        result = []
        if kmax > 1000:
                kmax = 1000
        k = 0
        n = 2
        while k < kmax:
                i = 0
                while i < k and n % p <> 0:
                        i = i + 1
                if i == k:
                        p[k] = n
                        k = k + 1
                        result.append(n)
                n = n + 1
        return result

将其pyrexc编译后得到一个c文件,然后编译之后就是一个python module了~ 按照他的逻辑,我还对照相应的写了一个Python脚本:

#!/usr/bin/env python
# primespy.py
 
def primes(kmax):
        result = []
        if kmax > 1000:
                kmax = 1000
        k = 0
        n = 2
        while k < kmax:
                i = 0
                while i< k and n % result != 0:
                        i += 1
                if i == k:
                        result.append(n)
                        k += 1
                n+=1
        return result
两者的代码行数基本一样,来看一下运行结果:
In [23]: tpyx = timeit.Timer(stmt='primes.primes(1000)', setup='import primes')
 
In [24]: tpy = timeit.Timer(stmt='primespy.primes(1000)', setup='import primespy')
 
In [25]: tpyx.timeit(100)
Out[25]: 1.2969999313354492
 
In [26]: tpy.timeit(100)
Out[26]: 30.266000032424927
速度提高了30倍之多!

pyrex的优势是编写简单,不必处理多余的细节,而且也不需要为这种简单付出效率的代价。劣势在于目前的pyrex对C++的只是仍然不是很好,还在继续开发之中。至于对C++到地支持差到什么程度,我还没有进一步测试过,还希望看到各位的高见。

总结是,如果是针对C的Python扩展,或者lib Wrapper,Pyrex可以说是最方便的选择。如果涉及到C++的扩展,可 能目前boost.python还是最好的选择了。

补:目前正在看Boost.python,SWIG其实并没有细看过,呵呵,只是看了一些intro性质文章,就开始大发厥词了,希望大家补充指正。