如何在Python中打开文件列表

12
我正在读取数据文件(文本文件),并生成多个报告,每个报告都被写入不同的输出文件(也是文本文件)。我用了很长的方式打开它们: fP = open('file1','w') invP = open('inventory','w') orderP = open('orders','w') ... 等等,最后需要对应的close() 方法关闭。
如果我能够使用 for 循环打开它们,并使用一个 fP 名称和文件名列表,我可以保证关闭相同的文件。
我尝试使用 fp:filename的字典,但这显然行不通,因为 fP 变量未定义,或者字符串“fP”不是一个好的文件对象名称。
由于这些是输出文件,我可能不需要检查打开错误——如果我无法打开一个或多个文件,我无论如何都无法继续。
是否有任何方法可以从名称列表中打开一组文件(不超过10个),并在循环中处理它们?
6个回答

19

好消息!Python 3.3引入了一种标准的安全方法来实现这一点:

contextlib.ExitStack

根据文档:

每个实例维护一组已注册的回调函数堆栈,当实例关闭时以相反的顺序调用它们。
(...)
由于已注册的回调会按照注册的相反顺序调用,这就像是使用多个嵌套的with语句一样,并使用已注册的一组回调。

以下是一个如何使用它的示例:

from contextlib import ExitStack

with ExitStack() as stack:
    files = [
        stack.enter_context(open(filename))
        for filename in filenames
    ]
    # ... use files ...
当代码退出 with 语句时,已经打开的所有文件都将被关闭。这样你也知道,如果有2个文件被打开,然后第三个文件无法打开,那么已经打开的两个文件将被正确关闭。同时,如果在 with 块内部任何时候引发异常,你将看到正确的清除操作。

Python 令我惊叹不已。 - Joseph Hansen

10

是的,你可以使用列表推导式:

filenames = ['file1.txt', 'file2.txt', 'file3.txt'...]
filedata = {filename: open(filename, 'w') for filename in filenames}

现在,所有已打开的实例都被保存在filedata中,并分配给文件名。

要关闭它们:

for file in filedata.values():
    file.close()

我有正确的想法:一个字典,但是我没想到文件指针对象可以是filedata[filename]这种形式——它并不像filepointer一样是一个符号。 - ZZMike
2
这种方法也需要在所有内容周围加上 try/finally,以确保如果出现异常或提前退出的情况,您可以关闭打开的文件。 - Kos

5
使用 with 关键字 可以保证已打开的文件(以及其他类似的资源,称为“上下文管理器”)被关闭:
with open(file_path, 'w') as output_file:
    output_file.write('whatever')

退出with块时,即使发生异常,文件也将被正确关闭。

您可以轻松地遍历路径列表以获得所需的文件:

files = ['fp1.txt', 'inventory', 'orders']
for file in files:
    with open(file, 'w') as current_file:
        current_file.do_some_stuff()

5

由于您说有许多数据文件,所以不必手动输入文件名到列表中。您可以使用以下方法将文件名获取到列表中:

from os import listdir
from os.path import isfile, join
files_in_dir = [ f for f in listdir('/home/cam/Desktop') if isfile(join('/home/cam/Desktop',f)) ]

现在您可以:
for file in files_in_dir:
    with open(file, 'w') as f:
        f.do_something

3
您可以打开任意数量的文件并将它们保存在列表中以便稍后关闭:
fds = [open(path, 'w') for path in paths]

# ... do stuff with files

# close files
for fd in fds:
    fd.close()

或者您可以使用词典来提高可读性:

# map each file name to a file descriptor
files = {path: open(path, 'w') for path in paths}

# use file descriptors through the mapping
files['inventory'].write("Something")

# close files
for path in files:
    files[path].close()

1

如果您事先知道或定义了要创建的文件列表,则上面的两个答案都很好。但是,如果您想要更通用的解决方案,可以即时构建该列表,使用操作系统在磁盘上创建空文件(这取决于您使用的操作系统的不同方式),然后以交互方式创建文件列表:

import os
working_folder = input("Enter full path for the working folder/directory: ")
os.chdir(working_folder)
filenames_list = os.listdir()

#you can filter too, if you need so:
#filenames_list = [filename for filename in os.listdir() if '.txt' in filename]

#then you can do as Reut Sharabani and A.J. suggest above and make a list of file descriptors
file_descriptors = [open(filename, 'w') for filename in filenames_list]
#or a dictionary as Reut Sharabani suggests (I liked that one Reut :)

#do whatever you need to do with all those files already opened for writing

#then close the files
for fd in file_descriptors:
   fd.close()

如果你只是从头到尾处理一个文件,一些人建议使用“with”是可以的;但是如果你想同时处理所有文件,则最好使用文件描述符的列表或字典。


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