您的位置:首页 > 编程语言 > Python开发

python学习笔记之字符编码

2017-06-18 16:17 465 查看

python学习笔记之字符编码

最近在看《python核心编程第三版》,wesley chun的书,在练习过程中发现第二章网络编程关于TCP和UDP的例子在Python 3.6.0 的环境下竟然无法正常运行,如是上网搜索相关资料终于讲问题弄明白了。

参考资料:

http://www.cnblogs.com/eastmount/p/5055924.html

https://www.zhihu.com/question/57492575/answer/153242286

http://www.freebuf.com/articles/others-articles/25623.html

http://blog.csdn.net/zxh2075/article/details/53085035

大体原因是python3.3之后python对字符处理做了比较大的改进,导致与以前的不兼容。

改进《python核心编程第三版》UDP的例子(约定网络传输都是用utf-8编码)

这是python3.6环境下可以正常运行的UDP服务端

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from socket import *
from time import ctime

MYIP = ''
MYPORT = 9527
BUFSIZ = 1024
ADDR = (MYIP,MYPORT)

udpSerSock = socket(AF_INET,SOCK_DGRAM)
udpSerSock.bind(ADDR)

while True:
print('waiting for message...')
data,addr = udpSerSock.recvfrom(BUFSIZ)
#接受到的是utf-8编码的字节流,我们将其解码成python本地化的unicode字符后再做操作
senddata = '[%s] %s' %(ctime(),data.decode("utf-8"))
#发送时将python本地化的unicode字符编码成utf-8字节流
udpSerSock.sendto(senddata.encode('utf-8'),addr)
print('...received from and returned to :',addr)

udpSerSock.close()


这是python3.6环境下可以正常运行的UDP客户端(支持中文输入输出)

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import sys
from socket import *

SERVICE_IP = '10.168.0.197'
SERVICE_PORT = 9527
BUFSIZ = 1024
ADDR = (SERVICE_IP,SERVICE_PORT)

udpCliSock = socket(AF_INET,SOCK_DGRAM)

while True:
#接受终端输入(一般为标准输入stdin),这里是python本地化的unicode字符
data = input(">")
if not data:
break
#发送时将python本地化的unicode字符编码成utf-8字节流
udpCliSock.sendto(data.encode('utf-8'),ADDR)
data,ADDR = udpCliSock.recvfrom(BUFSIZ)
if not data:
break
#终端不一定支持utf-8编码字符的显示,将上面接受到的uft-8字节流解码成python本地化的unicode字符再输出到终端
print(data.decode('utf-8'))
#也可以显示的将python本地化的unicode字符编码成终端支持的编码方式(远程终端最好也不要这么用)
#print data.decode('utf-8').encode(sys.stdout.encoding)
udpCliSock.close()


这是python2.7环境下可以正常运行的UDP客户端(支持中文输入输出)

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import sys
from socket import *

SERVICE_IP = '10.168.0.197'
SERVICE_PORT = 9527
BUFSIZ = 1024
ADDR = (SERVICE_IP,SERVICE_PORT)

udpCliSock = socket(AF_INET,SOCK_DGRAM)

while True:
#python 2.x与python3不同,这里终端输入的是编码之后的字节流,我们需要根据终端的编码方式,解码成python本地化的unicode字符
data = raw_input(">").decode(sys.stdin.encoding)
if not data:
break
#发送时将python本地化的unicode字符编码成utf-8字节流
udpCliSock.sendto(data.encode("utf-8"),ADDR)
data,ADDR = udpCliSock.recvfrom(BUFSIZ)
if not data:
break
#终端不一定支持utf-8编码字符的显示,将上面接受到的uft-8字节流解码成python本地化的unicode字符再输出到终端
print(data.decode('utf-8'))
#也可以显示的将python本地化的unicode字符编码成终端支持的编码方式(远程终端最好也不要这么用)
#print data.decode('utf-8').encode(sys.stdout.encoding)

udpCliSock.close()


python 本地化的unicode编码是何种形式的编码这需要查看python源代码才能知道,可能各个平台下面的实现并不一定一致,其实我们作为使用者不需要关心这些具体的实现细节。

关于字符集和字符编码

字符集就是定义了字符和数字对应关系的一个集合,仅仅标记着字符和数字之间的映射关系。例如:ASCII字符集定义’A’对映十进制数65,’a’对映十进制数97。

字符编码则是定义字符对映的数字在内存中如何存放(计算机只识别数字)。例如:ASCII字符对映的数字在0-255范围内,所以一个字节就能够存放。UNICODE字符集中,某个常用汉字对映的数字可能是45681,那一个字节的内存空间就放不下,某个及其罕见的汉字对映的数字可能是100000000(仅仅为了举例子),那么就需要4个字节内存空间才能存放的下了。

对于美国这样的英语国家来说,所有使用的字符加起来也就100多个,但是对于中国这样的汉字国家,可能有几十万个字符。由于计算机系统早期的设计限制,导致全世界各个国家都自己玩自己的一套字符集。关起门自己跟自己玩没有问题,但是互联网兴起之后如何跟别的国家玩就是问题了。大家之间的语言不互通,导致字符相互之间不能识别,所以Unicode应运而生。

Unicode是一种国际通用字符集。它为每种语言中的每个字符设定了统一并且唯一的数字(可以理解为是一部收录了全世界各种文字和符号的字典)。

那么问题又来了,Unicode字符集收录全世界各种语言的文字和符号,那么可能有几十万甚至上百万的字符量了。这些字符对映的数字编号如何在内存中存放呢?这就设计到不同的编码方案了!!

UTF(Unicode Transformation Formats)来定义这些编码方案:

方案1 UTF-32

这是最傻瓜化的编码方式,大家都知道4个字节可以表示的数字范围是0~40多亿,那么每个Unicode字符对映的数字都用4个字节来存放肯定是足够的。但是美国这样的英语国家肯定不干了,他们所有的字符所对映的数字用一个字节就能表示,现在却用4个字节。也就是原来1个G的文件现在可能需要4个G空间,白白浪费了3个G。

方案2 UTF-16

鉴于UTF-32太浪费空间,连我们汉字国家都觉得浪费(常用汉字其实两个字节也基本够了)。那么UTF-16就是一种改进方案,使用2个字节或者4个字节来存放Unicode字符对映数字。Unicode中的大部分绝大部分常用字符,使用2个字节存放就够了,这也是Unicode设计的精妙之处,它将常用字符对映的数字尽量安排在0~65535之间(Unicode BMP)。对于那些罕见的字符可能就是需要使用代理对(surrogate pair)以4个字节来存放。UTF-16对于英文字符会浪费一个字节的空间。

方案3 UTF-8

相比于UTF-16,UTF-8就采用更加灵活的按需存放方式,英文字符(ASCII字符)只需要1个字节,常用汉字需要两个字节,罕见字符需要3个或者4个字符。UTF-8完全根据字符对映的数字的大小来安排存储空间。

这样就杜绝了空间的浪费,对于网络传输时非常有益的。互联网传输一般都用UTF-8就是这个原因。

UTF-16和UTF-8是最流行的Unicode编码方案:

1.Windows平台下面Unicode的编码方案是UTF-16,所以很多VC程序员第一印象就是Unicode字符(宽字符)占2个字节

2.UTF-8是最利于存储和传输的编码方式

3.在Python3程序中,我们尽量将从文件和网络上拿到的字符解码成Pthon自身的Unicode字符(如:decode(‘uft-8’))来操作,而在存储和网络传输时根据具体需求和平台使用合适的编码方式(如:encode(‘utf-8’))。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息