Дешевый способ поиска большого текстового файла для строки - PullRequest
29 голосов
/ 08 октября 2010

Мне нужно найти довольно большой текстовый файл для конкретной строки.Это журнал сборки с около 5000 строк текста.Каков наилучший способ сделать это?Использование регулярных выражений не должно вызывать проблем, не так ли?Я прочитаю блоки строк и воспользуюсь простым поиском.

Ответы [ 8 ]

46 голосов
/ 09 октября 2010

Если это «довольно большой» файл, то обращайтесь к строкам последовательно и не читайте весь файл в память:

with open('largeFile', 'r') as inF:
    for line in inF:
        if 'myString' in line:
            # do_something
16 голосов
/ 08 октября 2010

Вы можете сделать простую находку:

f = open('file.txt', 'r')
lines = f.read()
answer = lines.find('string')

Простая находка будет немного быстрее, чем регулярное выражение, если вам это сойдет с рук.

12 голосов
/ 08 февраля 2011

Следующая функция работает для текстовых файлов и двоичных файлов (хотя возвращает только позицию в подсчете байтов), она имеет преимущество в нахождении строк, даже если они перекрывают строку или буфер и не будетобнаруживается при поиске по строке или в буфере.

def fnd(fname, s, start=0):
    with open(fname, 'rb') as f:
        fsize = os.path.getsize(fname)
        bsize = 4096
        buffer = None
        if start > 0:
            f.seek(start)
        overlap = len(s) - 1
        while True:
            if (f.tell() >= overlap and f.tell() < fsize):
                f.seek(f.tell() - overlap)
            buffer = f.read(bsize)
            if buffer:
                pos = buffer.find(s)
                if pos >= 0:
                    return f.tell() - (len(buffer) - pos)
            else:
                return -1

Идея этого заключается в следующем:

  • поиск начальной позиции в файле
  • чтение из файлав буфер (строки поиска должны быть меньше размера буфера), но, если не в начале, отбросьте - 1 байт, чтобы перехватить строку, если она начинается в конце последнего буфера чтения и продолжается в следующем.
  • возврат позиции или -1, если не найден

Я использовал что-то вроде этого, чтобы найти подписи файлов внутри больших файлов ISO9660, что было довольно быстрым и не занимало много памяти, вытакже можно использовать больший буфер для ускорения.

5 голосов
/ 07 августа 2011

Я попытался собрать многопроцессорный пример поиска текстового файла. Это моя первая попытка использования многопроцессорного модуля; и я питон n00b. Комментарии вполне приветствуются. Мне придется подождать, пока на работе, чтобы проверить действительно большие файлы. Это должно быть быстрее в многоядерных системах, чем одноядерный поиск. Bleagh! Как остановить процессы после того, как текст найден и надежно сообщить номер строки?

import multiprocessing, os, time
NUMBER_OF_PROCESSES = multiprocessing.cpu_count()

def FindText( host, file_name, text):
    file_size = os.stat(file_name ).st_size 
    m1 = open(file_name, "r")

    #work out file size to divide up to farm out line counting

    chunk = (file_size / NUMBER_OF_PROCESSES ) + 1
    lines = 0
    line_found_at = -1

    seekStart = chunk * (host)
    seekEnd = chunk * (host+1)
    if seekEnd > file_size:
        seekEnd = file_size

    if host > 0:
        m1.seek( seekStart )
        m1.readline()

    line = m1.readline()

    while len(line) > 0:
        lines += 1
        if text in line:
            #found the line
            line_found_at = lines
            break
        if m1.tell() > seekEnd or len(line) == 0:
            break
        line = m1.readline()
    m1.close()
    return host,lines,line_found_at

# Function run by worker processes
def worker(input, output):
    for host,file_name,text in iter(input.get, 'STOP'):
        output.put(FindText( host,file_name,text ))

