美文网首页
第四章:文本和字节序列

第四章:文本和字节序列

作者: 许志辉Albert | 来源:发表于2021-02-08 11:11 被阅读0次

4.1 字符问题

“字符串”是个相当简单的概念:一个字符串是一个字符序列。问题出在“字符”的定义上。
在 2015 年,“字符”的最佳定义是Unicode 字符。因此,从Python 3 的 str 对象中获取 的元素是Unicode 字符,这相当于从Python 2 的 unicode 对象中获取的元素,而不是从 Python 2 的 str 对象中获取的原始字节序列。
Unicode 标准把字符的标识和具体的字节表述进行了如下的明确区分。
• 字符的标识,即码位,是 0~1 114 111 的数字(十进制),在 Unicode 标准中以 4~6 个 十六进制数字表示,而且加前缀“U+”。例如,字母 A 的码位是 U+0041,欧元符号的 码位是 U+20AC,高音谱号的码位是 U+1D11E。在 Unicode 6.3 中(这是 Python 3.4 使 用的标准),约 10% 的有效码位有对应的字符。
• 字符的具体表述取决于所用的编码。编码是在码位和字节序列之间转换时使用的算法。 在 UTF-8 编码中,A(U+0041)的码位编码成单个字节 \x41,而在 UTF-16LE 编码中 编码成两个字节 \x41\x00。再举个例子,欧元符号(U+20AC)在 UTF-8 编码中是三个 字节——\xe2\x82\xac,而在 UTF-16LE 中编码成两个字节:\xac\x20。
把码位转换成字节序列的过程是编码;把字节序列转换成码位的过程是解码。

s = 'café'
print(len(s))
b = s.encode('utf-8')
print(b)
print(len(b))
print(b.decode('utf-8'))
1

Unicode(又称统一码、万国码、单一码)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。

汉字“字”对应的数字是23383(十进制),十六进制表示为5B57。在Unicode中,我们有很多方式将数字23383表示成程序中的数据,包括:UTF-8、UTF-16、UTF-32。UTF是“Unicode Transformation Format”的缩写,可以翻译成Unicode字符集转换格式,即怎样将Unicode定义的数字转换成程序数据。

4.2字节概要

新的二进制序列类型在很多方面与 Python 2 的 str 类型不同。首先要知道,Python 内置 了两种基本的二进制序列类型:Python 3 引入的不可变 bytes 类型和 Python 2.6 添加的可 变 bytearray 类型。( Python 2.6 也引入了 bytes 类型,但那只不过是 str 类型的别名,与 Python 3 的 bytes 类型不同。)
bytes 或 bytearray 对象的各个元素是介于0~255(含)之间的整数,而不像Python 2 的 str 对象那样是单个的字符。然而,二进制序列的切片始终是同一类型的二进制序列,包 括长度为 1 的切片

cafe = bytes( 'café' , encoding = 'utf-8')
print(cafe)

print(cafe[0])

print(cafe[:1])

cafe_arr = bytearray(cafe)
print(cafe_arr)
print(cafe_arr[-1 :])
2

my_bytes[0] 获取的是一个整数,而my_bytes[:1] 返回的是一个长度为1 的 bytes 对象——这一点应该不会让人意外。s[0] == s[:1] 只对 str 这个序列类 型成立。不过,str 类型的这个行为十分罕见。对其他各个序列类型来说,s[i] 返回一个元素,而 s[i:i+1] 返回一个相同类型的序列,里面是 s[i] 元素。
虽然二进制序列其实是整数序列,但是它们的字面量表示法表明其中有 ASCII 文本。因 此,各个字节的值可能会使用下列三种不同的方式显示。

  • 可打印的 ASCII 范围内的字节(从空格到 ~),使用 ASCII 字符本身
  • 制表符、换行符、回车符和 \ 对应的字节,使用转义序列 \t、\n、\r 和 \
  • 其他字节的值,使用十六进制转义序列(例如,\x00 是空字节)。

构建 bytes 或 bytearray 实例还可以调用各自的构造方法,传入下述参数。

  • 一个str对象和一个encoding关键字参数
  • 一个可迭代对象
  • 一个整数

使用缓冲类对象构建二进制序列是一种低层操作,可能涉及类型转换。

