Python:比较带重音符的字符串无效
我刚开始学习Python,想要从一个列表中删除出现在另一个列表中的文件。这两个列表是通过在Mac和Windows上运行ll -R命令生成的(不过在此之前经过了一些处理,比如合并、排序等,使用了其他Python脚本)。
有些文件名中带有重音符号和特殊符号。虽然这些字符串看起来是一样的(在包含这些列表的文件中打印出来也一样),但实际上它们被认为是不相等的。
我找到了一些关于如何比较带有特殊字符的字符串的讨论:Python字符串比较——特殊/Unicode字符的问题。这和我的问题很相似。我还阅读了一些关于编码的内容,了解如何改变字符串的编码。
不过,我尝试了在编码文档中找到的所有编码方式:https://docs.python.org/2/library/codecs.html。对于所有可能的编码对,这两个字符串仍然不相等(见下面的程序——尝试了解码和编码的选项)。
当我逐个查看这两个字符串中的字符时,发现带重音的字母在一个文件中显示为一个字符(带重音的e),而在另一个文件中却显示为两个字符(e和一个可打印的空格)。
如果有任何想法,我会非常感激。
我把这两个文本文件缩减到每个文件只有一行一个单词(显然是带重音的)。我把文本文件上传到了Dropbox:testfilesindata 和 testmissingfiles(不过我还没有尝试从Dropbox下载一个新的副本)。
非常感谢!
PS. 对于链接的混乱表示抱歉。我没有10的声望……
#!/usr/bin/python3
import sys
codecs = [ 'ascii', 'big5', 'big5hkscs', 'cp037', 'cp424', 'cp437', 'cp500', 'cp720 ', 'cp737 ', 'cp775', 'cp850', 'cp852', 'cp855', 'cp856 ', 'cp857', 'cp858', 'cp860', 'cp861', 'cp862', 'cp863', 'cp864', 'cp865', 'cp866', 'cp869', 'cp874 ', 'cp875 ', 'cp932', 'cp949', 'cp950', 'cp1006 ', 'cp1026', 'cp1140', 'cp1250', 'cp1251', 'cp1252', 'cp1253', 'cp1254', 'cp1255', 'cp1256', 'cp1257', 'cp1258', 'euc_jp', 'euc_jis_2004', 'euc_jisx0213', 'euc_kr', 'gb2312', 'gbk', 'gb18030', 'hz', 'iso2022_jp', 'iso2022_jp_1', 'iso2022_jp_2', 'iso2022_jp_2004', 'iso2022_jp_3', 'iso2022_jp_ext', 'iso2022_kr', 'latin_1', 'iso8859_2', 'iso8859_3', 'iso8859_4', 'iso8859_5', 'iso8859_6', 'iso8859_7', 'iso8859_8', 'iso8859_9', 'iso8859_10', 'iso8859_13', 'iso8859_14', 'iso8859_15', 'iso8859_16', 'johab', 'koi8_r ', 'koi8_u ', 'mac_cyrillic', 'mac_greek', 'mac_iceland', 'mac_latin2', 'mac_roman', 'mac_turkish', 'ptcp154', 'shift_jis', 'shift_jis_2004', 'shift_jisx0213', 'utf_32', 'utf_32_be', 'utf_32_le', 'utf_16', 'utf_16_be', 'utf_16_le', 'utf_7', 'utf_8', 'utf_8_sig' ]
file1 = open('testmissingfiles','r')
file2 = open('testfilesindata','r')
list1 = file1.readlines()
list2 = file2.readlines()
word1 = list1[0].rstrip('\n')
word2 = list2[0].rstrip('\n')
for i in range(0,len(codecs)-1):
for j in range(0,len(codecs)-1):
try:
encoded1 = word1.decode(codecs[i])
encoded2 = word2.decode(codecs[j])
if encoded1 == encoded2:
sys.stdout.write('Succeeded with ' + codecs[i] + ' & ' + codecs[j] + '\n')
except:
pass
2 个回答
你的程序有几个问题:
首先,你的程序会在每次循环中产生一个AttributeError错误,导致程序无法正常运行。因为word1
和word2
都没有叫.decode()
的方法。在Python3中,你可以把字符串编码成一串字节,或者把一串字节解码成字符串。
其次,使用codecs
这个库其实是个误导。你的两个输入文件都是用UTF-8编码的。当你从文件中读取字节时,这些字节会被成功解码。
再者,虽然你的字符串看起来很相似,但它们实际上由不同的unicode编码点组成。具体来说,“Adhésion”包含了两个unicode编码点0065和0301,分别代表“拉丁小写字母E”和“组合重音符号”。而第二个词“Adhésion”则包含了一个编码点00E9,代表“带重音的拉丁小写字母E”。正如Daniel在他的回答中提到的,你可以先对这两个不同的字符串进行标准化,来检查它们是否在意义上是相等的。
以下是我解决你问题的方法:
#!/usr/bin/python3
import sys
import unicodedata
with open('testmissingfiles', 'r') as fp:
list1 = [line.strip() for line in fp]
with open('testfilesindata','r') as fp:
list2 = [line.strip() for line in fp]
word1 = list1[0]
word2 = list2[0]
if word1 == word2:
print("%s and %s are identical"%(word1, word2))
elif unicodedata.normalize('NFC', word1) == unicodedata.normalize('NFC', word2):
print("%s and %s look the same, but use different code poitns"%(word1, word2))
else:
print("%s and %s are unrelated"%(word1, word2))
使用 unicodedata.normalize
可以把字符串转换成相同的标准形式,这样就能确保它们看起来一样了:
import unicodedata
encoded1 = unicodedata.normalize('NFC', word1.decode('utf8'))
encoded2 = unicodedata.normalize('NFC', word2.decode('utf8'))