def main(file_name,text):
    t_start = time.time()
    # Create queues
    task_queue = multiprocessing.Queue()
    done_queue = multiprocessing.Queue()
    #submit file to open and text to find
    print 'Starting', NUMBER_OF_PROCESSES, 'searching workers'
    for h in range( NUMBER_OF_PROCESSES ):
        t = (h,file_name,text)
        task_queue.put(t)

    #Start worker processes
    for _i in range(NUMBER_OF_PROCESSES):
        multiprocessing.Process(target=worker, args=(task_queue, done_queue)).start()

    # Get and print results

    results = {}
    for _i in range(NUMBER_OF_PROCESSES):
        host,lines,line_found = done_queue.get()
        results[host] = (lines,line_found)

    # Tell child processes to stop
    for _i in range(NUMBER_OF_PROCESSES):
        task_queue.put('STOP')
#        print "Stopping Process #%s" % i

    total_lines = 0
    for h in range(NUMBER_OF_PROCESSES):
        if results[h][1] > -1:
            print text, 'Found at', total_lines + results[h][1], 'in', time.time() - t_start, 'seconds'
            break
        total_lines += results[h][0]

if __name__ == "__main__":
    main( file_name = 'testFile.txt', text = 'IPI1520' )
2 голосов
/ 09 октября 2010

Если нет способа определить, где будет находиться строка (первая половина, вторая половина и т. Д.), То на самом деле не существует оптимизированного способа поиска, кроме встроенной функции «поиск». Вы можете уменьшить время ввода-вывода и потребление памяти, не читая файл целиком за один снимок, а используя блоки по 4 КБ (обычно это размер блока жесткого диска). Это не сделает поиск быстрее, если строка не находится в первой части файла, но в любом случае уменьшит потребление памяти, что может быть хорошей идеей, если файл огромен.

0 голосов
/ 12 сентября 2018

Мне нравится решение Хавьера.Я не пробовал, но это звучит круто!

Для чтения произвольного большого текста и желания узнать, существует ли строка, замените ее, вы можете использовать Flashtext , что быстреечем Regex с очень большими файлами.

Редактировать:

Со страницы разработчика:

>>> from flashtext import KeywordProcessor
>>> keyword_processor = KeywordProcessor()
>>> # keyword_processor.add_keyword(<unclean name>, <standardised name>)
>>> keyword_processor.add_keyword('Big Apple', 'New York')
>>> keyword_processor.add_keyword('Bay Area')
>>> keywords_found = keyword_processor.extract_keywords('I love Big Apple and Bay Area.')
>>> keywords_found
>>> # ['New York', 'Bay Area']

Или при извлечении смещения:

>>> from flashtext import KeywordProcessor
>>> keyword_processor = KeywordProcessor()
>>> keyword_processor.add_keyword('Big Apple', 'New York')
>>> keyword_processor.add_keyword('Bay Area')
>>> keywords_found = keyword_processor.extract_keywords('I love big Apple and Bay Area.', span_info=True)
>>> keywords_found
>>> # [('New York', 7, 16), ('Bay Area', 21, 29)]
0 голосов
/ 01 марта 2018

Я удивлен, что никто не упомянул отображение файла в память: mmap

При этом вы можете получить доступ к файлу, как если бы он уже был загружен в память, и ОС позаботится о том, чтобы отобразить его как можно скорее. Кроме того, если вы сделаете это из 2 независимых процессов, и они отобразят файл как «общий», они будут использовать основную память.

После сопоставления он будет вести себя как bytearray . Вы можете использовать регулярные выражения, find или любой другой распространенный метод.

Помните, что этот подход немного специфичен для ОС. Он не будет автоматически переносимым.

0 голосов
/ 08 февраля 2011

5000 строк невелики (ну, это зависит от того, насколько длинны строки ...)

В любом случае: при условии, что строка будет словом и будет разделена пробелами ...

lines=open(file_path,'r').readlines()
str_wanted="whatever_youre_looking_for"


    for i in range(len(lines)):
        l1=lines.split()
        for p in range(len(l1)):
            if l1[p]==str_wanted:
                #found
                # i is the file line, lines[i] is the full line, etc.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...