Python open()在不同操作系统下对Unicode文件名的处理不同

5

文件名类似于:

filename = u"/direc/tories/español.jpg"

使用open()函数如下:

fp = open(filename, "rb")

这将正确打开在OSX(10.7)上的文件,但是在Ubuntu 11.04上,open()函数将尝试打开u"espa\xf1ol.jpg",并且这会导致IOError。在试图修复此问题的过程中,我已经在两个系统上检查了sys.getfilesystemencoding(),两者都设置为utf-8(虽然Ubuntu报告大写字母,即UTF-8,不确定是否相关)。我还在python文件中设置了# -*- coding: utf-8 -*-,但我确定这仅影响文件内部的编码,而不影响任何外部功能或python如何处理系统资源。该文件在两个系统上都存在,并正确显示了eñe。
最终的问题是:如何在Ubuntu系统上打开español.jpg文件?
编辑: 实际上,español.jpg字符串是通过Django的ORM(ImageFileField)从数据库中获取的,但到我处理它并看到行为差异的时候,我有一个单一的unicode字符串,其中包含了文件的绝对路径。

1
就此而言,"\xf1""ñ"的ISO-8859-1表示。 - Sven Marnach
3
顺便说一下,我触摸了一个名为 español.jpg 的文件,并使用您的方法打开它,在我的 Arch Linux 上正常工作。你能粘贴一下你的 Ubuntu 的 locale 输出吗? - Felix Yan
locale 给出了许多参数,包括 LANG,其值为 en_US.UTF-8 - mrmagooey
1
这不是 "\xF1" 字节串,而是 u"\xF1" Unicode 字符串。它等于 u"ñ",因此源的编码是正确的。在 Ubuntu 中,这对我来说很好用。我想有可能是文件系统没有正确的编码(例如,如果它是使用错误选项挂载的 NTFS 分区)。你遇到了什么 IOError 错误?如果你使用 os.listdir('/direc/tories')(字节串),你会得到什么结果? - bobince
IOError报错了,说我要查找的文件名不存在。如果我运行os.listdir('/direc/tories'),我会得到文件列表作为字符串对象,其中Unicode字符以两位十六进制格式打印(即\xf1)。它应该将其加载为Unicode对象吗? - mrmagooey
2个回答

2

以下内容可以在两种情况下正常工作:

fp = open(filename.encode(sys.getfilesystemencoding()), "rb")

2
这在Windows上不起作用,因为getfilesystemencoding实际上并没有报告文件系统编码(UTF-16)。如果您想要跨平台,请不要这么做。 - Philipp
1
@FelixYan:它无法处理文件名包含 ANSI 代码页之外字符的情况。 - dan04
3
dan04是正确的。在Windows下访问文件名的唯一可靠方式是使用Unicode字符串,因为Python直接支持将它们发送到Win32 API。 (大多数其他语言使用C标准库函数,这些函数使用ANSI API,因此无法处理字符不符合当前区域设置的ANSI代码页的文件名。) - bobince
1
在Windows上的C(++)程序中,您必须使用“Unicode”(即UTF-16)版本的所有内容,例如int wmain(int, wchar_t**)而不是int main(int, char**)以及_wfopen而不是fopen。另一方面,*nix则相反,很好地支持UTF-8但不支持UTF-16。这使得编写跨平台代码非常麻烦。 - dan04
1
然而,在Python中,您可以在任何地方使用Unicode字符串。在Windows上,它们将按原样传递给操作系统API,在*nix上,它们将转换为区域设置的编码。 - dan04
显示剩余5条评论

1

仅仅在文件的顶部设置文件编码是不够的。请确保您的编辑器使用相同的编码,并以该编码保存文本。如有必要,请重新输入任何非 ASCII 字符,以确保您的编辑器正在正确处理。

如果您的值来自数据库等,您仍需要确保整个过程中没有被编码为非 Unicode。


抱歉,应该从一开始就添加编辑信息,我认为这可能会消除编辑器编码问题...? - mrmagooey
@mrmagooey:是的,但请注意,您仍然需要确保在整个过程中没有以不同的方式对字符串进行编码。 - Marcin

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