【问题标题】:Handling backslash escapes in filenames read from a file处理从文件读取的文件名中的反斜杠转义
【发布时间】:2019-02-13 21:41:45
【问题描述】:

TL;DR:文本文件包含表示反斜杠转义的字符串;如何将它们用作os.stat() 的输入?

我有一个输入文件input.txt:

./with\backspace
./with\nnewline

用简单的循环处理它们不起作用:

>>> import os
>>> with open('input.txt') as f:
...     for line in f:
...         os.stat(line.strip())
... 
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
FileNotFoundError: [Errno 2] No such file or directory: './with\\backspace'

按照another question 中的建议使用.decode("unicode_escape") 只能部分工作 - 文件中的第一行失败,\n 的第二行没有。


旁注:输入文件名有./,我知道我可以使用os.listdir('.') 并遍历文件直到找到正确的文件。那不是我的目标。目标是处理文件中包含反斜杠转义的文件名。


附加测试:

>>> import os
>>> with open('./input.txt') as f:
...     for l in f:
...         os.stat(l.strip().decode('unicode_escape'))
... 
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
AttributeError: 'str' object has no attribute 'decode'
>>> with open('./input.txt') as f:
...     for l in f:
...         try:
...             os.stat(l.strip().encode('utf-8').decode('unicode_escape'))
...             print(l.strip())
...         except:
...             pass
... 
os.stat_result(st_mode=33188, st_ino=1053469, st_dev=2049, st_nlink=1, st_uid=1000, st_gid=1000, st_size=0, st_atime=1536468565, st_mtime=1536468565, st_ctime=1536468565)
./with\nnewline

使用os.fsencode() 编写显式字符串有效:

>>> os.stat(os.fsencode('with\x08ackspace'))
os.stat_result(st_mode=33188, st_ino=1053465, st_dev=2049, st_nlink=1, st_uid=1000, st_gid=1000, st_size=0, st_atime=1536468565, st_mtime=1536468565, st_ctime=1536468565)

但是,由于同一命令有多种变体,我仍然无法从文件中读取字符串,以便 os.stat() 接受它。

>>> with open('./input.txt') as f:
...      for l in f:
...          os.stat(os.fsdecode( bytes(l.strip(),'utf-8').decode('unicode_escape').encode('latin1') ) )
... 
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
FileNotFoundError: [Errno 2] No such file or directory: './with\x08ackslash'

【问题讨论】:

  • 请澄清这两个样本的正确结果转换应该是什么。
  • @wallyk 我不确定您所说的“校正结果转换”是什么意思,所以请澄清一下。目标是将每个字符串作为有效路径名传递给os.stat()。正如您从两个示例中看到的那样,将字符串作为从文件中读取的内容传递并使用.decode() 生成一个os.stat() 无法解释的字符串。具有此类文件名的文件存在于我的文件系统中,我需要一种 Pythonic 方式来访问它们。这有帮助吗?
  • 那么\b 是退格还是目录分隔符后跟b
  • @StephenRauch 我在 Linux 上工作,所以它不是目录分隔符。该文件使用touch$'\b'ackspace' 创建。所以文件名中的\b 是实际的反斜杠字节。在input.txt 这是一个字符串。正如我在问题中提到的,我查看了一个相关问题,该问题建议在字符串上使用 .decode('unicode_escape'),但这不起作用。我将编辑一个答案以在一秒钟内添加更多输出
  • 我想说在文件名中加入退格是一个非常糟糕的主意。

标签: python filenames text-processing


【解决方案1】:

在 macOS 中工作:

touch $'with\backspace'
touch $'with\newline'
echo $'./with\\backspace\n./with\\newline' > input.txt
python
>>> import os
>>> with open('./input.txt') as f:
...     for l in f:
...         os.stat(l.strip().decode('unicode_escape'))
posix.stat_result(st_mode=33188, st_ino=8604304962, st_dev=16777220, st_nlink=1, st_uid=501, st_gid=20, st_size=0, st_atime=1536469815, st_mtime=1536469815, st_ctime=1536469815)
posix.stat_result(st_mode=33188, st_ino=8604305024, st_dev=16777220, st_nlink=1, st_uid=501, st_gid=20, st_size=0, st_atime=1536470112, st_mtime=1536470112, st_ctime=1536470112)

在 Darwin Kernel Version 17.7.0 上使用 Python 2.7.14。

【讨论】:

  • 用 Python 2.7.15 进行了同样的测试。 OSError: [Errno 2] No such file or directory: './with\x08ackslash'
  • 没关系。我意识到输入文件不正确。确实有效。
【解决方案2】:

经过大约 2 小时的检查后,我意识到输入文件包含 ./with\backslash,而实际文件名是通过 touch with$'\b'ackspace 创建的。因此Health Raftery's answer 有效,但仅适用于 Python 2。在 Python 3 中,您会得到 AttributeError: 'str' object has no attribute 'decode',因为字符串 in Python 3 are already a unicode strings

在此过程中,我可能通过os.fsencode() 找到了更好的方法 在jfs's answer中引用。

import os

with open('./input.txt') as f:
    for l in f:
        # alternatively one can use 
        # bytes(l.strip(),sys.getdefaultencoding())
        bytes_filename =  bytes(l.strip(), 'utf-8').decode('unicode_escape')
        f_stat = os.stat(os.fsdecode( bytes_filename ) )
        print(l.strip(),f_stat)

由于我主要使用 Python 3,这就是我一直在寻找的。但是,Health Raftery's answer 仍然有效,因此 +1。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-01
    • 2014-09-20
    • 2012-01-17
    • 2011-12-31
    • 2019-06-23
    • 2023-04-08
    • 1970-01-01
    相关资源
    最近更新 更多