import array
numbers = array.array('h', [-2, -1, 0, 1, 2])
octets = bytes(numbers)
octets
3
  • 结构体和内存视图
    struct 模块提供了一些函数,把打包的字节序列转换成不同类型字段组成的元组,还有 一些函数用于执行反向转换,把元组转换成打包的字节序列。struct 模块能处理 bytes、 bytearray 和 memoryview 对象。

4.3基本的编码器

Python 自带了超过 100 种编解码器(codec, encoder/decoder),用于在文本和字节之间相互 转换。每个编解码器都有一个名称,如 'utf_8',而且经常有几个别名,如 'utf8'、'utf8' 和 'U8'。这些名称可以传给 open()、str.encode()、bytes.decode() 等函数的 encoding 参数。示例 4-5 使用 3 个编解码器把相同的文本编码成不同的字节序列。


4

4.4了解编码问题

虽然有个一般性的UnicodeError 异常,但是报告错误时几乎都会指明具体的异常: UnicodeEncodeError(把字符串转换成二进制序列时)或UnicodeDecodeError(把二进 制序列转换成字符串时)。如果源码的编码与预期不符,加载 Python 模块时还可能抛出 SyntaxError。接下来的几节说明如何处理这些错误。

出现与Unicode 有关的错误时,首先要明确异常的类型。导致编码问题的 是 UnicodeEncodeError、UnicodeDecodeError,还是如 SyntaxError 的其他错 误?解决问题之前必须清楚这一点。

4.4.1 解决UnicodeEncodeError

多数非 UTF 编解码器只能处理 Unicode 字符的一小部分子集。把文本转换成字节序列时, 如果目标编码中没有定义某个字符,那就会抛出 UnicodeEncodeError 异常,除非把 errors 参数传给编码方法或函数,对错误进行特殊处理。

city = 'São Paulo'
print(city.encode('utf-8'))
print(city.encode('utf-16'))
print(city.encode('iso8859_1'))
print(city.encode('cp437'))
print(city.encode('cp437',errors = 'ignore'))
print(city.encode('cp437',errors = 'replace'))
print(city.encode('cp437',errors = 'xmlcharrefreplace'))
5
city = 'São Paulo'
print(city.encode('utf-8'))
print(city.encode('utf-16'))
print(city.encode('iso8859_1'))
#print(city.encode('cp437'))
print(city.encode('cp437',errors = 'ignore'))
print(city.encode('cp437',errors = 'replace'))
print(city.encode('cp437',errors = 'xmlcharrefreplace'))
6

4.4.2 处理UnicodeDecodeError

octets = b'Montr\xe9al'
print(octets.decode('cp1252'))
print(octets.decode('iso8859_7'))
print( octets.decode('koi8_r') )
print( octets.decode('utf_8') )
print(octets.decode('utf_8', errors='replace') )
7
octets = b'Montr\xe9al'
print(octets.decode('cp1252'))
print(octets.decode('iso8859_7'))
print( octets.decode('koi8_r') )
#print( octets.decode('utf_8') )
print(octets.decode('utf_8', errors='replace') )
8

4.4.3 使用预期之外的编码加载模块时抛出的SyntaxError

Python 3 默认使用 UTF-8 编码源码,Python 2(从 2.5 开始)则默认使用 ASCII。如果加载 的 .py 模块中包含 UTF-8 之外的数据,而且没有声明编码,会得到类似下面的消息:
SyntaxError: Non-UTF-8 code starting with '\xe1' in file ola.py on line 1, but no encoding declared;
GNU/Linux 和 OS X 系统大都使用 UTF-8,因此打开在 Windows 系统中使用 cp1252 编码 的 .py 文件时可能发生这种情况。注意,这个错误在 Windows 版 Python 中也可能会发生, 因为 Python 3 为所有平台设置的默认编码都是 UTF-8

4.4.4 如何找出字节序列的编码

如何找出字节序列的编码?简单来说,不能。必须有人告诉你。

有些通信协议和文件格式,如HTTP 和 XML,包含明确指明内容编码的首部。可以肯 定的是,某些字节流不是ASCII,因为其中包含大于127 的字节值,而且制定UTF-8 和 UTF-16 的方式也限制了可用的字节序列。不过即便如此,我们也不能根据特定的位模式 来 100% 确定二进制文件的编码是 ASCII 或 UTF-8。

