【问题标题】:How to convert filename with invalid UTF-8 characters back to bytes?如何将带有无效 UTF-8 字符的文件名转换回字节?
【发布时间】:2015-03-14 13:24:07
【问题描述】:

如何将os.listdir 的输出转换为bytes 的列表(来自Unicode strs 的列表)?即使文件名是无效的UTF-8,它也必须工作,例如:

$ locale
LANG=
LANGUAGE=
LC_CTYPE=en_US.UTF-8
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
LC_COLLATE="POSIX"
LC_MONETARY="POSIX"
LC_MESSAGES="POSIX"
LC_PAPER="POSIX"
LC_NAME="POSIX"
LC_ADDRESS="POSIX"
LC_TELEPHONE="POSIX"
LC_MEASUREMENT="POSIX"
LC_IDENTIFICATION="POSIX"
LC_ALL=
$ python3
Python 3.4.0 (default, Apr 11 2014, 13:05:11) 
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> open(b'\x80', 'w')
<_io.TextIOWrapper name=b'\x80' mode='w' encoding='UTF-8'>
>>> os.listdir('.')
['\udc80']
>>> import sys
>>> [fn.encode(sys.getfilesystemencoding()) for fn in os.listdir('.')]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <listcomp>
UnicodeEncodeError: 'utf-8' codec can't encode character '\udc80' in position 0: surrogates not allowed
>>> [... for fn in os.listdir('.')]
[b'\x80']

那么我需要写信给上面的... 以使其工作吗?

请注意,在这种情况下,不能重命名文件、使用 Python 2.x 或仅使用 ASCII 文件名。我不是在寻找解决方法,而是在寻找代码来代替 ...s。

【问题讨论】:

  • 您是否尝试过首先获取字节:os.listdir(os.fsencode(os.curdir))
  • 或者只是os.listdir(b'.')...

标签: python python-3.x unicode utf-8 filesystems


【解决方案1】:

使用错误处理程序;在这种情况下,surrogateescape 错误处理程序看起来很合适:

值: 'surrogateescape'
含义: On decoding, replace byte with individual surrogate code ranging fromU+DC80toU+DCFF. This code will then be turned back into the same byte when the'surrogateescape'` 使用错误处理程序编码数据时。 (更多信息请参见PEP 383。)

os.fsencode() utility function 使用后一个选项;它在适用于您的操作系统时使用代理转义错误处理程序编码为sys.getfilesystemencoding()

使用'surrogateescape' 错误处理程序将文件名编码为文件系统编码,或者在Windows 上使用'strict';原样返回bytes

实际上,仅当文件系统编码为 mbcs 时,它才会使用 'strict',请参阅 os module source,这是一种仅在 Windows 上可用的编解码器。

演示:

>>> import sys
>>> ld = ['\udc80']
>>> [fn.encode(sys.getfilesystemencoding()) for fn in ld]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <listcomp>
UnicodeEncodeError: 'utf-8' codec can't encode character '\udc80' in position 0: surrogates not allowed
>>> [fn.encode(sys.getfilesystemencoding(), 'surrogateescape') for fn in ld]
[b'\x80']
>>> import os
>>> [os.fsencode(fn) for fn in ld]
[b'\x80']

【讨论】:

  • 谢谢,surrogateescape 在我的机器上确实有效(而surrogatepass 没有给出我期望的结果)。
  • os.listdir() 在内部使用os.fsdecode(),因此应该使用os.fsencode()。正如@pts 所说:基于代理的方法在这里会产生错误的结果。
  • @J.F.Sebastian:对,完全删除了surrogatepass 选项。
【解决方案2】:
>>> [os.fsencode(fn) for fn in os.listdir('.')]
[b'\x80']

还有一个对应的os.fsdecode,用于其他方向的转换。

这里的文档:https://docs.python.org/3/library/os.html#os.fsencode

【讨论】:

    【解决方案3】:

    如果您只想要来自os.listdir 的文件名(以字节为单位),它有该选项。来自docs

    path 可以是str 类型或bytes 类型。如果 pathbytes 类型,则返回的文件名也将是 bytes 类型;在所有其他情况下,它们将是str 类型。

    【讨论】:

      猜你喜欢
      • 2010-11-03
      • 2018-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-29
      相关资源
      最近更新 更多