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

正则表达式中的字符串和字节序列

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

比较简单的字符串正则表达式和字节序列正则表达式的行为
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 \u0be7\u0bed\u0be8\u0bef"  #➌ 
            " as 1729 = 1³ + 12³ = 9³ + 10³.")        #➍ 
 
text_bytes = text_str.encode('utf_8')  #➎ 
 
print('Text', repr(text_str), sep='\n  ') 
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))    #➒

➊ 前两个正则表达式是字符串类型。
➋ 后两个正则表达式是字节序列类型。
➌ 要搜索的Unicode文本,包括1729的泰米尔数字(逻辑行直到右括号才结束)。
➍ 这个字符串在编译时与前一个拼接起来(参见Python语言参考手册中的“2.4.2. String
literal concatenation”,https://docs.python.org/3/reference/lexical_analysis.html#string-
literal-concatenation)。
➎ 字节序列只能用字节序列正则表达式搜索。
➏ 字符串模式r’\d+‘能匹配泰米尔数字和ASCII数字。
➐ 字节序列模式rb’\d+‘只能匹配ASCII字节中的数字。
➑ 字符串模式r’\w+‘能匹配字母、上标、泰米尔数字和ASCII数字。
➒ 字节序列模式rb’\w+'只能匹配ASCII字节中的字母和数字。

输出

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']

分析:可以使用正则表达式搜索字符串和字节序列,但是在后一种情况中,ASCII范围外的字节不会当成数字和组成单词的字母。

字符串正则表达式有个re.ASCII标志,它让\w、\W、\b、\B、\d、\D、\s和\S只匹配ASCII字符。

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

os模块中的所有函数、文件名或路径名参数既能使用字符串,也能使用字节序列

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

#把字符串和字节序列参数传给listdir函数得到的结果
>>> os.listdir('.')  # ➊ 
['abc.txt', 'digits-of-π.txt'] 
>>> os.listdir(b'.')  # ➋ 
[b'abc.txt', b'digits-of-\xcf\x80.txt']

➊ 第二个文件名是“digits-of-π.txt”(有一个希腊字母π)。
➋ 参数是字节序列,listdir函数返回的文件名也是字节序列:b’\xcf\x80’是希腊字母π的UTF-8编码。

为了便于手动处理字符串或字节序列形式的文件名或路径名,os模块提供了特殊的编码和解码函数。

  • fsencode(filename)

如果filename是str类型(此外还可能是bytes类型),使用sys.getfilesystemencoding()返回的编解码器把filename编码成字节序列;否则,返回未经修改的filename字节 序列。

  • fsdecode(filename)

如果filename是bytes类型(此外还可能是str类型),使用sys.getfilesystemen- coding()返回的编解码器把filename解码成字符串;否则,返回未经修改的filename字符串。

使用surrogateescape处理鬼符
Python 3.1引入的surrogateescape编解码器错误处理方式是处理意外字节序列或未知编码的一种方式。

这种错误处理方式会把每个无法解码的字节替换成Unicode中U+DC00到U+DCFF之间的码位。

#使用surrogateescape错误处理方式
>>> os.listdir('.')  #➊ 
['abc.txt', 'digits-of-π.txt'] 
>>> os.listdir(b'.')  #➋ 
[b'abc.txt', b'digits-of-\xcf\x80.txt'] 
>>> pi_name_bytes = os.listdir(b'.')[1]  #➌ 
>>> pi_name_str = pi_name_bytes.decode('ascii', 'surrogateescape')  #➍ 
>>> pi_name_str  #➎ 
'digits-of-\udccf\udc80.txt' 
>>> pi_name_str.encode('ascii', 'surrogateescape')  #➏ 
b'digits-of-\xcf\x80.txt'

➊ 列出目录里的文件,有个文件名中包含非ASCII字符。
➋ 假设我们不知道编码,获取文件名的字节序列形式。
➌ pi_names_bytes是包含π的文件名。
➍ 使用’ascii’编解码器和’surrogateescape’错误处理方式把它解码成字符串。
➎ 各个非ASCII字节替换成代替码位:’\xcf\x80’变成了’\udccf\udc80’。
➏ 编码成ASCII字节序列:各个代替码位还原成被替换的字节。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章