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

[读书笔记]流畅的python-文本和字节序列

2020-06-21 21:03 120 查看

流畅的python-文本和字节序列

  • 基本的编解码器
  • 编码问题
  • 处理文本文件
  • unicode规范化(比较和排序)
  • unicode文本排序
  • Unicode数据库
  • 支持字符串和字节序列的双模式API
  • 字符和字节

    字符

    “字符”的最佳定义是unicode字符,从python3的str对象中获取的元素是unicode字符,相当于从py2的unicode对象中获取的元素,而不是从py2的str对象中获取的原始字节序列。

    字符的表示和具体的字节表述

    • 字符的表示,即码位,在unicode标准中以4-6个十六进制数字表示。
    • 字符的具体表述取决于所用的编码。如utf-8,utf-16LE

    字节

    python3中引入了不可变bytes类型和可变bytearray类型。bytes和bytearray对象的各个元素是介于0-255间的整数(1byte)。切片也是同一类型的二进制序列。
    字节的字面量表示

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

    byte的构造

    • bytes.fromhex()
    • 一个 str 对象和一个 encoding 关键字参数。
    • 一个可迭代对象,提供 0~255 之间的数值。
    • 一个实现了缓冲协议的对象(如 bytes、bytearray、memoryview、array.array);此时,把源对象中的字节序 列复制到新建的二进制序列中。

    基本的编解码器

    • Latin1:一种重要的编码,是其他编码的基础,例如 cp1252 和 Unicode(注意,latin1 与 cp1252 的字节值是一样的,甚至连码位也相同)。
    • cp1252 :Microsoft 制定的 latin1 超集,添加了有用的符号,例如弯引号和€(欧元);有些 Windows 应用把它称为“ANSI”,但它并不是 ANSI 标准。
    • cp437:IBM PC 最初的字符集,包含框图符号。与后来出现的 latin1 不兼容。
    • gb2312:用于编码简体中文的陈旧标准;这是亚洲语言中使用较广泛的多字节编码之一。
    • utf-8: 目前 Web 中最常见的 8 位编码; 与 ASCII 兼容(纯 ASCII 文本是有效的 UTF-8 文 本)。

    编码问题

    处理UnicodeEncodeError

    可以在enocode是指定errors=xxx

    >>> city = 'São Paulo'
    >>> city.encode('utf_8')
    b'S\xc3\xa3o Paulo'
    >>> city.encode('utf_16')
    b'\xff\xfeS\x00\xe3\x00o\x00 \x00P\x00a\x00u\x00l\x00o\x00'
    >>> city.encode('iso8859_1')
    b'S\xe3o Paulo'
    >>> city.encode('cp437')
    Traceback (most recent call last):  File "<stdin>", line 1, in <module>  File "/.../lib/python3.4/encodings/cp437.py", line 12, in encode    return codecs.charmap_encode(input,errors,encoding_map) UnicodeEncodeError: 'charmap' codec can't encode character '\xe3' in position 1: character maps to <undefined>
    >>> city.encode('cp437', errors='ignore')
    b'So Paulo'
    >>> city.encode('cp437', errors='replace')
    b'S?o Paulo'
    >>> city.encode('cp437', errors='xmlcharrefreplace') ➏
    b'São Paulo'

    处理UnicodeDecodeError

    指定errors,如:
    octets.decode(‘utf_8’, errors=‘replace’)

    如何找出字节序列的编码

    统一字符编码侦测包 Chardet,,它能 识别所支持的 30 种编码。Chardet 是一个 Python 库,可以在程序中使用,不过它也提供了 命令行工具 chardetect。

    BOM:鬼符

    UTF-16 编码的序列开头有几个额外的字节b’\xff\xfe’。这就是 BOM,即字节序标记(byte-order mark),指明编码时使用 Intel CPU 的小字节序。

    在小字节序设备中,各个码位的最低有效字节在前面:字母 ‘E’ 的码
    位是 U+0045(十进制数 69),在字节偏移的第 2 位和第 3 位编码为 69和 0。

    在大字节序 CPU 中,编码顺序是相反的;‘E’ 编码为 0 和 69。

    UTF-8 的一大优势是,不管设备使用哪种字节序,生成的字节序列始终一致,因此不需要 BOM。
    尽管如此,某些Windows 应用(尤其是 Notepad)依然会在 UTF-8 编码的文件中添加
    BOM;而且,Excel 会根据有没有 BOM 确定文件是不是 UTF-8 编码,
    否则,它假设内容使用 Windows 代码页(codepage)编码。

    处理文本文件

    处理文本的最佳实践是“Unicode 三明治”。 意思是,要尽早把输入(例 如读取文件时)的字节序列解码成字符串。不要依赖默认编码.

    unicode规范化(比较和排序)

    问题:因为 Unicode 有组合字符(变音符号和附加到前一个字符上的记号,打印时作为一个整 体),所以字符串比较起来很复杂。
    方法:使用 unicodedata.normalize 函数提供的 Unicode 规范化。

    ** 大小写折叠**
    大小写折叠其实就是把所有文本变成小写,再做些其他转换。这个功能由 str.casefold() 方法(Python 3.3 新增)支持。

    规范化文本匹配函数
    nfc_equal 和 fold_equal

    unicode文本排序

    方法一:
    在 Python 中,非 ASCII 文本的标准排序方式是使用 locale.strxfrm 函数,根据 locale 模块的文档,这 个函数会“把字符串转换成适合所在区域进行比较的形式”。

    方法二:
    使用PyUCA库

    PyUCA 没有考虑区域设置。如果想定制排序方式,可以把自定义的排序表路径传给 Collator() 构造方法。PyUCA 默认使用项目自带的 allkeys.txt(https://github.com/jtauber/pyuca),这就是 Unicode 6.3.0 的“Default Unicode Collation Element Table”(http://www.unicode.org/Public/UCA/6.3.0/allkeys.txt)的副本。

    Unicode数据库

    Unicode 标准提供了一个完整的数据库(许多格式化的文本文件),不仅包括码位与字符 名称之间的映射,还有各个字符的元数据,以及字符之间的关系。例如,Unicode 数据库 记录了字符是否可以打印、是不是字母、是不是数字,或者是不是其他数值符号。字符串 的 isidentifier、isprintable、isdecimal 和 isnumeric 等方法就是靠这些信息作 判断的。 str.casefold 方法也用到了 Unicode 表中的信息。 unicodedata 模块中有几个函数用于获取字符的元数据。例如,字符在标准中的官方名称是不是组合字符(如结合波形符构成的变音符号等),以及符号对应的人类可读数值(不是码位)。
    注:re 模块对 Unicode 的支持并不充分。PyPI 中有个新开发的 regex 模块,它的最终目的是取代 re 模块,以提供更好的 Unicode 支持。

    支持字符串和字节序列的双模式API

    re和os模块中的一些函数能接受字符串或字节序列

    正则匹配:re
    如果使用字节序列构建正则表达式,\d 和 \w 等模式只能匹配 ASCII 字符;相比之下,如果是字符串模式,就能匹配 ASCII 之外的 Unicode 数字或字母。


    前两个正则表达式是字符串类型。 后两个正则表达式是字节序列类型。

    os函数中的字符串和字节序列
    GNU/Linux 内核不理解 Unicode,为了规避这个问题,os 模块中的所有函数、文件名或路径名参数既能使用字符串,也能 使用字节序列。如果这样的函数使用字符串参数调用,该参数会使用 sys.getfilesystemencoding() 得到的编解码器自动编码,然后操作系统会使用相同 的编解码器解码。

    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: