将Python文件名转换为Unicode

16

我使用Windows系统的Python 2.6版本。我使用os.walk来读取文件树,文件名中可能有非7位字符(例如德语中的“ae”),这些字符被编码为Python内部字符串表示形式。我正在使用Python库函数处理这些文件名,但由于编码错误而失败了。

如何将这些文件名转换为正确的(Unicode?)Python字符串?我有一个名为"d:\utest\ü.txt"的文件,以Unicode格式传递路径无法工作:

>>> list(os.walk('d:\\utest'))
[('d:\\utest', [], ['\xfc.txt'])]
>>> list(os.walk(u'd:\\utest'))
[(u'd:\\utest', [], [u'\xfc.txt'])]

2
它确实有效:看看你的输出!!目录名u'd:\utest'和文件名u'\xfc.txt'都被表示为unicode对象u'blahblah',而不是之前的str对象'blahblah'。也许u带分音符的事实以\ xfc表示令你感到困惑,但这与str和unicode相同,与str / unicode问题无关。 - John Machin
也许你需要详细说明“由于错误编码而失败”...是什么失败了?怎么失败的?请展示完整的回溯和错误信息。 - John Machin
6个回答

47
如果你将一个Unicode字符串传递给os.walk(),你会得到Unicode结果:
>>> list(os.walk(r'C:\example'))          # Passing an ASCII string
[('C:\\example', [], ['file.txt'])]
>>> 
>>> list(os.walk(ur'C:\example'))        # Passing a Unicode string
[(u'C:\\example', [], [u'file.txt'])]

如果文件名无法解码,则在Python 2中可以获得字节串而不是Unicode。 - jfs

6
我正在寻找适用于Python 3.0+的解决方案。如果有其他人需要,我会在这里发布。
rootdir = r'D:\COUNTRY\ROADS\'
fs_enc = sys.getfilesystemencoding()
for (root, dirname, filename) in os.walk(rootdir.encode(fs_enc)):
    # do your stuff here, but remember that now
    # root, dirname, filename are represented as bytearrays

@ramdaz 1. 正如语法高亮所示:在闭合引号之前不应使用奇怪的斜杠。这是Python中的“SyntaxError”。2. 答案中的代码将bytes传递给os.walk(),因此文件名以字节形式生成。您应该改用Unicode(删除.encode()调用)。OP感到困惑。在Windows上传递Unicode是正确的做法。 - jfs
似乎在Python 3.5中停止工作:TypeError: os.scandir()在Windows上不支持字节路径,请改用Unicode - koppor

4
os.walk(unicode(root_dir, 'utf-8'))

4

一种更为直接的方法是尝试以下步骤:找到您文件系统的编码,然后将其转换为Unicode。例如,

unicode_name = unicode(filename, "utf-8", errors="ignore")

要走另一条路,

unicode_name.encode("utf-8")

2
os.walk没有规定必须始终使用os.listdir,但也没有列出如何处理Unicode的方式。然而os.listdir中确实有以下说明:

从2.3版本起更改:在Windows NT/2k/XP和Unix中,如果路径是Unicode对象,则结果将是Unicode对象列表。无法解码的文件名仍将作为字符串对象返回。

对于您是否简单地使用Unicode参数,这样做可以起到作用吗?

for dirpath, dirnames, filenames in os.walk(u"."):
  print dirpath
  for fn in filenames:
    print "   ", fn

1

不,它们没有被编码在Python内部的字符串表示中,这样的东西是不存在的。它们是根据操作系统/文件系统的编码进行编码的。但是,在os.walk中传递unicode是可以工作的。

我不知道当文件名无法解码时os.walk的行为如何,但我认为你会得到一个字符串,就像os.listdir()一样。在这种情况下,您以后还会遇到问题。此外,并非所有Python 2.x标准库都能正确接受Unicode参数,因此您可能仍然需要将它们编码为字符串。因此,问题实际上可能出现在其他地方,但如果是这种情况,您会注意到的。;-)

如果您需要更多对解码的控制,您不能总是传递一个字符串,然后像往常一样解码它:

filename = filename.decode()

1
哦,抱歉我比其他答案更详细,并提出了解决方案可能存在的问题。 - Lennart Regebro
1
+1 对于做出合理的假设但仍明确说明它是一个假设。如果我可以的话,另外+1 是为了增加讨论的价值。 - RichieHindle
Python 3.1发布公告(在我写这篇文章的两天前发布)中提到:“使用Unicode字符串的文件系统API现在可以处理其中包含无法解码字节的路径。”我不知道这是否能解决这个潜在问题,也不知道如何解决,但任何关心此问题的人都应该查看Python 3.1。 - RichieHindle
好的,在3.x中你总是会得到unicode,所以只要切换到3.x就可能解决这个问题。但是当然,目前还没有很多适用于3.x的第三方模块,其中最值得一提的是Setuptools。 - Lennart Regebro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接