过滤非常大的文件夹中的文件

6 投票
6 回答
3139 浏览
提问于 2025-04-15 18:44

我有一个文件夹里面有10万个文本文件。我想把那些超过20行的文件放到另一个文件夹里。请问我该怎么用Python来实现这个?我试过用os.listdir,但是显然连文件名都装不下,内存不够。有没有办法一次获取大约100个文件名呢?

这是我的代码:

import os
import shutil

dir = '/somedir/'

def file_len(fname):
    f = open(fname,'r')
    for i, l in enumerate(f):
        pass
    f.close()
    return i + 1

filenames = os.listdir(dir+'labels/')

i = 0
for filename in filenames:
    flen = file_len(dir+'labels/'+filename)
    print flen
    if flen > 15:
        i = i+1
        shutil.copyfile(dir+'originals/'+filename[:-5], dir+'filteredOrigs/'+filename[:-5])
print i

输出结果是:

Traceback (most recent call last):
  File "filterimage.py", line 13, in <module>
    filenames = os.listdir(dir+'labels/')
OSError: [Errno 12] Cannot allocate memory: '/somedir/'

这是我修改过的脚本:

import os
import shutil
import glob

topdir = '/somedir'

def filelen(fname, many):
    f = open(fname,'r')
    for i, l in enumerate(f):
        if i > many:
            f.close()
            return True
    f.close()
    return False

path = os.path.join(topdir, 'labels', '*')
i=0
for filename in glob.iglob(path):
    print filename
    if filelen(filename,5):
        i += 1
print i

在文件较少的文件夹里能正常工作,但在大文件夹里,它只打印“0”...在Linux服务器上能运行,但在Mac上却打印“0”...唉...

6 个回答

0

目前被接受的答案根本不管用。这个函数:

def many_line(fname, many=15):
    for i, line in enumerate(line):
        if i > many:
            return True
    return False

有两个问题:首先,fname这个参数没有被使用,而且文件也没有被打开。其次,调用enumerate(line)会出错,因为line没有定义。

enumerate(line)改成enumerate(open(fname))就能解决这个问题。

2

有几点想法。首先,你可以使用 glob 模块来获取一小部分文件。其次,按照行数排序会非常耗时,因为你需要打开每个文件并数行。如果你能按字节数来分组,就可以通过使用 stat 模块来避免打开文件。如果必须在20行处进行分割,你至少可以通过计算一个20行文件的最小字符数来排除大量文件,而不去打开那些小于这个字符数的文件。

4

你可以试试使用glob.iglob,这个方法会返回一个迭代器:

topdir = os.path.join('/somedir', 'labels', '*')
for filename in glob.iglob(topdir):
     if filelen(filename) > 15:
          #do stuff

另外,请不要把dir当作变量名使用,这样会覆盖掉Python自带的功能。

还有一个很重要的改进可以应用到你的filelen函数上。如果你用下面的代码替换掉它,你会节省很多时间。相信我,你现在的写法是最慢的选择

def many_line(fname, many=15):
    for i, line in enumerate(open(fname)):
        if i > many:
            return True
    return False

撰写回答