内置函数"open"用于编码和解码文件名的机制是什么?



我对open有点困惑。我正在运行 Windows 10,当我调用sys.getfilesystemencoding时,我会得到mbcs,因此如果我将文件名传递给open例如:

open('Meow!.txt')

据推测,源文件的编码是 utf-8。open是否使用设置为默认 Windows ANSI 代码页的mbcs编码对文件名'Meow!.txt'进行编码?然后将请求传递给操作系统?

  • 一般来说,当您在 2.X 中将文件名作为 unicode 传递给open并在 3.X 中将str传递给时会发生什么?

  • 当文件名在 3.X 中作为bytes对象传递或在 2.X 中作为str对象传递时,是否覆盖文件名的默认自动编码?

准确地说,这是在 2.7 中使用内置open时内部发生的情况:

Python 设置了一个常量,用于命名文件名的默认编码,此常量称为Py_FileSystemDefaultEncoding,并且因平台而异。最终,当其值设置为 Null 时,Python 将尝试获取平台的默认编码(如果有):

/*bltinmodule.c*/
/* The default encoding used by the platform file system APIs
Can remain NULL for all platforms that don't have such a concept
*/
#if defined(MS_WINDOWS) && defined(HAVE_USABLE_WCHAR_T)
const char *Py_FileSystemDefaultEncoding = "mbcs";
#elif defined(__APPLE__)
const char *Py_FileSystemDefaultEncoding = "utf-8";
#else
const char *Py_FileSystemDefaultEncoding = NULL; /* use default */
#endif

Py_FileSystemDefaultEncoding使用"mbcs"(多字节字符集)Windows编码,则可以使用sys.getfilesystemencoding()调用来检查Py_FileSystemDefaultEncoding的值:

Python 2.7 文档:sys.getfilesystemencoding()

在 Windows NT+ 上,文件名是本机的 Unicode,因此不执行转换。getfilesystemencoding()仍然返回"mbcs",因为这是应用程序在明确希望将 Unicode 字符串转换为用作文件名时等效的字节字符串时应使用的编码。

例如,让我们假设当一个带有中文字符的文件名时,为简单起见,我将使用 U+5F08 中国象棋 CJK 作为我要写的文件名:

>>> f = open(u'u5F08.txt', 'w')
>>> f
<open file u'u5f08', mode 'w' at 0x000000000336B1E0>
  • 一般来说,当您在 2.X 中将文件名作为 unicode 传递给open并在 3.X 中将str传递给时会发生什么?

此答案取决于平台。例如,在Windows中,不需要将Unicode字符串转换为任何编码,即使使用默认的文件系统编码"mbcs",也不需要证明:

>>> f = open(u'u5F08.txt'.encode('mbcs'), 'w')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IOError: [Errno 22] invalid mode ('w') or filename: '?.txt'

顺便说一下,即使您使用"utf-8"编码,也不会获得正确的文件名:

>>> f = open(u'u5F08.txt'.encode('utf8'), 'w')

如果你在Windows上检查它而不是弈.txt,这将给你å1/4ˆ.txt文件名。总之,显然没有Unicode文件名的转换。我认为这条规则也适用于str。由于 2.X 中的str是一个原始字节字符串,Python 不会神奇地选择编码 **但是我无法验证这一点,Python 可能会使用 "mbcs" 编码解码str。可以通过使用"mbcs"代码页字符集之外的字符来验证我相信,但这同样取决于您的 Windows 区域设置。在Windows实现中,很多东西都封装在较低级别。如果没记错的话,我认为"mbcs"现在被认为是Windows API的遗留问题。 Python 3.6 改用 UTF-8,除非启用了传统模式。

实际上,问题似乎在于Windows API及其实现,而不是Python本身的实现。

最新更新