然而,就像人类语言也有规则和限制一样,只要假定字节流是人类可读的纯文本,就可 能通过试探和分析找出编码。例如,如果 b'\x00' 字节经常出现,那么可能是 16 位或 32 位编码,而不是8 位编码方案,因为纯文本中不能包含空字符;如果字节序列 b'\x20\ x00' 经常出现,那么可能是 UTF-16LE 编码中的空格字符(U+0020),而不是鲜为人知的 U+2000 EN QUAD 字符——谁知道这是什么呢!

统一字符编码侦测包 Chardet(https://pypi.python.org/pypi/chardet)就是这样工作的,它能 识别所支持的 30 种编码。Chardet 是一个 Python 库,可以在程序中使用,不过它也提供了 命令行工具 chardetect。下面是它对本章书稿文件的检测报告:

$ chardetect 04-text-byte.asciidoc 04-text-byte.asciidoc: utf-8 with confidence 0.99

二进制序列编码文本通常不会明确指明自己的编码,但是 UTF 格式可以在文本内容的开头 添加一个字节序标记。

4.5 处理文本文件

处理文本的最佳实践是“Unicode 三明治”(如图 4-2 所示)。4 意思是,要尽早把输入(例 如读取文件时)的字节序列解码成字符串。这种三明治中的“肉片”是程序的业务逻辑, 在这里只能处理字符串对象。在其他处理过程中,一定不能编码或解码。对输出来说,则 要尽量晚地把字符串编码成字节序列。多数 Web 框架都是这样做的,使用框架时很少接触 字节序列。例如,在 Django 中,视图应该输出 Unicode 字符串;Django 会负责把响应编 码成字节序列,而且默认使用 UTF-8 编码。

9

在 Python 3 中能轻松地采纳 Unicode 三明治的建议,因为内置的 open 函数会在读取文件时 做必要的解码,以文本模式写入文件时还会做必要的编码,所以调用 my_file.read() 方法得到的以及传给 my_file.write(text) 方法的都是字符串对象,可以看出,处理文本文件很简单。但是,如果依赖默认编码,你会遇到麻烦。

10

4.6 为了正确比较而规范化Unicode字符串

因为Unicode 有组合字符(变音符号和附加到前一个字符上的记号,打印时作为一个整 体),所以字符串比较起来很复杂
例如,“café”这个词可以使用两种方式构成,分别有 4 个和 5 个码位,但是结果完全一样

s1 = 'café'
s2 = 'cafe\u0301' 
print(s1,s2)
print( len(s1), len(s2) )
print( s1 == s2 )
11

后续的一些内容平时用的比较少就只是简单的进行浏览,不做进一步的内容总结提取

相关文章

  • fluent python- 第 5 章 一等函数-面向对象(附

    第 4 章 文本和字节序列 人类使用文本, 计算机使用字节序列。 第 5 章 一等函数 前言: 在 Python ...

  • 第四章:文本和字节序列

    4.1 字符问题 “字符串”是个相当简单的概念:一个字符串是一个字符序列。问题出在“字符”的定义上。在 2015 ...

  • 文本与字节序列

    字符问题 字符的标识:码位,是数字,如 Unicode 标准下 A 的码位是 U+0041字符的具体表述取决于编码...

  • chapter4 文本和字节序列

    字符问题 Unicode 标准把字符的标识和具体的字节表述进行了明确区分 字符的标识,即码位,是0-1114111...

  • java io流个人使用方法总结

    使用字节流来读写文件 使用字符流来读写文件 读取不同的编码文本时候需要进行修正 更换其输出的位置 序列化和反序列化...

  • MarkDown语法

    ~~无序列表 ~~ 文本1 文本2 文本3 有序列表 标题2 文本3 文本1 文本2 文本3 文本4 链接和图片...

  • HelloWorld

    这是预览模式 MarkDown常用语法 有序列表 文本 文本 文本 无序列表 文本1 文本2 文本3 链接和图片 ...

  • Java序列化和反序列化

    1. 什么是序列化和反序列化 序列化:把Java对象转化为字节序列的过程。 反序列化:把字节序列恢复为Java对象...

  • MarkDown常用标签

    有序列文本 实现方式:数字. 序列1 序列2 无序列文本 实现方法:- 文本 文本1 文本2 连接的使用 【文本】...

  • Java序列化

    1、Java序列化和反序列化含义 序列化:把对象转换为字节序列的过程称为序列化。反序列化:把字节序列恢复为对象的过...

网友评论

      本文标题:第四章:文本和字节序列

      本文链接:https://www.haomeiwen.com/subject/koexxltx.html