记录我学习《流畅的python》的过程
2021.1.18
1.处理文本文件
依赖默认编码来处理文本文件会遇到一些问题。
t = open('cafe.txt', 'w', encoding='utf_8').write('café')
print(t)
f = open('cafe.txt').read()
print(f)
运行结果:
问题是:写入文件时指定了utf-8编码,但是读取文件时没有这么做,使用的是系统默认的编码,所以会出现这种问题。
解决的办法:
fp = open('cafe.txt', 'w', encoding='utf_8')
print(fp)
print(fp.write('café'))
print(fp.close())
import os
print(os.stat('cafe.txt').st_size)
fp2 = open('cafe.txt')
print(fp2)
print(fp2.encoding)
print(fp2.read())
fp3 = open('cafe.txt', encoding='utf_8')#使用正确的编码打开文件
print(fp3)
print(fp3.read())
fp4 = open('cafe.txt', 'rb')#'rb'标志指明在二进制模式下读取文件
print(fp4)
print(fp4.read)
运行结果:
2.编码默认值
探索编码默认值
import sys,locale
expersions = """
locale.getpreferredencoding()
type(my_file)
my_file.encoding
sys.stdout.isatty()
sys.stdout.encoding
sys.stdin.isatty()
sys.stdin.encoding
sys.stderr.isatty()
sys.stderr.encoding
sys.getdefaultencoding()
sys.getfilesystemencoding()
"""
my_file = open('dummy', 'w')
for expersion in expersions.split():
value = eval(expersion)
print(expersion.rjust(30), '->', repr(value))
运行结果:
关于编码默认值的最佳建议是:别依赖默认值。
2.正确比较,规范化Unicode字符串
用两种方式表示café,位数不一样,结果却一样。
s1 = 'café'
s2 = 'cafe\u0301'
print(s1, s2)
print(len(s1), len(s2))
print(s1 == s2)
运行结果:
s1和s2这两种序列成为标准等价物,应用程序把他们视作相同的字符,但是在python中码位不同,判定二者不同。
解决方案:使用unicodedata.normalize函数提供的Unicode规范化。
-
NFC,使用最少的码位构成等价的字符串
-
NFD,把组合字符分解成基字符和单独的组合字符
from unicodedata import normalize
s1 = 'café' # 把“e”和重音符组合在一起
s2 = 'cafe\u0301' # 分解成“e”和重音符
print(len(s1), len(s2))
print(len(normalize('NFC', s1)), len(normalize('NFC', s2)))
print(len(normalize('NFD', s1)), len(normalize('NFD', s2)))
print(normalize('NFC', s1) == normalize('NFC', s2))
print(normalize('NFD', s1) == normalize('NFD', s2))
运行结果:
使用NFC,有些单字符会被规范化成另一个单字符。
from unicodedata import normalize,name
ohm = '\u2126'
print(name(ohm))
ohm_c = normalize('NFC', ohm)
print(name(ohm_c))
print(ohm == ohm_c)
print(normalize('NFC', ohm) == normalize('NFC', ohm_c))
运行结果:
- NFKC,K表示兼容性
from unicodedata import normalize,name
half = '½'
print(normalize('NFKC', half))
four_squared = '4²'
print(normalize('NFKC', four_squared))
micro = 'μ'
micro_kc = normalize('NFKC', micro)
print(micro, micro_kc)
print(ord(micro), ord(micro_kc))
print(name(micro), name(micro_kc))
问题是输入法所输入的μ应该是不同,书上的结果两个μ不同。
微子符μ和小写希腊字母μ
3.大小写折叠
大小写折叠就是把所有文本变成小写,在做些其他转换。由str.casefold()实现
对于只包含latin1字符的字符串,它和lower一样,除了μ。
from unicodedata import name
micro = 'μ'
print(name(micro))
micro_cf = micro.casefold()
print(name(micro_cf))
print(micro, micro_cf)
eszett = 'β'
print(name(eszett))
eszett_cf = eszett.casefold()
print(name(eszett_cf))
运行结果:
这里的例子显示的都一样,可以选用一些其他的微字符来表示。
4.比较规范化Unicode字符串
rom unicodedata import normalize
def nfc_equal(str1, str2):
return normalize('NFC', str1) == normalize('NFC', str2)
def fold_equal(str1, str2):
return (normalize('NFC', str1).casefold() ==
normalize('NFC', str2).casefold())
s1 = 'café'
s2 = 'cafe\u0301'
print(s1 == s2)
print(nfc_equal(s1, s2))
print(nfc_equal('a', 'A'))
s3 = 'Straße' # ß为德语的ss
s4 = 'strasse'
print('\n')
print(s3 == s4)
print(nfc_equal(s3, s4))
print(fold_equal(s3, s4))
print(fold_equal(s1, s2))
print(fold_equal('A', 'a'))
运行结果:
5.极端规范化
去掉字符串中的所有变音符号
import unicodedata
import string
def shave_marks(txt):
"""去掉全部变音符号"""
norm_txt = unicodedata.normalize('NFD', txt) # 把所有字符分解成基字符和组合符号
shaved = ''.join(c for c in norm_txt
if not unicodedata.combining(c)) # 过滤所有组合记号
return unicodedata.normalize('NFC', shaved) # 重组
order = '"Herr Voß: · ½ cup of tker caffòǒèīèě"'
print(shave_marks(order))
深入规范化:
import unicodedata
import string
def shave_marks_latin(txt):
"""把拉丁基字符中的所有的变音符号删除"""
norm_txt = unicodedata.normalize('NFD', txt) # 把所有字符分解成基字符和组合符号
latin_base = False
keepers = []
for c in norm_txt:
if unicodedata.combining(c) and latin_base:
continue # 忽略拉丁基字符上的变音符号
keepers.append(c)
# 如果不是组合字符,那就是新的基字符
if not unicodedata.combining(c):
latin_base = c in string.ascii_letters
shaved = ''.join(keepers)
return unicodedata.normalize('NFC', shaved) # 重组
order = '"Herr Voß: · ½ cup of tker caffòǒèīèě"'
print(shave_marks_latin(order))
这里面没有变音的非希腊字母,可以自行示范。
6.Unicode文本排序
初始排序:
fruits = ['caju', 'atemoia', 'cajá', 'açai', 'acerola']
sorted_fruits = sorted(fruits)
print(sorted_fruits)
优化后:
import locale
print(locale.setlocale(locale.LC_COLLATE, 'pt_BR.UTF-8'))
fruits = ['caju', 'atemoia', 'cajá', 'açai', 'acerola']
sorted_fruits = sorted(fruits, key=locale.strxfrm)
print(sorted_fruits)
使用Unicode排序算法排序:
import pyuca
coll = pyuca.Collator()
fruits = ['caju', 'atemoia', 'cajá', 'açai', 'acerola']
sorted_fruits = sorted(fruits, key=coll.sort_key)
print(sorted_fruits)
7.Unicode数据库
import unicodedata
import re
re_digit = re.compile(r'\d')
sample = '1\xbc\xb2\u0969\u216b\u2466\u2480\u3285'
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='\t')
8.支持字符串和字节序列的双模式API
可以使用正则表达式搜索字符串和字节序列,但是在后一种情况中,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 \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_numbers_bytes.findall(text_bytes))