zoukankan      html  css  js  c++  java
  • 《Fluent Python》 CH.04_数据结构-文本和字节序列 (码位字符、Unicode、文本编解码异常等)

    本章主要内容

    • 字符、码位和字节表述
    • bytes、bytearray 和 memoryview 等二进制序列的独特特性
    • 全部 Unicode 和陈旧字符集的编解码器
    • 避免和处理编码错误
    • 处理文本文件的最佳实践
    • 默认编码的陷阱和标准 I/O 的问题
    • 规范化 Unicode 文本,进行安全的比较
    • 规范化、大小写折叠和暴力移除音调符号的实用函数
    • 使用 locale 模块和 PyUCA 库正确地排序 Unicode 文本
    • Unicode 数据库中的字符元数据
    • 能处理字符串和字节序列的双模式 API

    其他

    • jupyter nbconvert --to markdown E:PycharmProjectsTianChiProject0_山枫叶纷飞competitions13_fluent_pythonCH.04_数据结构-文本和字节序列.ipynb

    4.1 字符问题

    "字符"的最佳定义就是Unicode字符,因此从Python3的str对象中获取的元素是Unicode字符。
    区别

    • python2 unicode 类似于 python3 str
    • python2 str 类似于 python3 bytes

    Unicode 标准把字符的标识和具体的字节表述进行了如下的明确区分:

    • 字符的标示,即码位,在 Unicode 标准中以 4~6 个十六进制数字表示,而且加前缀“U+”。
    • 字符的具体表述取决于所用的编码。编码是在码位和字节序列之间 转换时使用的算法。如utf-8算法、utf-16算法等。
    • 百度百科=》在Unicode中:汉字“字”对应的数字是23383(十进制),十六进制表示为5B57。在Unicode中,我们有很多方式将数字23383表示成程序中的数据,包括:UTF-8、UTF-16、UTF-32。UTF是“Unicode Transformation Format”的缩写,可以翻译成Unicode字符集转换格式,即怎样将Unicode定义的数字转换成程序数据。

    把码位(可读文本)转换成字节序列(机器码)的过程是编码;把字节序列转换成码位的过程是 解码。

    示例,编码和解码:

    s = 'café' # python3中默认使用的是Unicode编码,'café' 字符串有 4 个 Unicode 字符。
    len(s)
    b = s.encode('utf8')
    b
    
    b'cafxc3xa9'
    
    len(b)
    
    5
    
    b.decode('utf8')
    
    'café'
    

    4.2 字节概要

    bytes 或 bytearray 对象的各个元素是介于 0~255(含)之间的整 数,而不像 Python 2 的 str 对象那样是单个的字符。
    字节对象有以下特点:

    • bytes 对象可以从 str 对象使用给定的编码构建。
    • 各个元素是 range(256) 内的整数
    • bytes 对象的切片还是 bytes 对象,即使是只有一个字节的切片
    • bytearray 对象没有字面量句法,而是以 字节序列 字面量参数的形式显示
    • bytearray 对象的切片还是 bytearray 对象
    cafe = bytes('café', encoding='utf8')
    cafe
    
    b'cafxc3xa9'
    
    print(type(cafe[:2]))
    cafe[:2]
    
    
    <class 'bytes'>
    
    
    
    
    
    b'ca'
    
    cafe_arr = bytearray(cafe)
    cafe_arr
    
    
    bytearray(b'cafxc3xa9')
    
    cafe_arr[-1:]
    
    
    bytearray(b'xa9')
    

    结构体和内存视图

    struct 模块提供了一些函数,把打包的字节序列转换成不同类型字段 组成的元组,还有一些函数用于执行反向转换,把元组转换成打包的字 节序列。struct 模块能处理 bytes、bytearray 和 memoryview 对 象。

    4.3 基本的编解码器

    示例 4-5 使用 3 个编解码器编码字符串“El Niño”,得到的字节序 列差异很大

    for codec in ['latin_1', 'utf_8', 'utf_16']:
        print(codec, 'El Niño'.encode(codec), sep='	')
    
    latin_1	b'El Nixf1o'
    utf_8	b'El Nixc3xb1o'
    utf_16	b'xffxfeEx00lx00 x00Nx00ix00xf1x00ox00'
    

    4.4 了解编解码问题

    • UnicodeEncodeError(把字符串转换成二进制序列时)
    • 或 UnicodeDecodeError(把二进制序列转换成字符串时)

    4.4.1 处理UnicodeEncodeError

    方法

    • 使用encode的errors处理措施,可选忽略'ignore',替换replace(把无法编码的字符替换成 '?'), 或者使用xmlcharrefreplace('xmlcharrefreplace' 把无法编码的字符替换成 XML实体。)

    示例 4-6 编码成字节序列, 成功和错误处理:

    city = 'São Paulo'
    city.encode('utf-8')
    
    b'Sxc3xa3o Paulo'
    
    city.encode('cp437')
    
    
    ---------------------------------------------------------------------------
    
    UnicodeEncodeError                        Traceback (most recent call last)
    
    <ipython-input-14-31f4f9909b38> in <module>
    ----> 1 city.encode('cp437')
          2 
          3 
    
    
    D:Anaconda3libencodingscp437.py in encode(self, input, errors)
         10 
         11     def encode(self,input,errors='strict'):
    ---> 12         return codecs.charmap_encode(input,errors,encoding_map)
         13 
         14     def decode(self,input,errors='strict'):
    
    
    UnicodeEncodeError: 'charmap' codec can't encode character 'xe3' in position 1: character maps to <undefined>
    
    city.encode('cp437', errors='replace')
    
    
    b'S?o Paulo'
    
    city.encode('cp437', errors='xmlcharrefreplace')
    
    
    b'S&#227;o Paulo'
    

    4.4.2 处理UnicodeDecodeError

    不是每一个字节都包含有效的 ASCII 字符,也不是每一个字符序列都是 有效的 UTF-8 或 UTF-16。因此,把二进制序列转换成文本时,如果假 设是这两个编码中的一个,遇到无法转换的字节序列时会抛出 UnicodeDecodeError。

    另一方面,很多陈旧的 8 位编码——如 'cp1252'、'iso8859_1' 和 'koi8_r'——能解码任何字节序列流而不抛出错误,例如随机噪声。 因此,如果程序使用错误的 8 位编码,解码过程悄无声息,而得到的

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

    Python 3 默认使用 UTF-8 编码源码,Python 2(从 2.5 开始)则默认使用 ASCII。

    GNU/Linux 和 OS X 系统大都使用 UTF-8,因此打开在 Windows 系统中 使用 cp1252 编码的 .py 文件时可能发生这种情况。注意,这个错误在 Windows 版 Python 中也可能会发生,

    因为 Python 3 为所有平台设置的默 认编码都是 UTF-8。

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

    统一字符编码侦测包

    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

    4.4.5 BOM:有用的鬼符

    BOM,即字节序标记(byte-order mark),指明编码时使用 Intel CPU 的小字节序。

    UTF-16 有两个变种:UTF-16LE,显式指明使用小字节序(Intel x86 架构);

    UTF-16BE, 显式指明使用大字节序(ARM架构)。如果使用这两个变种,不会生成 BOM。

    4.5 处理文本文件(最佳实践:Unicode 三明治)

    Unicode 三明治

    • bytes->str,解码输入的字节序列
    • 100% str,只处理文本
    • str -> bytes, 编码输出的文本
    open('/competitions/013_fluent_python/file/cafe.txt', 'w+', encoding='utf_8').write('café')
    
    
    4
    
    open('E:\PycharmProjects\TianChiProject\00_山枫叶纷飞competitions\013_fluent_python\file\cafe.txt').read()
    
    
    'caf茅'
    
    open('E:\PycharmProjects\TianChiProject\00_山枫叶纷飞competitions\013_fluent_python\file\cafe.txt',  encoding='utf_8').read()
    
    
    'café'
    

    需要在多台设备中或多种场合下运行的代码,一定不能依赖 默认编码。打开文件时始终应该明确传入 encoding= 参数,因为 不同的设备使用的默认编码可能不同,有时隔一天也会发生变化。

    编码默认值:一团糟

    在 GNU/Linux 和 OS X 中,这些编码的默认值都是 UTF-8,而 且多年来都是如此,因此 I/O 能处理所有 Unicode 字符。

    在 Windows 中,不仅同一个系统中使用不同的编码,还有只支持 ASCII 和 127 个额外的字符的代码页(如 'cp850' 或 'cp1252'),而且不同的代码页之间增加的字符也有所不同。因 此,若不多加小心,Windows 用户更容易遇到编码问题。

    综上,locale.getpreferredencoding() 返回的编码是最重要的:这 是打开文件的默认编码,也是重定向到文件的 sys.stdout/stdin/stderr 的默认编码。

    目前win10默认编码为cp936':

    import locale
    locale.getpreferredencoding(do_setlocale=True)
    
    
    'cp936'
    

    关于编码默认值的最佳建议是:别依赖默认值。

    4.7 Unicode文本排序

    Python 比较任何类型的序列时,会一一比较序列里的各个元素。对字符 串来说,比较的是码位。可是在比较非 ASCII 字符时,得到的结果不尽 如人意。

    解决办法:

    • 在 Python 中,非 ASCII 文本的标准排序方式是使用 locale.strxfrm 函数,根据 locale 模块的文档 (https://docs.python.org/3/library/locale.html? highlight=strxfrm#locale.strxfrm),这 个函数会“把字符串转换成适合所 在区域进行比较的形式”。 使用 locale.strxfrm 函数之前,必须先为应用设定合适的区域设置, 还要祈祷操作系统支持这项设置。

    4.8 Unicode数据库

    Unicode 标准提供了一个完整的数据库(许多格式化的文本文件),不仅包括码位与字符名称之间的映射,还有各个字符的元数据,以及字符 之间的关系。

    例如,Unicode 数据库记录了字符是否可以打印、是不是 字母、是不是数字,或者是不是其他数值符号。字符串的 isidentifier、isprintable、isdecimal 和 isnumeric 等方法就 是靠这些信息作判断的。

    str.casefold 方法也用到了 Unicode 表中的 信息。

    示例 4-21 Unicode 数据库中数值字符的元数据示例(各个标号说 明输出中的各列):

    • ('U+%04x' % ord(char),U+0000 格式的码位
    • char.center(6),在长度为 6 的字符串中居中显示字符
    • char.isdigit() : 如果字符匹配正则表达式 r'd',显示 re_dig
    import unicodedata
    import re
    re_digit = re.compile(r'd')
    sample = '1xbcxb2u0969u136bu216bu2466u2480u3285'
    for char in sample:
        print('U+%04x' % ord(char),
              char.center(6),
              're_dig' if re_digit.match(char) else '-',
              'isdig' if char.isdigit() else '-',
              'isnum' if char.isnumeric() else '-',
              format(unicodedata.numeric(char), '5.2f'),
              unicodedata.name(char),
              sep='	')
    
    U+0031	  1   	re_dig	isdig	isnum	 1.00	DIGIT ONE
    U+00bc	  ¼   	-	-	isnum	 0.25	VULGAR FRACTION ONE QUARTER
    U+00b2	  ²   	-	isdig	isnum	 2.00	SUPERSCRIPT TWO
    U+0969	  ३   	re_dig	isdig	isnum	 3.00	DEVANAGARI DIGIT THREE
    U+136b	  ፫   	-	isdig	isnum	 3.00	ETHIOPIC DIGIT THREE
    U+216b	  Ⅻ   	-	-	isnum	12.00	ROMAN NUMERAL TWELVE
    U+2466	  ⑦   	-	isdig	isnum	 7.00	CIRCLED DIGIT SEVEN
    U+2480	  ⒀   	-	-	isnum	13.00	PARENTHESIZED NUMBER THIRTEEN
    U+3285	  ㊅   	-	-	isnum	 6.00	CIRCLED IDEOGRAPH SIX
    

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

    • 标准库中的一些函数能接受字符串或字节序列为参数,然后根据类型展 现不同的行为。

    re 和 os 模块中就有这样的函数。

    4.9.1 正则表达式(re)中的字符串和字节序列

    • 字节序列只能用字节序列正则表达式搜索。
    • 字符串模式 r'd+' 能匹配泰米尔数字和 ASCII 数字。
    • 字节序列模式 rb'd+' 只能匹配 ASCII 字节中的数字。
    • 字符串模式 r'w+' 能匹配字母、上标、泰米尔数字和 ASCII 数字。
    • 字节序列模式 rb'w+' 只能匹配 ASCII 字节中的字母和数字。

    示例:

    import re
    re_numbers_str = re.compile(r'd+')
    re_words_str = re.compile(r'w+')
    re_numbers_bytes = re.compile(rb'd+')
    re_words_bytes = re.compile(rb'w+')
    text_str = ("Ramanujan saw u0be7u0bedu0be8u0bef"
    " as 1729 = 1³ + 12³ = 9³ + 10³.")
    text_bytes = text_str.encode('utf_8')
    print('Text', repr(text_str), sep='
     ')
    print('Numbers')
    print(' str :', re_numbers_str.findall(text_str))
    print(' bytes:', re_numbers_bytes.findall(text_bytes))
    print('Words')
    print(' str :', re_words_str.findall(text_str))
    print(' bytes:', re_words_bytes.findall(text_bytes))
    
    
    Text
     'Ramanujan saw ௧௭௨௯ as 1729 = 1³ + 12³ = 9³ + 10³.'
    Numbers
     str : ['௧௭௨௯', '1729', '1', '12', '9', '10']
     bytes: [b'1729', b'1', b'12', b'9', b'10']
    Words
     str : ['Ramanujan', 'saw', '௧௭௨௯', 'as', '1729', '1³', '12³', '9³', '10³']
     bytes: [b'Ramanujan', b'saw', b'as', b'1729', b'1', b'12', b'9', b'10']
    

    4.9.2 os函数中的字符串和字节序列

    GNU/Linux 内核(UTF-8编码格式)不理解 Unicode(包括字符集、编码方案),因此你可能发现了,对任何合理的编 码方案来说,在文件名中使用字节序列都是无效的,无法解码成字符串。

    为了规避这个问题,os 模块中的所有函数、文件名或路径名参数既能 使用字符串,也能使用字节序列。

    如果这样的函数使用字符串参数调用,该参数会使用 sys.getfilesystemencoding() 得到的编解码器 自动编码,然后操作系统会使用相同的编解码器解码。这几乎就是我们 想要的行为,与 Unicode 三明治最佳实践一致。

    示例 4-23 把字符串和字节序列参数传给 listdir 函数得到的结 果

    import os
    os.listdir('.')
    
    
    ['.cache',
     'correlation.png',
     'FeatureBagging.png',
     'HBOS.png',
     'KNN.png',
     'LOF.png',
     'PCA.png']
    
    os.listdir(b'.')
    
    
    [b'.cache',
     b'correlation.png',
     b'FeatureBagging.png',
     b'HBOS.png',
     b'KNN.png',
     b'LOF.png',
     b'PCA.png']
    

    使用 surrogateescape 处理鬼符

    Python 3.1 引入的 surrogateescape 编解码器错误处理方式是处理 意外字节序列或未知编码的一种方式,它的说明参见“PEP 383 — Non-decodable Bytes in System Character Interfaces”(https://www.python.org/dev/peps/pep-0383/)。

    这种错误处理方式会把每个无法解码的字节替换成 Unicode 中 U+DC00 到 U+DCFF 之间的码位(Unicode 标准把这些码位称 为“Low Surrogate Area”),这些码位是保留的,没有分配字符,供 应用程序内部使用。编码时,这些码位会转换成被替换的字节值.

    你不逼自己一把,你永远都不知道自己有多优秀!只有经历了一些事,你才会懂得好好珍惜眼前的时光!
  • 相关阅读:
    HangFire快速入门
    HangFire概述
    Lodop错误汇总
    微信支付过程遇到的问题
    动态规划算法
    几句话~
    Welcom To My Blog
    【技巧】关于素数
    安徽省小学组省赛2014年第一题 木板面积(C++)
    洛谷 P1425 小鱼的游泳时间
  • 原文地址:https://www.cnblogs.com/zhazhaacmer/p/14382991.html
Copyright © 2011-2022 走看看