Фильтровать файлы в очень большой папке - PullRequest
6 голосов
/ 01 февраля 2010

У меня есть папка с текстовыми файлами 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-сервере, печатает 0 на Mac ... да ладно ...

Ответы [ 6 ]

4 голосов
/ 01 февраля 2010

вы можете попробовать использовать glob.iglob, который возвращает итератор:

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

Также, пожалуйста, не используйте dir для имени переменной: вы скрываете встроенное.

Еще одно важное улучшение, которое вы можете внести, - ваша функция filelen. Если вы замените его следующим, вы сэкономите много времени. Поверьте мне, то, что у вас есть сейчас, является самой медленной альтернативой :

def many_line(fname, many=15):
    for i, line in enumerate(open(fname)):
        if i > many:
            return True
    return False
2 голосов
/ 01 февраля 2010

Пара мыслей. Во-первых, вы можете использовать модуль glob для получения небольших групп файлов. Во-вторых, сортировка по количеству строк будет очень трудоемкой, так как вы должны открывать каждый файл и считать строки. Если вы можете разделить по количеству байтов, вы можете избежать открытия файлов с помощью модуля stat. Если важно, чтобы разделение происходило в 20 строк, вы можете, по крайней мере, вырезать большие массивы файлов, определив минимальное количество символов, которое будет иметь файл из 20 строк вашего типа, и не открывая файл меньшего размера.

0 голосов
/ 30 октября 2018

Вы можете использовать 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/

0 голосов
/ 02 февраля 2010

В настоящее время принятый ответ просто не работает. Эта функция:

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)) исправит это.

0 голосов
/ 01 февраля 2010

как насчет использования сценария оболочки? Вы можете выбрать один файл за раз:

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

Пожалуйста, исправьте, если я ошибаюсь

0 голосов
/ 01 февраля 2010
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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...