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

时间:2010-02-01 14:18:02

标签: python file-io

我有一个包含100k文本文件的文件夹。我想把超过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 :(得分:4)

您可以尝试使用返回迭代器的glob.iglob

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

此外,请不要将dir用于变量名称:您正在隐藏内置函数。

您可以介绍的另一项重大改进是filelen功能。如果用以下内容替换它,您将节省大量时间。相信我,what you have now is the slowest alternative

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

答案 1 :(得分:2)

几个想法。首先,您可以使用glob模块来获取较小的文件组。其次,按行计数排序将非常耗时,因为您必须打开每个文件并计算行数。如果可以按字节计数进行分区,则可以使用stat模块来避免打开文件。如果分割发生在20行是至关重要的,那么你至少可以通过计算出你的类型的20行文件所具有的最小字符数来减少大量文件,而不是打开任何小于该行的文件。 / p>

答案 2 :(得分:0)

import os,shutil
os.chdir("/mydir/")
numlines=20
destination = os.path.join("/destination","dir1")
for file in os.listdir("."):
    if os.path.isfile(file):
        flag=0
        for n,line in enumerate(open(file)):
            if n > numlines: 
                flag=1
                break
        if flag:
            try:
                shutil.move(file,destination) 
            except Exception,e: print e
            else:
                print "%s moved to %s" %(file,destination)

答案 3 :(得分:0)

如何使用shell脚本?你可以一次选择一个文件:

for f in `ls`;
loop
if `wc -l f`>20; then
  mv f newfolder
fi
end loop

如果我错了,请纠正

答案 4 :(得分:0)

目前接受的答案只是简单不起作用。这个功能:

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

有两个问题:首先,fname arg未使用且文件未打开。其次,对enumerate(line)的调用将失败,因为line未定义。

enumerate(line)更改为enumerate(open(fname))会解决问题。

答案 5 :(得分:0)

您可以使用os.scandir这是一个生成器,因此不会一次读取所有文件名(python 3.5附带,否则,或者只是读取:pip install scandir)。

示例:

    import os
    for file in os.scandir(path):
        do_something_with_file(path+file.name)

scandir文档:https://pypi.org/project/scandir/