Подтвердить что ты не робот

Дешевый способ поиска большого текстового файла для строки

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

4b9b3361

Ответ 1

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

with open('largeFile', 'r') as inF:
    for line in inF:
        if 'myString' in line:
            # do_something

Ответ 2

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

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

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

Ответ 3

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

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 байт, чтобы поймать строку, если она запущена в конце последнего буфера чтения и продолжение на следующем.
  • return position или -1, если не найден

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

Ответ 4

Я попытался собрать многопроцессорный пример поиска текстового файла. Это мое первое усилие при использовании модуля многопроцессорности; и я питон 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' )

Ответ 5

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

Ответ 6

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

Чтобы прочитать произвольный большой текст и узнать, существует ли строка, замените ее, вы можете использовать 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)]

Ответ 7

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

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

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

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

Ответ 8

Это полностью вдохновлено ответом laurasia выше, но это уточняет структуру.

Также добавлены некоторые проверки:

  • Он будет правильно возвращать 0 при поиске пустого файла для пустой строки. В ответе Лавразии это крайний случай, который вернет -1.
  • Он также предварительно проверяет, больше ли целевая строка, чем размер буфера, и выдает ошибку, если это так.

На практике строка цели должна быть намного меньше буфера для эффективности, и существуют более эффективные методы поиска, если размер строки цели очень близок к размеру буфера.

def fnd(fname, goal, start=0, bsize=4096):
    if bsize < len(goal):
        raise ValueError("The buffer size must be larger than the string being searched for.")
    with open(fname, 'rb') as f:
        if start > 0:
            f.seek(start)
        overlap = len(goal) - 1
        while True:
            buffer = f.read(bsize)
            pos = buffer.find(goal)
            if pos >= 0:
                return f.tell() - len(buffer) + pos
            if not buffer:
                return -1
            f.seek(f.tell() - overlap)

Ответ 9

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.