c++编码规范_Python进阶14_Unicode字符的规范化

32014e7bf2ef91e6716724800e50f9cc.png

Unicode字符的规范化

Unicode 三明治

关于处理文本文件这里有一个很有意思和形象的比喻:“Unicode 三明治”。可以想象成三部曲吧: - 第一步:要尽早把输入的字节序列解码输入的字节序列 - 然后:只处理文本 - 最后:要尽量晚地把字符串编码成字节序列 具体如下图:

63cd2e9cac82ed765c08d34257d89900.png

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())
<_io.TextIOWrapper name='cafe.txt' mode='w' encoding='utf_8'>
5
<_io.TextIOWrapper name='cafe.txt' mode='r' encoding='cp936'>
caf茅
<_io.TextIOWrapper name='cafe.txt' mode='r' encoding='utf_8'>
café
<_io.BufferedReader name='cafe.txt'>
b'cafxc3xa9'

规范化Unicode字符串的函数

规范化Unicode字符串是为了正确的比较字符串而出现的。例如下面的例子: - cafécafeu0301 - 分别的码位长度是4和5,但是结果是完成一样 - Python 看到的是不同的码位序列,因此判定二者不相等 - 应用程序应该把它们视作相同的字符

这里就需要:使用unicodedata.normalize进行规范化. 函数的第一个参数是的设置有四个选择: - NFC(Normalization Form C)使用最少的码位构成等价的字符串 - NFD 把组合字符分解成基字符和单独的组合字符 - NFKC 较严格的规范化形式,对“兼容字符”有影响 - NFKD 较严格的规范化形式,对“兼容字符”有影响

保存文本之前,最好使用 normalize('NFC', user_text) 清洗字符串.推荐使用NFC进行比较.

s1 = 'café' 
s2 = 'cafeu0301'
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 = 'cafeu0301'
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 = 'cafeu0301'

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 字符


请大家关注公众号:瓦力人工智能

分享关于人工智能,机器学习,深度学习以及计算机视觉的好文章,同时自己对于这个领域学习心得笔记。想要一起深入学习人工智能的小伙伴一起结伴学习吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值