支持字符串和字節序列的雙模式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字節序列:各個代替碼位還原成被替換的字節。

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