Unicode字符的规范化
Unicode 三明治
关于处理文本文件这里有一个很有意思和形象的比喻:“Unicode 三明治”。可以想象成三部曲吧: - 第一步:要尽早把输入的字节序列解码输入的字节序列 - 然后:只处理文本 - 最后:要尽量晚地把字符串编码成字节序列 具体如下图:
python3中的操作函数: - 内置的open函数在读取文件的时候做解码 - read 和 write中的方法都市处理字符串
但是这里有一个默认编码问题:
如下面的实例所示,不同的平台使用的默认编码格式不一样,到时无法正确读取文件. 需要在多台设备中或多种场合下运行的代码,一定不能依赖默认编码。打开文件时始终应该明确传入 encoding= 参数
# 一个平台上的编码问题
# 写入的时候指定为utf_8
fo = open('cafe.txt','w',encoding='utf_8').write('café')
print(fo)
# 读取的时候使用默认的编码读取,window用的是cp936的格式
strfile = open('cafe.txt').read()
print(strfile)
4
caf茅
# 关于上例中的详解
# 返回一个 TextIOWrapper 对象
fp = open('cafe.txt','w',encoding='utf_8')
print(fp)
# 写入
fp.write('café')
fp.close()
# os.stat 报告文件中有 5 个字节
import os
print(os.stat('cafe.txt').st_size)
# windows 默认的编码格式是encoding='cp936'
fp2 = open('cafe.txt')
print(fp2)
# 读取的是一个中文字符
print(fp2.read())
fp2.close()
# 这里指定的编码格式
fp3 = open('cafe.txt',encoding='utf_8')
print(fp3)
print(fp3.read())
fp3.close()
# 使用二进制格式读取
fp4 = open('cafe.txt','rb')
# 返回的是 BufferedReader 对象
print(fp4)
print(fp4.read())
5
caf茅
café
b'caf\xc3\xa9'
规范化Unicode字符串的函数
规范化Unicode字符串是为了正确的比较字符串而出现的。例如下面的例子: - café 与 cafe\u0301 - 分别的码位长度是4和5,但是结果是完成一样 - Python 看到的是不同的码位序列,因此判定二者不相等 - 应用程序应该把它们视作相同的字符
这里就需要:使用unicodedata.normalize进行规范化. 函数的第一个参数是的设置有四个选择: - NFC(Normalization Form C)使用最少的码位构成等价的字符串 - NFD 把组合字符分解成基字符和单独的组合字符 - NFKC 较严格的规范化形式,对“兼容字符”有影响 - NFKD 较严格的规范化形式,对“兼容字符”有影响
保存文本之前,最好使用 normalize('NFC', user_text) 清洗字符串.推荐使用NFC进行比较.
s1 = 'café'
s2 = 'cafe\u0301'
print("s1:",s1,"\ns2:", s2)
print("s1 len:",len(s1),"\ns2 len:", len(s2))
s1: café
s2: café
s1 len: 4
s2 len: 5
#normalize NFC 与NFD
from unicodedata import normalize
s1 = 'café'
s2 = 'cafe\u0301'
print("s1 len:",len(s1),"\ns2 len:", len(s2))
print("s1 NFC len:",len(normalize('NFC',s1)),
"\ns2 NFC len:", len(normalize('NFC',s2)))
print("s1 NFD len:",len(normalize('NFD',s1)),
"\ns2 NFD len:", len(normalize('NFD',s2)))
print(normalize('NFC',s1) == normalize('NFC',s2))
print(normalize('NFD',s1) == normalize('NFD',s2))
print(normalize('NFC',s1) == normalize('NFD',s2))
s1 len: 4
s2 len: 5
s1 NFC len: 4
s2 NFC len: 4
s1 NFD len: 5
s2 NFD len: 5
True
True
False
Unicode 大小写折叠
大小写折叠其实就是把所有文本变成小写,再做些其他转换。 - str.casefold()
只包含 latin1 字符的字符串 s,s.casefold() 得到的结果与 s.lower() 一样.str.casefold() 和 str.lower() 得到不同结果的有 116 个码位.
#Unicode 大小写折叠
from unicodedata import normalize, name
micro = 'µ'
print(name(micro))
micro_cf = micro.casefold()
print(name(micro_cf))
print(micro,micro_cf)
MICRO SIGN
GREEK SMALL LETTER MU
µ μ
Unicode 文本匹配函数
多语言文本处理时需要使用如下函数: - nfc_equal - fold_equal
from 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(fold_equal('a','A'))
print(nfc_equal('a','A'))
False
True
True
False
极端“规范化”:去掉变音符号
在什么时候需要去掉变音符号: - 搜索涉及很多技术 - 去掉变音符号还能让 URL 更易于阅读
如何实现去掉变音符号:如下代码
#去掉全部组合记号的函数
import unicodedata
import string
def shave_marks(txt):
#把所有字符分解成基字符和组合记号
norm_txt = unicodedata.normalize('NFD',txt)
unicodedata.combining('a')
#过滤掉所有组合记号
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è latte • bowl of açaí.”'
print(shave_marks(order))
“Herr Voß: • ½ cup of Œtker™ caffe latte • bowl of acai.”
# 删除拉丁字母中组合记号的函数
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è latte • bowl of açaí.”'
print(shave_marks_latin(order))
“Herr Voß: • ½ cup of Œtker™ caffe latte • bowl of acai.”
关于Unicode字符规范化小结: - NFC 和 NFD 可以放心使用,而且能合理比较 Unicode 字符串 - 对大多数应用来说,NFC 是最好的规范化形式 - 不区分大小写的比较应该使用 str.casefold() - 文本匹配实用函数:要利用normalize,casefold - 去掉变音符号:需要注意不能把它们变成 ASCII 字符
请大家关注公众号:瓦力人工智能
分享关于人工智能,机器学习,深度学习以及计算机视觉的好文章,同时自己对于这个领域学习心得笔记。想要一起深入学习人工智能的小伙伴一起结伴学习吧!