解码文件名问题

时间:2013-10-01 19:24:19

标签: python unicode utf-8 codec

为了简化我的问题,我创建了一个工作演示,它应该根据处理文件名的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提供的文件名。它有一个“latin-1”编码,尽管我的终端有LANG =“en_GB.UTF-8”。如果我使用严格的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实现的类中;除非有人有任何其他建议吗?

2 个答案:

答案 0 :(得分:1)

首先,写unicode(path)几乎总是一个坏主意。如果需要将字符串转换为Unicode,则需要知道它所在的字符集。

假设p表示来自文件系统的路径(例如,你是从os.listdir获得的),那么你想用文件系统的编码解码它,而不仅仅是Python认为是一个很好的默认值。 *所以,正确的做法就是你上面已经做过的事情:

p = p.decode(sys.getfilesystemencoding())

如果path表示其他内容(例如,您是通过用户输入获得的),那就是另一个故事。

或者,如果path是上面已经计算过的p值中的一个,那么已经 unicode,所以再次尝试解码它将它重新编码为默认编码然后重新解码,这是一件很蠢的事情。

但是如果不知道字符串来自哪里,你(和我们)就不知道它是什么字符集,因此你不知道如何解码它。


*在某些系统上,你会很幸运的。例如,使用Mac上的Python 3.x,默认编码和文件系统编码都将始终为UTF-8。但是对于旧的Linux机器上的Python 2.x,默认编码可能是UTF-8,而文件系统是Latin-1 ......这似乎正是你在这里得到的。

答案 1 :(得分:0)

字符串Greg. Descripci\xf3n v\xeddeos\n以latin-1或其他非Unicode和非UTF-8编码进行编码,因此您需要执行此操作:

"Greg. Descripci\xf3n v\xeddeos\n".decode('latin-1').encode(sys.getfilesystemencoding())

这会产生:

'Greg. Descripci\xc3\xb3n v\xc3\xaddeos\n'

问题是您自己的文件系统编码可能与用于通过Web提交文件或文件名的编码不匹配。您可能需要检查传入的编码以查看实际编码是什么。它可能是也可能不是latin-1:我只使用了latin-1,因为它是最常用的8位编码。

(根据您正在做的事情,您实际上可能不需要对其进行重新编码。)