问题描述
对于我正在从事的Web抓取项目,我计划将实体存储在数据库中,这些实体的ID是其名称/标题的md5哈希值。
但是,由于字符串中存在Unicode,因此会出现相同名称/标题的不同哈希值
例如,“运动学,物理Ed \ xa0和娱乐”的md5哈希与“运动学,物理Ed和娱乐”的md5哈希不同。
我尝试使用Unicode归一化,但哈希值之间的差异仍然相同
import hashlib
import unicodedata
def generate_id(*args):
"""
:param args: strings to be used to generate an id
:return: md5 hash of the passed arguments
"""
string = ''
for arg in args:
string += ' ' + arg
hash_algorithm = hashlib.md5()
hash_algorithm.update(string.encode('utf-8'))
return hash_algorithm.hexdigest()
def clean_text(text):
"""
normalizes the unicode in a text to be more readable and generate a more accurate id from
:param text: string to be normalized
:return: normalized version of text
"""
return unicodedata.normalize('NFC',text)
print(generate_id(clean_text('Kinesiology,Phys Ed\xa0and Recreation'))) # hashes to acd21f3b094a77d1a2393a8daeac42d9
print(generate_id('Kinesiology,Phys Ed and Recreation')) # hashes to 5ac6bc3ca3d743d99e9b93a7a5379fe9
我该怎么做,以确保两个字符串都相同并且散列到相同的id,以使“ Kinesiology,Phys Ed \ xa0和Recreation”与“ Kinesiology,Phys Ed和Recreation”具有相同的字符串和相同的哈希值(不管是否存在unicode,任何2个字符串都一样)?
解决方法
由于“具有相同的散列”只是二进制相等性的代理,因此您需要将字符串标准化为相同。
在Unicode术语中,两个给定的字符串不是规范上等效的,但它们是兼容的。因此,您将能够使用NFKD
函数中的兼容性分解/合成范式(NFKC
或clean_text()
)来生成相同的哈希值:
def clean_text(text):
return unicodedata.normalize('NFKD',text)
NO-BREAK SPACE (U+00A0)
字符的分解属性设置为<noBreak> SPACE (U+0020)
。分解属性中存在一个关键字(在这种情况下为<noBreak>
),表明该字符与常规空格字符兼容,但不规范对等。
旁注
由于评论中有此要求,因此需要对NFKC和NFKD范式之间的区别进行一些澄清:
Unicode字符可以由多个代码点组成。某些字符可以以不同(但在规范上等效)的方式表示:作为单个代码点或作为代码点的组合。例如:é
可以表示为é
或e + ◌́
。规范化时,合成范式(NFC,NFKC)将尝试将序列转换为其组成形式(e + ◌́
→é
);分解范式(NFD,NFKD)将尝试将组合的字符转换为序列(é
→e + ◌́
)。您使用哪一个完全取决于情况。只需确保不要将苹果与橘子进行比较。