python字符串转成0x字节组_python高级(四)—— 文本和字节序列(编码问题)

本文主要内容

字符

字节

结构体和内存视图

字符和字节之间的转换——编解码器

BOM鬼符

标准化Unicode字符串

Unicode文本排序

字符

'''字符编码问题是经常困扰python编程人员的问题,我在编写爬虫的过程中也经常遇到这个头疼的事。

从python3开始,明确区分了人类语言(文本字符串)和机器语言(二进制字节),咱们先说文本字符串

开始之前,得对"字符"进行定义:

字符:Unicode字符,从python3的str对象中获取的元素是Unicode字符

字符串:字符串就是一个字符序列(这里对于(一)中内容相呼应)'''

if __name__ == "__main__":#创建字符

s1 = str('a')

s2= 'b's3= u'c'

print(s1, s2, s3) #a b c

此时只用记住在python3中字符就是unicode,也就是str是unicode,这是人类能够看懂的语言。

字节

'''python3中内置有两种基本的二进制序列类型:不可变的bytes和可变bytearray

(1)bytes和bytearray的各个元素是介于0~255(8个bit)之间的整数;

(2)二进制序列的切片始终是同一类型的二进制序列'''

if __name__ == "__main__":#创建bytes 和 bytearray

b1 = bytes('abc你好', encoding='utf8') #关于encode稍后会说,不知道有没有人和我一样总是将编码与解码的方向混淆

print(b1) #b'abc\xe4\xbd\xa0\xe5\xa5\xbd'

b2= bytearray('abc你好', encoding='utf8')print(b2) #bytearray(b'abc\xe4\xbd\xa0\xe5\xa5\xbd')

#切片(提示:序列都可以切片)

print(b1[3:5]) #b'\xe4\xbd'

print(b2[3:5]) #bytearray(b'\xe4\xbd')

#使用列表取值的方法试试

print(b1[3]) #228 此时取出来的就不是字节序列了,而是一个元素

for _ inb1:print(_, end=',') #97,98,99,228,189,160,229,165,189, 这都是8bit的整数

#bytes的不可变 vs. bytearray的可变

#b1[3] = 160 # 报错:'bytes' object does not support item assignment

print(id(b2), b2) #4373768376 bytearray(b'abc\xe4\xbd\xa0\xe5\xa5\xbd')

b2[2] = 78

print(id(b2), b2) #4373768376 bytearray(b'abN\xe4\xbd\xa0\xe5\xa5\xbd')

#将b2转换成字符串看看

print(b2.decode('utf8')) #abN你好

#注意,这里之所以能够用utf8转成unicode,是因为N的ascii码和utf8一致

b2.extend(bytearray('添加的内容', encoding='utf8')) #既然是可变序列,bytearray当然拥有一般的序列的方法

print(id(b2), b2) #4373768376 bytearray(b'abN\xe4\xbd\xa0\xe5\xa5\xbd\xe6\xb7\xbb\xe5\x8a\xa0\xe7\x9a\x84\xe5\x86\x85\xe5\xae\xb9')

print(b2.decode('utf8')) #abN你好添加的内容

#PS:大家可以将二进制序列当成列表,元素就是ascii编码(0~255)

结构体和内存视图

'''struct可以从二进制序列中提取结构化信息。

struct模块提供了一些函数,可以将打包的字节序列转换成不同类型字段组成的元组;还有一些函数用于执行反向转换。

struct模块可以处理bytes、bytearray、memoryview对象。'''

importstructif __name__ == "__main__":#memoryview类用于共享内存,可以访问其他二进制序列、打包的数组和缓冲中的数据切片,该操作无需赋值字节序列

fmt = '<3s3sHH' #设置格式,< 是小字节序,3s3s是两个3字节序列,HH是两个16位二进制整数

with open('L3_图_python.jpg', 'rb') as f: # 需要在github中下载后运行

img=memoryview(f.read())print(bytes(img[:10])) #b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x02\x00\x1c\x00\x1c\x00\x00'

print(struct.unpack(fmt, img[:10])) #(b'\xff\xd8\xff', b'\xe0\x00\x10', 17994, 17993) :拆包

del img

字符和字节之间的转换——编解码器

'''python自带有超过100中编解码器,用于在字符串和字节之间相互转换。

每个编码都有多个名称,例如'utf_8'、'utf8'、'utf-8'、'U8',这些都可以传递给open()、str.encode()、bytes.decode()中的

encoding参数'''

if __name__ == "__main__":#看看不同的编码效果

for codec in ['gbk', 'utf8', 'utf16']:print(codec, "你好".encode(codec), sep='\t')'''gbk b'\xc4\xe3\xba\xc3'

utf8 b'\xe4\xbd\xa0\xe5\xa5\xbd'

utf16 b'\xff\xfe`O}Y''''

#咱们再来解码

print(b'\xc4\xe3\xba\xc3'.decode('gbk')) #你好

print(b'\xe4\xbd\xa0\xe5\xa5\xbd'.decode('utf8')) #你好

print(b'\xff\xfe`O}Y'.decode('utf16')) #你好

'''遇到编码问题一般很烦躁,下面来看看一般怎么解决编码问题。

(1)UnicodeEncodeError

(2) UnicodeDecodeError'''

if __name__ == "__main__":#(1)UnicodeEncodeError

#使用errors参数

s1 = "hello,你长胖啦".encode('latin-1', errors='ignore')print(s1) #b'hello' 使用 errors='ignore' 忽略了无法编码的字符

s2= "hello,你长胖啦".encode('latin-1', errors='replace')print(s2) #b'hello?????' 使用errors='replace'将无法编码的字符用问好代替

