【问题标题】:Trouble reading string with non-ascii characters in python 3在 python 3 中读取带有非 ascii 字符的字符串时遇到问题
【发布时间】:2021-04-01 19:46:58
【问题描述】:

我正在尝试从 WikiArt 数据集中读取图像。但是,我无法加载一些包含非 ascii 字符的图像: 例如: fã©lix-del-marle_nu-agenouill-sur-fond-bleu-1937.jpg' 尽管该文件存在于目录中。 我还比较了os.listdir() 的输出字符串名称和FileNotFoundError: No such file: '/wiki_art_paintings/rescaled_600px_max_side/Expressionism/fã©lix-del-marle_nu-agenouill-sur-fond-bleu-1937.jpg' 的输出字符串名称 通过做 'fã©lix-del-marle_nu-agenouill-sur-fond-bleu-1937.jpg' == 'fã©lix-del-marle_nu-agenouill-sur-fond-bleu-1937.jpg'。输出为 False

这里有什么问题?

【问题讨论】:

  • 请添加您的代码和正确的错误回溯输出
  • 您是否逐字符检查了哪些代码有字符?你可以编写脚本来做到这一点。也许您有两个看起来相同但代码不同的字符。或者可能有代码没有显示在屏幕上。
  • 当我逐个字符检查时,它会将ã 显示为两个字符a ̃ - 在 unicode 中是可能的
  • @furas,是的,我该如何解决?
  • 与规范化/非规范化形式相关的接缝。看看这个stackoverflow.com/questions/3126929/…

标签: python string non-ascii-characters python-unicode file-not-found


【解决方案1】:

问题是因为在Unicode 中,您可以使用单个字符或创建一些字符作为其他两个字符的组合,并且您在两个不同的地方都有这两种情况。在一个地方,您将一些字符作为单个字符(带有单个代码),而在另一个地方,您将字符作为两个其他字符的组合(带有两个代码)。当您将len() 用于两个字符串时,您甚至可以看到差异。在您的示例中,一个版本的长度为53,另一个版本的长度为52

您似乎可以使用unicodedata.normalize()NFCNFKCNFDNFKD 选项之一将一个名称转换为另一个名称。所以你必须测试哪一个适合你。

在一个方向上您可能需要NFCNFKC,在另一个方向上您可能需要NFDNFKD

您也可以使用unidecode 创建没有本地字符的文本:fa(c)lix-del-marle_nu-agenouill-sur-fond-bleu-1937.jpg,但这对您可能不是那么有用。

import unicodedata
from unidecode import unidecode

a = 'fã©lix-del-marle_nu-agenouill-sur-fond-bleu-1937.jpg'
b = 'fã©lix-del-marle_nu-agenouill-sur-fond-bleu-1937.jpg'

print('a:', a)
print('b:', b)

print('--- len ---')
print('len(a):', len(a))
print('len(b):', len(b))

print('--- encode ---')
print('a.encode:', a.encode('utf-8'))
print('b.encode:', b.encode('utf-8'))

print('--- a == normalize(b) ---')
print('NFC: ', a == unicodedata.normalize('NFC', b) )
print('NFKC:', a == unicodedata.normalize('NFKC', b) )
print('NFD: ', a == unicodedata.normalize('NFD', b) )
print('NFKD:', a == unicodedata.normalize('NFKD', b) )

print('--- b == normalize(a) ---')
print('NFC: ', b == unicodedata.normalize('NFC', a) )
print('NFKC:', b == unicodedata.normalize('NFKC', a) )
print('NFD: ', b == unicodedata.normalize('NFD', a) )
print('NFKD:', b == unicodedata.normalize('NFKD', a) )

print('--- unidecode ---')
print('a:', unidecode(a))
print('b:', unidecode(b))

结果:

a: fã©lix-del-marle_nu-agenouill-sur-fond-bleu-1937.jpg
b: fã©lix-del-marle_nu-agenouill-sur-fond-bleu-1937.jpg
--- len ---
len(a): 53
len(b): 52
--- encode ---
a.encode: b'fa\xcc\x83\xc2\xa9lix-del-marle_nu-agenouill-sur-fond-bleu-1937.jpg'
b.encode: b'f\xc3\xa3\xc2\xa9lix-del-marle_nu-agenouill-sur-fond-bleu-1937.jpg'
--- a == normalize(b) ---
NFC:  False
NFKC: False
NFD:  True
NFKD: True
--- b == normalize(a) ---
NFC:  True
NFKC: True
NFD:  False
NFKD: False
--- unidecode ---
a: fa(c)lix-del-marle_nu-agenouill-sur-fond-bleu-1937.jpg
b: fa(c)lix-del-marle_nu-agenouill-sur-fond-bleu-1937.jpg

只有当我必须将 MacOS 文件传输到其他系统时,我才会遇到其他两个字符的组合


文档:unicodedata

Pythonsheet:Unicode

堆栈溢出:Normalizing Unicode

【讨论】:

  • 谢谢你,@furas。简单地引入规范化帮助我解决了这个问题。
【解决方案2】:

这两个字符串不一样。看:

> ciao='fã©lix-del-marle_nu-agenouill-sur-fond-bleu-1937.jpg'.encode('utf-8')       
> bye='fã©lix-del-marle_nu-agenouill-sur-fond-bleu-1937.jpg'.encode('utf-8')        
> ciao.hex() 
 '6661cc83c2a96c69782d64656c2d6d61726c655f6e752d6167656e6f75696c6c2d7375722d666f6e642d626c65752d313933372e6a7067'
> bye.hex()  
 '66c3a3c2a96c69782d64656c2d6d61726c655f6e752d6167656e6f75696c6c2d7375722d666f6e642d626c65752d313933372e6a7067'
> ciao2='fa'.encode('utf-8')
> bye2='f'.encode('utf-8')
> ciao2.hex()
 '6661'
> bye2.hex() 
 '66'

'f' 周围似乎有一个隐藏字符。这似乎是一个'a'

【讨论】:

  • 很好的描述,但它是解决方案的地方,我在您的回答中看不到任何解决方案
  • 问题是你的文件名包含é。创建文件的进程用 UTF-8 写出名称,需要 2 个字节来表示 é。您的文件系统不理解 UTF-8,因此它显示 2 个字节,就好像它被编码为 latin-1。尝试在对open() 的调用中将 é 放在文件名中,而不是 ã©。
猜你喜欢
  • 1970-01-01
  • 2015-01-03
  • 1970-01-01
  • 2018-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-15
相关资源
最近更新 更多