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

Исправляемый поток регулярного выражения Python

Есть ли способ использовать регулярное выражение для потока в python? как

reg = re.compile(r'\w+')
reg.match(StringIO.StringIO('aa aaa aa'))

И я не хочу этого делать, получая значение всей строки. Я хочу знать, есть ли способ совместить регулярное выражение на srtream (на лету).

4b9b3361

Ответ 1

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

Это не сработало (я получил "TypeError: ожидаемая строка или буфер" из re.match), поэтому я немного посмотрел на реализацию модуля re в стандартной библиотеке.

К сожалению, использование регулярных выражений в потоке кажется невозможным. Ядро модуля реализовано на C, и эта реализация ожидает, что весь ввод будет сразу в памяти (я думаю, в основном из-за соображений производительности). Кажется, нет простого способа исправить это.

Я также посмотрел PYL (Python LEX/YACC), но их lexer использует re внутренне, поэтому это не решит проблему.

Возможно, можно использовать ANTLR, который поддерживает бэкэнд Python. Он создает лексер с использованием чистого кода на языке python и, похоже, может работать с входными потоками. Поскольку для меня проблема не такая важная (я не ожидаю, что мой вклад будет очень большим...), я, вероятно, не буду исследовать это дальше, но это может стоить взгляда.

Ответ 2

В конкретном случае файла, если вы можете записать карту памяти с mmap, и если вы работаете с bytestrings вместо Unicode, вы можете передать файл с отображением памяти в re, как если бы это была байтовая строка, и она будет работать. Это ограничено вашим адресным пространством, а не вашей оперативной памятью, поэтому 64-разрядная машина с 8 ГБ оперативной памяти может легко отображать 32-гигабайтный файл.

Если вы можете это сделать, это действительно хороший вариант. Если вы не можете, вы должны перейти к более беспорядочным параметрам.


Сторонний regex модуль (не re) предлагает поддержку частичного соответствия, которая может использоваться для создания поддержки потоковой передачи... но это беспорядочно и имеет много предостережений. Такие вещи, как lookbehinds и ^, не будут работать, совпадения с нулевой шириной будут сложными, и я не знаю, будет ли он корректно взаимодействовать с другими расширенными функциями regex, а re - не, Тем не менее, это, по-видимому, самое близкое к полному решению.

Если вы передадите partial=True в regex.match, regex.fullmatch, regex.search или regex.finditer, то помимо сообщения о полных совпадениях, regex также сообщит о том, что может быть совпадением, если данные был расширен:

In [10]: regex.search(r'1234', '12', partial=True)
Out[10]: <regex.Match object; span=(0, 2), match='12', partial=True>

Он будет сообщать о частичном совпадении, а не о полном совпадении, если больше данных может изменить результат совпадения, так что, например, regex.search(r'[\s\S]*', anything, partial=True) всегда будет частичным совпадением.

При этом вы можете сохранить скользящее окно данных, чтобы оно соответствовало, расширяя его, когда вы попадаете в конец окна и отбрасываете потребляемые данные с самого начала. К сожалению, все, что смущает данные, исчезающие с начала строки, не будет работать, поэтому lookbehinds, ^, \b и \b отсутствуют. Совпадения с нулевой шириной также потребуют тщательной обработки. Здесь доказательство концепции, использующее скользящее окно над файлом или файлоподобным объектом:

import regex

def findall_over_file_with_caveats(pattern, file):
    # Caveats:
    # - doesn't support ^ or backreferences, and might not play well with
    #   advanced features I'm not aware of that regex provides and re doesn't.
    # - Doesn't do the careful handling that zero-width matches would need,
    #   so consider behavior undefined in case of zero-width matches.
    # - I have not bothered to implement findall behavior of returning groups
    #   when the pattern has groups.
    # Unlike findall, produces an iterator instead of a list.

    # bytes window for bytes pattern, unicode window for unicode pattern
    # We assume the file provides data of the same type.
    window = pattern[:0]
    chunksize = 8192
    sentinel = object()

    last_chunk = False

    while not last_chunk:
        chunk = file.read(chunksize)
        if not chunk:
            last_chunk = True
        window += chunk

        match = sentinel
        for match in regex.finditer(pattern, window, partial=not last_chunk):
            if not match.partial:
                yield match.group()

        if match is sentinel or not match.partial:
            # No partial match at the end (maybe even no matches at all).
            # Discard the window. We don't need that data.
            # The only cases I can find where we do this are if the pattern
            # uses unsupported features or if we're on the last chunk, but
            # there might be some important case I haven't thought of.
            window = window[:0]
        else:
            # Partial match at the end.
            # Discard all data not involved in the match.
            window = window[match.start():]
            if match.start() == 0:
                # Our chunks are too small. Make them bigger.
                chunksize *= 2

Ответ 3

Это, кажется, старая проблема. Поскольку я отправил в аналогичный вопрос, вы можете захотеть подклассировать класс Matcher моего решения streamsearch-py и выполнить сопоставление регулярных выражений в буфере. Проверьте шаблон kmp_example.py. Если окажется, что классический подход Knuth-Morris-Pratt - это все, что вам нужно, тогда ваша проблема будет решена прямо сейчас с помощью этой небольшой библиотеки с открытым исходным кодом: -)

Ответ 4

Да - с помощью метода getvalue:

import cStringIO
import re

data = cStringIO.StringIO("some text")
regex = re.compile(r"\w+")
regex.match(data.getvalue())