处理从文件读取的文件名中的反斜杠转义

时间:2018-09-09 03:33:45

标签: python filenames text-processing

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'

2 个答案:

答案 0 :(得分: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)

那是达尔文内核版本17.7.0上的Python 2.7.14。

答案 1 :(得分:0)

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

在此过程中,我可能通过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。