在try-except块中使用open,打开的文件过多?

0 投票
2 回答
1723 浏览
提问于 2025-04-17 16:31

简单来说,我正在一个特定的位置遍历所有的子文件夹,并从三个不同的文件中收集一些数字。

def GrepData():
import glob as glob
import os as os

os.chdir('RUNS')
RUNSDir = os.getcwd()
Directories = glob.glob('*.*')
ObjVal = []
ParVal = []
AADVal = []

for dir in Directories:
    os.chdir(dir)
    (X,Y) = dir.split(sep='+')
    AADPath = glob.glob('Aad.out')
    ObjPath = glob.glob('fobj.out')
    ParPath = glob.glob('Par.out')

    try:
        with open(os.path.join(os.getcwd(),ObjPath[0])) as ObjFile:
            for line in ObjFile:
                ObjVal.append(list([X,Y,line.split()[0]]))
        ObjFile.close()
    except(IndexError):
        ObjFile.close()

    try:
        with open(os.path.join(os.getcwd(),ParPath[0])) as ParFile:
            for line in ParFile:
                ParVal.append(list([X,Y,line.split()[0]]))
        ParFile.close()
    except(IndexError):
        ParFile.close()
    try:
        with open(os.path.join(os.getcwd(),AADPath[0])) as AADFile:
            for line in AADFile:
                AADVal.append(list([X,Y,line.split()[0]]))
        AADFile.close()
    except(IndexError):
        AADFile.close()
    os.chdir(RUNSDir)

每次打开文件的命令都放在一个尝试 - 捕获的代码块里,因为有时候打开的文件可能是空的,这样在尝试使用line.split()时就会出现索引错误,因为列表是空的。

但是当我运行这个脚本时,我遇到了以下错误:“OSError: [Errno 24] 打开的文件太多了”。

我原以为“with open...”这个写法是可以在使用完文件后自动关闭它?显然这并没有发生。

所以我想问两件事:

  1. 我的理解“with open”是正确的吗?
  2. 我该如何修正导致这个问题的错误?

(是的,我知道代码看起来不太优雅。整个尝试 - 捕获的部分应该是一个可以重复使用的对象 - 但我会在弄清楚这个错误后再去修正它)

2 个回答

0

"打开的文件太多"这个问题和你写的Python代码是否语法正确没有关系,你使用with的方式也是对的。关键在于错误信息中的"OSError",这指的是底层的操作系统。

当你调用open()时,Python解释器会执行一个系统调用。这个系统调用的具体细节会根据你使用的操作系统有所不同,但在Linux上,这个调用是open(2)。操作系统的内核会处理这个系统调用。在文件打开的期间,它会在系统文件表中占有一个条目,并占用操作系统的资源——这意味着文件在打开时实际上是“占用空间”的。因此,操作系统对同时可以打开的文件数量是有限制的。

你的问题在于,当你调用open()时,没有足够快地调用close()。如果你的目录结构需要同时打开成千上万的文件,这可能会接近这个限制,这个限制在Linux上是可以临时调整的(不过我对其他操作系统不太熟悉,所以不想深入讨论跨平台的具体做法)。

1

试着把你的 try-except 放到 with 里面,像这样:

with open(os.path.join(os.getcwd(),ObjPath[0])) as ObjFile:
    for line in ObjFile:
        try:
            ObjVal.append(list([X,Y,line.split()[0]]))
        except(IndexError): 
           pass

注意:你不需要手动关闭文件,这就是 with 的作用。另外,如果你使用的是相同的名字,就不需要在导入时使用 as os

撰写回答