s3= "hello,你长胖啦".encode('latin-1', errors='xmlcharrefreplace')print(s3) #b'hello,你长胖啦' 使用errors='xmlcharrefreplace'将无法编码的内容替换成XML实体

#(2) UnicodeDecodeError

#乱码字符称为鬼符,以下实例演示出现鬼符的情况

s4= b'Montr\xe9al'

print(s4.decode('cp1252')) #Montréal

print(s4.decode('iso8859_7')) #Montrιal

print(s4.decode('koi8_r')) #MontrИal

#print(s4.decode('utf8')) # 报错:UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe9 in position 5: invalid continuation byte

print(s4.decode('utf8', errors='replace')) #Montr�al

'''大多数人都遇到过乱码问题,并且可能总是调试不成功,这可能是各种程序之间的编码不匹配

以下代码引用自,可以用来查看当前环境的一些默认编码'''

#-*- coding: utf-8 -*-

importsys, locale

expressions= """locale.getpreferredencoding()

type(my_file)

my_file.encoding

sys.stdout.isatty()

sys.stdout.encoding

sys.stdin.isatty()

sys.stdin.encoding

sys.stderr.isatty()

sys.stderr.encoding

sys.getdefaultencoding()

sys.getfilesystemencoding()"""my_file= open('dummy', 'w')for expression inexpressions.split():

value=eval(expression)print(expression.rjust(30), '->', repr(value))'''我电脑运行结果如下:

(' locale.getpreferredencoding()', '->', "'UTF-8'")

(' type(my_file)', '->', "")

(' my_file.encoding', '->', 'None')

(' sys.stdout.isatty()', '->', 'True')

(' sys.stdout.encoding', '->', "'UTF-8'")

(' sys.stdin.isatty()', '->', 'True')

(' sys.stdin.encoding', '->', "'UTF-8'")

(' sys.stderr.isatty()', '->', 'True')

(' sys.stderr.encoding', '->', "'UTF-8'")

(' sys.getdefaultencoding()', '->', "'ascii'")

(' sys.getfilesystemencoding()', '->', "'utf-8'")'''

BOM鬼符

'''关于BOM的内容比较底层,以下内容全部选自'''

标准化Unicode字符串

'''初一看这个标题可能会有点蒙,难道Unicode本身还不够标准么?

先看看以下的例子:'''s1= 'café's2= 'cafe\u0301'

print(s1, s2) #café café

print(s1 == s2) #False

'''我们发现café可以用'café'和'cafe\u0301'两种方式表示,这个词对于人来说是一样的,但是这两种表示对于计算机来说确实不一样的

向这样的序列叫"标准等价物",在计算机中存储的值不相等,但应用程序应该认为相等。

要解决这个问题,需要用到unicodedata.nomalize函数,它的第一个参数可以选择这四种形式的一个:"NFC"、"NFD"和"NFKC"、"NFKD"

"NFC":使用最少的码为构成等价的字符串

"NFD":把组合的字符分割成基本字符和单独的组合字符

"NFKC"&"NFKD":这两种是较严格的规范形式,对"兼容字符有影响"'''

from unicodedata importnormalizeif __name__ == "__main__":#"NFC" & "NFD"

print(s1.encode('utf8'), s2.encode('utf8')) #b'caf\xc3\xa9' b'cafe\xcc\x81'

s1 = normalize("NFC", s1)

s2= normalize("NFC", s2)print(s1, s2) #café café

print(s1 == s2) #True

print(s1.encode('utf8'), s2.encode('utf8')) #b'caf\xc3\xa9' b'caf\xc3\xa9'

#"NFKC"&"NFKD"

#这两种方式会损失信息,所以不建议使用,除非一些特殊情况,比如搜索和索引中

s3 = '½'

print(normalize('NFKC', s3)) #1⁄2 将½转换成了1⁄2

s4 = '™'

print(normalize('NFKC', s4)) #TM 将™转换成了TM

'''另外,如果比较的时候不区分大小写,建议使用str.casefold(), 它与lower基本一致,其中大约有116个特殊的字符结果不同'''s5= 'AKJkakshfKHDSdshfKSDShKkHkjhKJkgJhgJHkkHkjhJKhKJhK'

print(s5.casefold()) #akjkakshfkhdsdshfksdshkkhkjhkjkgjhgjhkkhkjhjkhkjhk

Unicode文本排序

'''python比较序列时,会一一比较其中的元素。对于字符来说,比较的是其码位,主要是比较的ascii码;

非ascii文本的标准排序方式是使用locale.strxfrm函数,但是使用这个函数必须事先设定区域,但有些操作系统不支持,并且改变区域设置并不十分合适

建议使用pyuca.Collator.sort_key方法进行排序'''

if __name__ == "__main__":#python默认的排序

fruits = ['caju', 'atemoia', 'cajá', 'açaí', 'acerola']print(sorted(fruits)) #['acerola', 'atemoia', 'açaí', 'caju', 'cajá']

#但正确排序应该是:['açaí','acerola', 'atemoia', 'cajá', 'caju']

#使用pyuca.Collator.sort_key

importpyucaprint(sorted(fruits, key = pyuca.Collator().sort_key)) #['açaí', 'acerola', 'atemoia', 'cajá', 'caju']

#pyuca可以将自定义排序表路径传递给Collator()构造方法,pyuca默认使用自带的allkeys.txt

python高级系列文章目录

你可能感兴趣的:(python字符串转成0x字节组_python高级(四)—— 文本和字节序列(编码问题))