Поток анализа регулярных выражений Python - PullRequest
27 голосов
/ 08 января 2011

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

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

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

Ответы [ 4 ]

16 голосов
/ 03 января 2012

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

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

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

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

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

5 голосов
/ 28 марта 2017

В конкретном случае файла, если вы можете отобразить файл в памяти с помощью mmap, и если вы работаете со строками байтов вместо 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's 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
2 голосов
/ 11 января 2015

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

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

Да - с использованием метода getvalue:

import cStringIO
import re

data = cStringIO.StringIO("some text")
regex = re.compile(r"\w+")
regex.match(data.getvalue())
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...