【发布时间】:2013-10-08 02:06:36
【问题描述】:
为了简化我的问题,我创建了一个工作演示,它应该根据处理文件名的 python unicode 文档工作。输出如下:
$ ./test_unicode.py /tmp/gsynctest/Greg.*
p = '/tmp/gsynctest/Greg. Descripci\xf3n v\xeddeos'
up = u'/tmp/gsynctest/Greg. Descripci\xf3n v\xeddeos'
up.utf8 = /tmp/gsynctest/Greg. Descripción vídeos
Command line file exists = True
Unicode file exists = False
UTF-8 file exists = False
如您所见,按照出现的顺序,p 是通过 argv 和 glob 提供的文件名。尽管我的终端具有 LANG="en_GB.UTF-8",但它具有“latin-1”编码。如果我使用严格的 unicode 错误集对其进行解码,我会得到up 显示的字符串。如果我将其编码为 utf8,我会得到代表真实文件名的内容。
但是,根据 unicode 文档,应使用 sys.getfilesystemencoding() 对 unicode 文件名进行编码以便访问它。但这不起作用。三个 exists 检查显示哪一个有效,它似乎是 latin-1 (ISO-8859-1) 编码。
我不知道为什么我看到的内容不反映文档。
这里是测试程序代码:
#!/usr/bin/env python
import sys, os
paths = sys.argv[1:]
fsenc = sys.getfilesystemencoding()
for p in paths:
print "p = %s" % repr(p)
if not isinstance(p, unicode):
up = unicode(p, encoding = "latin-1", errors = "strict")
print "up = %s" % repr(up)
print "up.utf8 = %s" % up.encode("utf8")
print "Command line file exists = %s" % os.path.exists(p)
print "Unicode file exists = %s" % os.path.exists(up)
print "%s file exists = %s" % (fsenc, os.path.exists(up.encode(fsenc)))
。 . .
原问题:
如果我尝试以原始形式解码以下文件名表示,我会收到“无效的连续字节”错误:Greg. Descripci\xf3n v\xeddeos\n
for p in paths:
p = p.decode(sys.getfilesystemencoding())
这是提交此错误的用户提交的真实文件名。我对 unicode / UTF-8 编码的理解不是很好,但据我所知,它不是合法的 UTF-8,因为它需要某种终结符。我并不关心打印时文件名的外观,它只需要在磁盘上可以访问。处理这样的文件的常规方法是什么?我的大部分问题都源于尝试打印文件:
debug(u"Filename: %s" % unicode(path))
更新:尝试、更努力、更努力仍然有什么好处吗?
for e in (sys.getfilesystemencoding(), "UTF-8", "Latin-1"):
try:
p_dec = p.decode("Latin-1")
p = p_dec.encode(sys.getfilesystemencoding())
except UnicodeDecodeError:
pass
对于文件系统编码相同的编码,显然不是最佳选择,因为它将以相同的编码进行解码和编码。但至少我可以保证在后续调用中解码文件名不会出现异常。我看到的唯一问题是,不正确的编码可能会正确解码文件名,从而产生完全错误的编码文件名。
无论哪种方式,我都需要跟踪两个文件名吗?磁盘上可访问的原始文件名和可打印文件名?还是文件系统编码的文件名既可打印又可访问?
更新 2:我的问题的答案是“否”。我实现了自己的编解码器来循环编码类型并在文件系统编码中重新编码。该表示现在可打印:Greg. Descripción vídeos,但该文件不再可访问。所以我认为保持文件系统访问和可打印性的最简单方法是将文件名包装在一个具有打印和 IO 实现的类中;除非有人有任何其他建议?
【问题讨论】:
-
UTF-8 不会“期待某种终结符”。但是
i之后的\xf3不是任何有效的UTF-8 字符串的一部分。这几乎可以肯定是 Latin-1 或相关的东西,比如 cp1252。 -
另外,从您的描述中不清楚哪一行正在获取此字符串并引发此异常。是
p.decode(sys.getfilesystemencoding()),还是unicode(path)? -
什么操作系统?在 Windows 上,使用 unicode 字符串作为文件名,以避免 C stdio 函数的代码页限制字节文件名处理。
-
Linux。看来
p毕竟是可打印的。p在另一个函数中发生了一些事情,该函数通过将其包装在unicode()中来对其进行转换。这是一个简单的解决方法。然后另一个异常是由于从文件路径构造的正则表达式在调试语句中不可打印的结果。我通过包装repr()语句解决了这个问题。我得到了不可打印的字符,但我并不太担心这些,我只是希望它不会引发异常。 -
最后,如果从 Latin-1 解码,然后编码为 UTF-8 以进行打印会产生可读的输出,这意味着您的终端是 UTF-8,不是 Latin-1 .这意味着只执行
ls /tmp/gsynctest/Greg*应该显示mojibake,因为您拥有的以拉丁语1 命名的文件名作为UTF-8 是无意义的。但是,一些 UTF-8 解码器比 Python 更宽松,并尝试将任何无效字节视为 Latin-1,因此您的测试用例实际上可能看起来仍然有效,这使得调试变得更加困难。
标签: python unicode utf-8 codec