7.11. codecs — 字符编码和解码| 文件系统 - LearnKu
文章推薦指數: 80 %
Python Unicode HOWTO 是特别有帮助的。
编码#. 理解编码的最好方式是去查看用不同方式对相同字符串编码产生的字节序列 ...
微信登录
提交改进
7.11.codecs—字符编码和解码
Python3标准库实例教程
/
目的:文本不同表现形式之间转换的编解码器。
codecs模块提供了流和文件接口用于转换数据。
它通常用于处理Unicode文本,但是用于其他的目的的编码格式也是可以处理的。
Unicode入门
CPython3.x版本区分文本和字节。
bytes实例使用一系列8位字节值。
相反,str字符串内部被作为一系列Unicode码点管理。
每个码点被存储为2-4字节的序列,这个取决于Python编译时给出的选项。
当输出str值时,它使用几种标准方案之一进行编码,以便稍后字节序列可以被重建为相同的文本字符串。
编码值的字节不一定与码点相同,并且编码定义了两组值之间转换的方式。
读取Unicode数据还需要知道编码,以便传入的字节可以转换为Unicode类使用的内部表示。
西方语言最常见的编码是UTF-8和UTF-16,它们分别使用一个或者两个字节序列表示每个代码点。
对于大多数字符用于两个字节的码点存储可能不太适合,所以其他的编码可能更高效。
推荐阅读
有关Unicode的更多介绍性信息,请参阅本节末尾的参考列表。
PythonUnicodeHOWTO是特别有帮助的。
编码
理解编码的最好方式是去查看用不同方式对相同字符串编码产生的字节序列。
下面的例子用这个函数格式化字节字符串以更好地阅读。
codecs_to_hex.py
importbinascii
defto_hex(t,nbytes):
"""Formattexttasasequenceofnbytelongvalues
separatedbyspaces.
"""
chars_per_item=nbytes*2
hex_version=binascii.hexlify(t)
returnb''.join(
hex_version[start:start+chars_per_item]
forstartinrange(0,len(hex_version),chars_per_item)
)
if__name__=='__main__':
print(to_hex(b'abcdef',1))
print(to_hex(b'abcdef',2))
这个函数使用binascii得到字符串的16进制表示,然后在返回之前每隔nbytes插入一个空格。
$python3codecs_to_hex.py
b'616263646566'
b'616263646566'
第一个编码示例首先使用unicode类的原始表示法打印文français,然后从Unicode数据库中输出每个字符的名称。
接下来两行分别将字符串编码为UTF-8和UTF-16,并将编码结果显示为16进制。
codecs_encodings.py
importunicodedata
fromcodecs_to_heximportto_hex
text='français'
print('Raw:{!r}'.format(text))
forcintext:
print('{!r}:{}'.format(c,unicodedata.name(c,c)))
print('UTF-8:{!r}'.format(to_hex(text.encode('utf-8'),1)))
print('UTF-16:{!r}'.format(to_hex(text.encode('utf-16'),2)))
str的编码结果是一个bytes对象。
$python3codecs_encodings.py
Raw:'français'
'f':LATINSMALLLETTERF
'r':LATINSMALLLETTERR
'a':LATINSMALLLETTERA
'n':LATINSMALLLETTERN
'ç':LATINSMALLLETTERCWITHCEDILLA
'a':LATINSMALLLETTERA
'i':LATINSMALLLETTERI
's':LATINSMALLLETTERS
UTF-8:b'6672616ec3a7616973'
UTF-16:b'fffe6600720061006e00e700610069007300'
给一个编码字节序列作为bytes实例,decode()方法将会把它们翻译为Unicode码点并返回一个str实例。
codecs_decode.py
fromcodecs_to_heximportto_hex
text='français'
encoded=text.encode('utf-8')
decoded=encoded.decode('utf-8')
print('Original:',repr(text))
print('Encoded:',to_hex(encoded,1),type(encoded))
print('Decoded:',repr(decoded),type(decoded))
编码方式不会改变输出类型。
$python3codecs_decode.py
Original:'français'
Encoded:b'6672616ec3a7616973'
有关默认编码的讨论请参考sys关于UnicodeDefaults的部分。
处理文件
当处理I/O操作的时候,编解码字符串是特别重要的。
当写入文件,socket或者其它流的时候,数据必须被进行恰当的编码。
一般而言,所有的文本数据当它被读的时候需要从字节形式解码,写入的时候必须从内部值转换为特定的表示。
程序可以显示地编码和解码数据,但根据所使用的编码,确定是否读取了足够的字节以完全解码数据可能并不容易。
好在codecs模块提供了管理数据编解码的类,因此程序中一般不需要那么做。
codecs提供的简单接口可以用来替换内建的open()方法。
新版本工作方式类似内建方法,但是添加了两个参数来指定编码以及错误处理技术。
codecs_open_write.py
fromcodecs_to_heximportto_hex
importcodecs
importsys
encoding=sys.argv[1]
filename=encoding+'.txt'
print('Writingto',filename)
withcodecs.open(filename,mode='w',encoding=encoding)asf:
f.write('français')
#定义用于to_hex()的字节分组
nbytes={
'utf-8':1,
'utf-16':2,
'utf-32':4,
}.get(encoding,1)
#显示文件原生字节
print('Filecontents:')
withopen(filename,mode='rb')asf:
print(to_hex(f.read(),nbytes))
这个例子以带有「ç」的unicode字符串开头,并使用在命令行上指定的编码将文本保存到文件。
$python3codecs_open_write.pyutf-8
Writingtoutf-8.txt
Filecontents:
b'6672616ec3a7616973'
$python3codecs_open_write.pyutf-16
Writingtoutf-16.txt
Filecontents:
b'fffe6600720061006e00e700610069007300'
$python3codecs_open_write.pyutf-32
Writingtoutf-32.txt
Filecontents:
b'fffe00006600000072000000610000006e000000e700000061000000
6900000073000000'
使用open()读取数据很简单,只需要一个编码:编码必须实现直到,才能正确设置解码器。
某些数据格式将编码,例如:XML,指定为文件的一部分,但通常有应用程序管理。
codecs简单地将编码作为参数并假设它是正确的。
codecs_open_read.py
importcodecs
importsys
encoding=sys.argv[1]
filename=encoding+'.txt'
print('Readingfrom',filename)
withcodecs.open(filename,mode='r',encoding=encoding)asf:
print(repr(f.read()))
这个例子读取先前程序创建的文件,并将生成的unicode对象的表示形式打印到控制台。
$python3codecs_open_read.pyutf-8
Readingfromutf-8.txt
'français'
$python3codecs_open_read.pyutf-16
Readingfromutf-16.txt
'français'
$python3codecs_open_read.pyutf-32
Readingfromutf-32.txt
'français'
字节顺序
在不同计算机系统之间传输数据的时候,UTF-16和UTF-32等多字节编码可能会造成问题,可以通过直接复制或者网络通信复制文件。
不同系统使用不同的高低字节排序。
数据的这种特性(称为其字节序)取决于诸如操作系统和应用程序开发人员所做的硬件体系结构和选择等因素。
事先并不总是知道给定数据使用什么字节顺序,因此多字节编码包括字节顺序标记(BOM)作为编码的输出的前几个字节。
例如,UTF-16定义0xFFFE和0xFEFF不是有效字符,可以被用于去标识字节顺序。
codecs定义了表示UTF-16和UTF-32字节顺序的常量。
codecs_bom.py
importcodecs
fromcodecs_to_heximportto_hex
BOM_TYPES=[
'BOM','BOM_BE','BOM_LE',
'BOM_UTF8',
'BOM_UTF16','BOM_UTF16_BE','BOM_UTF16_LE',
'BOM_UTF32','BOM_UTF32_BE','BOM_UTF32_LE',
]
fornameinBOM_TYPES:
print('{:12}:{}'.format(
name,to_hex(getattr(codecs,name),2)))
根据当前系统原生字节顺序,BOM,BOM_UTF16和 BOM_UTF32自动设置为大端值或者小端值。
$python3codecs_bom.py
BOM:b'fffe'
BOM_BE:b'feff'
BOM_LE:b'fffe'
BOM_UTF8:b'efbbbf'
BOM_UTF16:b'fffe'
BOM_UTF16_BE:b'feff'
BOM_UTF16_LE:b'fffe'
BOM_UTF32:b'fffe0000'
BOM_UTF32_BE:b'0000feff'
BOM_UTF32_LE:b'fffe0000'
字节顺序可以被codecs中的解码器自动地检测和处理,但是当解码的时候可以显示声明一个字节顺序。
codecs_bom_create_file.py
importcodecs
fromcodecs_to_heximportto_hex
#选择UTF-16编码的非本地编码
ifcodecs.BOM_UTF16==codecs.BOM_UTF16_BE:
bom=codecs.BOM_UTF16_LE
encoding='utf_16_le'
else:
bom=codecs.BOM_UTF16_BE
encoding='utf_16_be'
print('Nativeorder:',to_hex(codecs.BOM_UTF16,2))
print('Selectedorder:',to_hex(bom,2))
#编码数据
encoded_text='français'.encode(encoding)
print('{:14}:{}'.format(encoding,to_hex(encoded_text,2)))
withopen('nonnative-encoded.txt',mode='wb')asf:
#写入字节顺序标记,它没有包含在编码文本中
#,因为选择编码的时候字节顺序被显示给定了。
f.write(bom)
#写入编码文本的字节字符串
f.write(encoded_text)
codecs_bom_create_file.py 检测出本地系统字节顺序,然后使用替代形式,以便下一个示例可以自动地检测出来。
$python3codecs_bom_create_file.py
Nativeorder:b'fffe'
Selectedorder:b'feff'
utf_16_be:b'006600720061006e00e7006100690073'
codecs_bom_detection.py 打开文件的时候没有声明编码顺序,所以解码器使用文件前两个字节的BOM值决定它。
codecs_bom_detection.py
importcodecs
fromcodecs_to_heximportto_hex
#查看原生数据
withopen('nonnative-encoded.txt',mode='rb')asf:
raw_bytes=f.read()
print('Raw:',to_hex(raw_bytes,2))
#重新打开文件,并且让codecs检测BOM
withcodecs.open('nonnative-encoded.txt',
mode='r',
encoding='utf-16',
)asf:
decoded_text=f.read()
print('Decoded:',repr(decoded_text))
因为文件的前两个字节用于字节顺序检测,所以他们没有包含在read()方法的返回值中。
$python3codecs_bom_detection.py
Raw:b'feff006600720061006e00e7006100690073'
Decoded:'français'
错误处理
前面的部分指出读取Unicode文件的时候需要知道编码。
正确设置编码非常重要,原因有俩。
如果在读取文件的时候编码设置的不正确,数据则将被解释为错误,或者解码失败。
并非所有的Unicode字符都可以被所有编码表示,所以如果写入数据的时候使用了错误的编码将会引发错误,数据也可能丢失。
codecs使用了5个错误处理选项,同str的encode()方法和bytes的decode()方法相同,列出于下列表格中。
Codec错误处理模式
错误模式
描述
strict
数据没有被正确转换将会引发错误。
replace
对于不能编码的数据替换一个特殊的标记字符。
ignore
跳过数据。
xmlcharrefreplace
XML字符(仅用于编码)
backslashreplace
转移序列(仅用于编码)
编码错误
在将Unicode数据写入ASCII输出流(例如常规文件或者没有健壮的编码集的sys.stdout)时,最常见的错误条件是接收UnicodeEncodeError。
这个例子程序可以被用于去试验不同的文件处理模式。
codecs_encode_error.py
importcodecs
importsys
error_handling=sys.argv[1]
text='français'
try:
#保存数据,编码为ASCII,使用命令行中声明的错误处理模式
withcodecs.open('encode_error.txt','w',
encoding='ascii',
errors=error_handling)asf:
f.write(text)
exceptUnicodeEncodeErroraserr:
print('ERROR:',err)
else:
#如果写入文件的时候没有错误,显示文件内容
withopen('encode_error.txt','rb')asf:
print('Filecontents:{!r}'.format(f.read()))
然而严格模式是最安全的对于确保一个应用程序对于所有的I/O操作显式地设置正确的编码,当错误出现的时候可能导致程序崩溃。
$python3codecs_encode_error.pystrict
ERROR:'ascii'codeccan'tencodecharacter'\xe7'inposition
4:ordinalnotinrange(128)
其他的错误处理模式可能比较松散。
例如,replace确保没有错误引发,代价是可能丢失无法转换为请求编码的数据。
Unicode字符π仍然不能转换为ASCII,但是不会引发异常,而是在输出中将它替换为?。
$python3codecs_encode_error.pyreplace
Filecontents:b'fran?ais'
为了去跳过整个有问题的数据,使用ignore。
任何不能被编码的字符都将被丢弃。
$python3codecs_encode_error.pyignore
Filecontents:b'franais'
有两个无损的错误处理选项,它们都用一个独立于编码标准定义的替代表示替换字符。
xmlcharrefreplace引用XML字符作为替代(字符引用的列表在W3C文档「XML字符实体定义」中指定)。
$python3codecs_encode_error.pyxmlcharrefreplace
Filecontents:b'français'
另一个无损错误处理选项是backslashreplace,它产生一个输出格式就像打印unicode对象的方法repr()返回的那样。
Unicode字符被替换为以\u开始,接下来是码点的16进制值。
$python3codecs_encode_error.pybackslashreplace
Filecontents:b'fran\\xe7ais'
解码错误
很可能在解码数据的时候看到错误,特别是当使用了错误的编码的时候。
codecs_decode_error.py
importcodecs
importsys
fromcodecs_to_heximportto_hex
error_handling=sys.argv[1]
text='français'
print('Original:',repr(text))
#使用一个编码保存数据
withcodecs.open('decode_error.txt','w',
encoding='utf-16')asf:
f.write(text)
#转化文件中的字节
withopen('decode_error.txt','rb')asf:
print('Filecontents:',to_hex(f.read(),1))
#尝试以错误的编码读取数据
withcodecs.open('decode_error.txt','r',
encoding='utf-8',
errors=error_handling)asf:
try:
data=f.read()
exceptUnicodeDecodeErroraserr:
print('ERROR:',err)
else:
print('Read:',repr(data))
当指定解码格式的时候,strict错误处理模式在字节流不能被正确解码的时候将会引发错误。
这个例子中,UnicodeDecodeError错误是由于尝试使用UTF-8解码器转化部分UTF-16BOM字符。
$python3codecs_decode_error.pystrict
Original:'français'
Filecontents:b'fffe6600720061006e00e70061006900
7300'
ERROR:'utf-8'codeccan'tdecodebyte0xffinposition0:
invalidstartbyte
切换为ignore将会导致解码器跳过无效的字节。
但是结果仍然不尽人意,因为解码输出内嵌了空字节。
$python3codecs_decode_error.pyignore
Original:'français'
Filecontents:b'fffe6600720061006e00e70061006900
7300'
Read:'f\x00r\x00a\x00n\x00\x00a\x00i\x00s\x00'
replace模式中,无效的字节将被替换为\uFFFD,官方的Unicode替换字符,看起来像一个黑色背景包含白色问好的钻石?。
$python3codecs_decode_error.pyreplace
Original:'français'
Filecontents:b'fffe6600720061006e00e70061006900
7300'
Read:'��f\x00r\x00a\x00n\x00�\x00a\x00i\x00s\x00'
编码翻译
虽然大多数程序可以再内部使用str数据,但是将编解码作为I/O操作的一部分,有时候更改文件的编码而不保留中间数据格式是非常有用的。
EncodedFile() 将使用了某个编码的文件句柄包装为一个类,这个类在I/O发生时将数据转换为另外一种编码。
codecs_encodedfile.py
fromcodecs_to_heximportto_hex
importcodecs
importio
#原始数据
data='français'
#手动编码为UTF-8
utf8=data.encode('utf-8')
print('StartasUTF-8:',to_hex(utf8,1))
#设置输出缓冲池,并将它包装为EncodedFile
output=io.BytesIO()
encoded_file=codecs.EncodedFile(output,data_encoding='utf-8',
file_encoding='utf-16')
encoded_file.write(utf8)
#获取缓冲内容,并编码为UTF-16
utf16=output.getvalue()
print('EncodedtoUTF-16:',to_hex(utf16,2))
#使用UTF-16数据设置另一个缓冲池
#并且包装为另一个EncodedFile
buffer=io.BytesIO(utf16)
encoded_file=codecs.EncodedFile(buffer,data_encoding='utf-8',
file_encoding='utf-16')
#读取数据的UTF-8版本
recoded=encoded_file.read()
print('BacktoUTF-8:',to_hex(recoded,1))
这个例子显示了从EncodedFile()返回并写入单独句柄的过程。
无论是用于读还是写,file_encoding始终引用打开的文件句柄的编码,data_encoding当read()或者write调用的时候使用。
$python3codecs_encodedfile.py
StartasUTF-8:b'6672616ec3a7616973'
EncodedtoUTF-16:b'fffe6600720061006e00e70061006900
7300'
BacktoUTF-8:b'6672616ec3a7616973'
非Unicode字符
尽管先前的例子都是用于Unicode编码,但是codecs可以用于许多其他的数据转换。
Python中包含的codecs可以用于处理base-64,bzip2,ROT-13,ZIP等其它数据格式。
codecs_rot13.py
importcodecs
importio
buffer=io.StringIO()
stream=codecs.getwriter('rot_13')(buffer)
text='abcdefghijklmnopqrstuvwxyz'
stream.write(text)
stream.flush()
print('Original:',text)
print('ROT-13:',buffer.getvalue())
任何可以表示为一个函数的转换只需要一个输入参数并且返回一个字节或者Unicode字符串即可注册为一个解码器。
对于「rot_13」,输入应该是一个Unicode字符串,输出也将是一个Unicode字符串。
$python3codecs_rot13.py
Original:abcdefghijklmnopqrstuvwxyz
ROT-13:nopqrstuvwxyzabcdefghijklm
使用codecs封装数据流相比直接使用zlib提供了更简单的接口。
codecs_zlib.py
importcodecs
importio
fromcodecs_to_heximportto_hex
buffer=io.BytesIO()
stream=codecs.getwriter('zlib')(buffer)
text=b'abcdefghijklmnopqrstuvwxyz\n'*50
stream.write(text)
stream.flush()
print('Originallength:',len(text))
compressed_data=buffer.getvalue()
print('ZIPcompressed:',len(compressed_data))
buffer=io.BytesIO(compressed_data)
stream=codecs.getreader('zlib')(buffer)
first_line=stream.readline()
print('Readfirstline:',repr(first_line))
uncompressed_data=first_line+stream.read()
print('Uncompressed:',len(uncompressed_data))
print('Same:',text==uncompressed_data)
并非所有的压缩和编码系统支持读取部分数据通过流接口readline()或者read(),因为他们需要找到压缩段的末尾来扩展它。
如果程序不能在内存中保存所有未压缩的数据,使用压缩库的增量访问功能而不是编解码器。
$python3codecs_zlib.py
Originallength:1350
ZIPcompressed:48
Readfirstline:b'abcdefghijklmnopqrstuvwxyz\n'
Uncompressed:1350
Same:True
增量编码
提供的一些编码,特别是zlib或者bz2,可能会在数据流处理时大幅改变数据流的长度。
对于大数据集,这些操作最好是渐进式的,一次只处理小量数据块。
IncrementalEncoder和IncrementalDecoderAPI设计用于这个目的。
codecs_incremental_bz2.py
importcodecs
importsys
fromcodecs_to_heximportto_hex
text=b'abcdefghijklmnopqrstuvwxyz\n'
repetitions=50
print('Textlength:',len(text))
print('Repetitions:',repetitions)
print('Expectedlen:',len(text)*repetitions)
#重复文本以构建大量数据
encoder=codecs.getincrementalencoder('bz2')()
encoded=[]
print()
print('Encoding:',end='')
last=repetitions-1
foriinrange(repetitions):
en_c=encoder.encode(text,final=(i==last))
ifen_c:
print('\nEncoded:{}bytes'.format(len(en_c)))
encoded.append(en_c)
else:
sys.stdout.write('.')
all_encoded=b''.join(encoded)
print()
print('Totalencodedlength:',len(all_encoded))
print()
#一次只解压一个字节串
decoder=codecs.getincrementaldecoder('bz2')()
decoded=[]
print('Decoding:',end='')
fori,binenumerate(all_encoded):
final=(i+1)==len(text)
c=decoder.decode(bytes([b]),final)
ifc:
print('\nDecoded:{}characters'.format(len(c)))
print('Decoding:',end='')
decoded.append(c)
else:
sys.stdout.write('.')
print()
restored=b''.join(decoded)
print()
print('Totaluncompressedlength:',len(restored))
每次传递给编码器或者解码器的时候,其内部状态会更新。
当状态一致的时候(由编解码器定义),数据返回并且状态重置。
在那之前,调用encode()或者decode()将不会返回任何数据。
当传入最后一批数据时,参数final应该设置为True,因此编解码器直到清除所有剩余的缓冲数据。
$python3codecs_incremental_bz2.py
Textlength:27
Repetitions:50
Expectedlen:1350
Encoding:.................................................
Encoded:99bytes
Totalencodedlength:99
Decoding:......................................................
..................................
Decoded:1350characters
Decoding:..........
Totaluncompressedlength:1350
Unicode数据和网络通信
网络套接字是字节流,与标准输入和输出不同,他们默认不支持编码。
这意味着想要通过网络发送或接收Unicode数据的程序必须在写入套接字之前将其编码为字节。
这个Server将它接收到的数据送回给发送端。
codecs_socket_fail.py
importsys
importsocketserver
classEcho(socketserver.BaseRequestHandler):
defhandle(self):
#接收字节并送回客户端
data=self.request.recv(1024)
self.request.send(data)
return
if__name__=='__main__':
importcodecs
importsocket
importthreading
address=('localhost',0)#向内核申请一个端口
server=socketserver.TCPServer(address,Echo)
ip,port=server.server_address#分配的端口和ip?
t=threading.Thread(target=server.serve_forever)
t.setDaemon(True)#设置为守护进程
t.start()
#连接到服务器
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((ip,port))
#发送数据
#错误:没有进行编码!
text='français'
len_sent=s.send(text)
#接收一个响应
response=s.recv(len_sent)
print(repr(response))
#清场
s.close()
server.socket.close()
可以在每次调用send()之前显示编码数据,但是如果缺少一个send()调用将导致编码错误。
$python3codecs_socket_fail.py
Traceback(mostrecentcalllast):
File"codecs_socket_fail.py",line43,in
codecs_socket.py
importsys
importsocketserver
classEcho(socketserver.BaseRequestHandler):
defhandle(self):
"""Getsomebytesandechothembacktotheclient.
Thereisnoneedtodecodethem,sincetheyarenotused.
"""
data=self.request.recv(1024)
self.request.send(data)
classPassThrough:
def__init__(self,other):
self.other=other
defwrite(self,data):
print('Writing:',repr(data))
returnself.other.write(data)
defread(self,size=-1):
print('Reading:',end='')
data=self.other.read(size)
print(repr(data))
returndata
defflush(self):
returnself.other.flush()
defclose(self):
returnself.other.close()
if__name__=='__main__':
importcodecs
importsocket
importthreading
address=('localhost',0)#让内核分配一个端口
server=socketserver.TCPServer(address,Echo)
ip,port=server.server_address#分配的端口和ip?
t=threading.Thread(target=server.serve_forever)
t.setDaemon(True)#设置守护进程
t.start()
#连接服务器
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((ip,port))
#使用读取器和写入器包装套接字
read_file=s.makefile('rb')
incoming=codecs.getreader('utf-8')(PassThrough(read_file))
write_file=s.makefile('wb')
outgoing=codecs.getwriter('utf-8')(PassThrough(write_file))
#发送数据
text='français'
print('Sending:',repr(text))
outgoing.write(text)
outgoing.flush()
#接收响应
response=incoming.read()
print('Received:',repr(response))
#清场
s.close()
server.socket.close()
这个例子展示了使用PassThrough去显示发送之前编码的数据,以及在客户端接收响应后对其进行解码。
$python3codecs_socket.py
Sending:'français'
Writing:b'fran\xc3\xa7ais'
Reading:b'fran\xc3\xa7ais'
Reading:b''
Received:'français'
自定义编码
因为Python已经自带了大量的标准编解码器,一般情况下应用不可能需要自定义编解码器。
但是如果需要的时候,codecs中有几个基类使这个过程变得更容易。
第一步是去了解编码转换的性质。
这些例子将使用「invertcaps」编码,进行大小写转换。
以下是对输入字符串执行此转换的编码函数的简单定义。
codecs_invertcaps.py
importstring
definvertcaps(text):
"""Returnnewstringwiththecaseofalllettersswitched.
"""
return''.join(
c.upper()ifcinstring.ascii_lowercase
elsec.lower()ifcinstring.ascii_uppercase
elsec
forcintext
)
if__name__=='__main__':
print(invertcaps('ABCdef'))
print(invertcaps('abcDEF'))
这个例子中,编解码器是同一个函数(与ROT-13一样)。
$python3codecs_invertcaps.py
abcDEF
ABCdef
尽管它很容易理解,但是实现并不高效,特别是对于大文本字符串。
幸运的是,codecs包含一些用于创建基于字符映射的编解码器的辅助函数。
字符映射编码由两个字典组成。
编码字典将输入字符串中的字符值转换为输出中的字节值,解码字典则以另一种方式运行。
首先创建解码映射,然后使用make_encoding_map()将其转换为编码映射。
C函数charmap_encode()和charmap_decode()使用这种映射关系高效地转换他们的输入数据。
codecs_invertcaps_charmap.py
importcodecs
importstring
#映射每个字符到它自己
decoding_map=codecs.make_identity_dict(range(256))
#创建一个大小写字母序数值对列表
pairs=list(zip(
[ord(c)forcinstring.ascii_lowercase],
[ord(c)forcinstring.ascii_uppercase],
))
#修改映射关系,大小写相互转换
decoding_map.update({
upper:lower
for(lower,upper)
inpairs
})
decoding_map.update({
lower:upper
for(lower,upper)
inpairs
})
#创建一个独立的编码映射图
encoding_map=codecs.make_encoding_map(decoding_map)
if__name__=='__main__':
print(codecs.charmap_encode('abcDEF','strict',
encoding_map))
print(codecs.charmap_decode(b'abcDEF','strict',
decoding_map))
print(encoding_map==decoding_map)
虽然逆变器的编码和解码图是相同的,但情况并非总是如此。
make_encoding_map()检测多个输入字符被编码为相同输出的情况,如果这样,就将编码值替换为None以将编码标记为未定义的情况。
$python3codecs_invertcaps_charmap.py
(b'ABCdef',6)
('ABCdef',6)
True
字符映射编码器和解码器支持前面描述的所有标准错误处理方法,因此不需要额外的工作来准守相关的API标准。
codecs_invertcaps_error.py
importcodecs
fromcodecs_invertcaps_charmapimportencoding_map
text='pi:\u03c0'
forerrorin['ignore','replace','strict']:
try:
encoded=codecs.charmap_encode(
text,error,encoding_map)
exceptUnicodeEncodeErroraserr:
encoded=str(err)
print('{:7}:{}'.format(error,encoded))
因为π的Unicode码点没有在这个编码图中,严格模式错误处理将会引发一个异常。
$python3codecs_invertcaps_error.py
ignore:(b'PI:',5)
replace:(b'PI:?',5)
strict:'charmap'codeccan'tencodecharacter'\u03c0'in
position4:charactermapsto
register()向注册表添加搜索功能,以便当用户想要使用编解码器时可以找到它。
搜索函数必须采用一个表示编码名称的字符串作为参数,并在找到编码时返回CodecInfo对象,否则返回None。
codecs_register.py
importcodecs
importencodings
defsearch1(encoding):
print('search1:Searchingfor:',encoding)
returnNone
defsearch2(encoding):
print('search2:Searchingfor:',encoding)
returnNone
codecs.register(search1)
codecs.register(search2)
utf8=codecs.lookup('utf-8')
print('UTF-8:',utf8)
try:
unknown=codecs.lookup('no-such-encoding')
exceptLookupErroraserr:
print('ERROR:',err)
可以注册多个搜索函数,并以此调用每个搜索函数,直到返回一个CodecInfo或者找完列表。
codecs内部注册的搜索函数知道怎么去加载标准的编解码器,例如,UTF-8,因此这些名称永远不会传递到自定义搜索函数。
$python3codecs_register.py
UTF-8:
codecs包含基类以帮助设置字符映射编码。
本示例将所有这些部分放在一起来注册一个所搜函数,该函数返回为invertcaps编解码器配置的CodecInfo实例。
codecs_invertcaps_register.py
importcodecs
fromcodecs_invertcaps_charmapimportencoding_map,decoding_map
classInvertCapsCodec(codecs.Codec):
"Statelessencoder/decoder"
defencode(self,input,errors='strict'):
returncodecs.charmap_encode(input,errors,encoding_map)
defdecode(self,input,errors='strict'):
returncodecs.charmap_decode(input,errors,decoding_map)
classInvertCapsIncrementalEncoder(codecs.IncrementalEncoder):
defencode(self,input,final=False):
data,nbytes=codecs.charmap_encode(input,
self.errors,
encoding_map)
returndata
classInvertCapsIncrementalDecoder(codecs.IncrementalDecoder):
defdecode(self,input,final=False):
data,nbytes=codecs.charmap_decode(input,
self.errors,
decoding_map)
returndata
classInvertCapsStreamReader(InvertCapsCodec,
codecs.StreamReader):
pass
classInvertCapsStreamWriter(InvertCapsCodec,
codecs.StreamWriter):
pass
deffind_invertcaps(encoding):
"""Returnthecodecfor'invertcaps'.
"""
ifencoding=='invertcaps':
returncodecs.CodecInfo(
name='invertcaps',
encode=InvertCapsCodec().encode,
decode=InvertCapsCodec().decode,
incrementalencoder=InvertCapsIncrementalEncoder,
incrementaldecoder=InvertCapsIncrementalDecoder,
streamreader=InvertCapsStreamReader,
streamwriter=InvertCapsStreamWriter,
)
returnNone
codecs.register(find_invertcaps)
if__name__=='__main__':
#无状态编解码器
encoder=codecs.getencoder('invertcaps')
text='abcDEF'
encoded_text,consumed=encoder(text)
print('Encoded"{}"to"{}",consuming{}characters'.format(
text,encoded_text,consumed))
#流写入器
importio
buffer=io.BytesIO()
writer=codecs.getwriter('invertcaps')(buffer)
print('StreamWriterforiobuffer:')
print('writing"abcDEF"')
writer.write('abcDEF')
print('buffercontents:',buffer.getvalue())
#增量解码器
decoder_factory=codecs.getincrementaldecoder('invertcaps')
decoder=decoder_factory()
decoded_text_parts=[]
forcinencoded_text:
decoded_text_parts.append(
decoder.decode(bytes([c]),final=False)
)
decoded_text_parts.append(decoder.decode(b'',final=True))
decoded_text=''.join(decoded_text_parts)
print('IncrementalDecoderconverted{!r}to{!r}'.format(
encoded_text,decoded_text))
无状态编解码器基类是Codec,使用新的实现重写了encode()和decode()(这个例子中,分别调用了charmap_encode()和charmap_decode())。
每个方法必须返回一个包含转换数据元祖以及消费的输入字节或者字符的数量。
IncrementalEncoder 和 IncrementalDecoder 作为增量接口的基类。
增量类的encode()和decode()方法是这样定义的,它们只返回实际转换后的数据。
然和有关缓冲池的信息都将保存为内部状态。
「invertcaps」不需要缓冲数据(它使用一对一映射)。
对于根据正在处理的数据产生不同输出量的编码(例如压缩算法),BufferedIncrementalEncoder和BufferedIncrementalDecoder是更合适的基类,因为他们管理输入的未处理部分。
StreamReader 和 StreamWriter也需要encode()和decode()方法,并且由于他们需要返回与Codec版本相同的值,因此可以使用多重继承来实现。
$python3codecs_invertcaps_register.py
Encoded"abcDEF"to"b'ABCdef'",consuming6characters
StreamWriterforiobuffer:
writing"abcDEF"
buffercontents:b'ABCdef'
IncrementalDecoderconvertedb'ABCdef'to'abcDEF'
推荐阅读
codecs标准库文档
locale --访问管理基于本地化的配置设置行为。
io --io模块也包含了处理编解码的文件和流包装。
socketserver --更详细的echo服务器的例子,请看socketserver模块。
encodings --Python标准库提供的编解码器实现包。
PEP100 --PythonUnicode集成PEP。
Unicode入门 --Python官方Unicode使用指导。
Textvs.DataInsteadofUnicodevs.8-bit --Python3.0的「新增功能」文章中关于文本处理更改的部分。
PythonUnicode对象 --FredrikLundh关于在Python2.0中使用非ASCII字符集的文章。
Python中如何使用UTF-8 --EvanJones使用Unicode的快速指南,包括XML数据和Byte-Order标记。
Unicode优点 --Bray介绍国际化和Unicode。
关于字符串 --Bray介绍了编程语言中字符串处理的历史。
字符vs.字节 --TimBray的「计算机程序员现代字符串处理的文章」的第一部分。
本部分涵盖了ASCII字节以外格式的文本内存表示。
字节顺序 --维基百科中字节顺序解释。
W3CXML字符实体定义 --无法用编码表示的字符引用的XML表示规范。
本文章首发在LearnKu.com网站上。
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照CC协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
原文地址:https://learnku.com/docs/pymotw/codecs-c...
译文地址:https://learnku.com/docs/pymotw/codecs-c...
上一篇
下一篇
Markdown文本
纠错改进
推荐文章:
更多推荐...
博客
收集了一些各大网站python的登陆方式,希望对学习python的小白,和想写爬虫的你们有所帮助,,本项目用于研究和分享各大网站的模拟登陆方式
14
/
5
|
3年前
讨论数量:0
发起讨论
只看当前版本
暂无话题~
提交
忘记密码?
or
注册
第三方账号登录
微信登录
GitHub登录
匿名举报,为防止滥用,仅管理员可见举报者。
我要举报该,理由是:
垃圾广告:恶意灌水、广告、推广等内容
无意义内容:测试、灌水、文不对题、消极内容、文章品质太差等
违规内容:色情、暴利、血腥、敏感信息等
不友善内容:人身攻击、挑衅辱骂、恶意行为
科学上网:翻墙、VPN、Shadowsocks,政策风险,会被关站!
不懂提问:提问太随意,需要再做一遍《提问的智慧》测验
随意提问:提问没有发布在社区问答分类下
排版混乱:没有合理使用Markdown编写文章,未使用代码高亮
内容结构混乱:逻辑不清晰,内容混乱,难以阅读
标题随意:标题党、标题不释义
尊重版权:分享付费课程、破解软件(付费),侵犯作者劳动成果
其他理由:请补充说明
举报
取消
延伸文章資訊
- 1python自然語言編碼轉換模組codecs介紹 - 程式前沿
python對多國語言的處理是支援的很好的,它可以處理現在任意編碼的字元,這裡深入的研究一下python對多種不同語言的處理。 有一點需要清楚的是, ...
- 2Python小坑:open()和codecs.open()区别,以及常见指令和 ...
啥也别说了,用codecs.open()就完事了写贴原因因为接触Python时候已经是Python3.x的年代了,用的文件操作最多的就是with open() as file_:这一段话, ...
- 37.11. codecs — 字符编码和解码| 文件系统 - LearnKu
Python Unicode HOWTO 是特别有帮助的。 编码#. 理解编码的最好方式是去查看用不同方式对相同字符串编码产生的字节序列 ...
- 4python 文件读写时用open还是codecs.open - slower - 博客园
python 文件读写时用open还是codecs.open. 当我面有数据需要保存时,第一时间一般会想到写到一个txt文件中,当然,数据量比较大的时候还是写到数据库 ...
- 5Python的codecs模块- 腾讯云开发者社区
encoding: gb2312 -*- import codecs, sys print ' - ' * 60 # 创建gb2312编码器look = codecs.lookup( " gb2...