Python re для пользовательского типа последовательности - PullRequest
12 голосов
/ 07 апреля 2019

У меня есть пользовательский объект, подобный последовательности, s, который наследует collections.Sequence и реализует пользовательские __len__ и __getitem__.Он представляет собой большой набор строк (> 4 ГБ) и загружается лениво (я не могу позволить себе загружать все в память).

Я хотел бы сделать для него совпадение RE, re.compile('some-pattern').match(s), но этозавершается с TypeError: expected string or buffer.

На практике шаблон не похож на '.*', требующий загрузки всего s;обычно требуется несколько первых десятков байтов для сопоставления;тем не менее, я не могу заранее сказать точное количество байтов и хочу, чтобы оно было общим, поэтому я не хочу делать что-то вроде re.compile('some-pattern').match(s[:1000]).

Любые предложения о том, как создать str-подобный объект, который принимается re?

Следующий код иллюстрирует мои неудачные попытки.Наследование от str также не работает.

In [1]: import re, collections

In [2]: class MyStr(collections.Sequence):
    def __len__(self): return len('hello')
    def __getitem__(self, item): return 'hello'[item]
   ...:

In [3]: print(re.compile('h.*o').match(MyStr()))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-3-df08913b19d7> in <module>()
----> 1 print(re.compile('h.*o').match(MyStr()))

TypeError: expected string or buffer

Если большой строковый объект получен из одного большого файла, тогда я могу использовать mmap, и он должен работать.Однако мой случай более сложный.У меня есть несколько больших файлов, я mmap редактировал каждый из них, и у меня есть собственный класс, который представляет их объединенное представление.Я на самом деле хочу выполнить сравнение RE, начиная с любой заданной позиции в представлении.Я опускаю такие детали в первоначальном вопросе, но думаю, что это может быть полезно для того, кто хочет понять, почему у меня такое странное требование.

1 Ответ

11 голосов
/ 11 апреля 2019

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

Это потому, что в настоящее время нет специального метода, который позволил бы вашему пользовательскому классу действовать как объект протокола буфера . Методы re принимают только строки str (которые реализуют буферный протокол) и строки unicode (и подклассы, данные доступны напрямую, а не через __unicode__). Методы re не принимают произвольные последовательности, и только буферный протокол позволил бы вам избежать считывания всего этого в память за один раз.

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

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

В Python эта функция доступна через mmap модуль , а файл с отображением в памяти реализует протокол буфера . Вы можете передать такие объекты непосредственно в re.match(), и Python и ваша ОС будут работать вместе для поиска данных в файле на предмет соответствия.

Итак, учитывая большой файл filename = '/path/to/largefile' и регулярное выражение pattern, это будет искать в файле совпадение в начале для вашего шаблона:

import re
import mmap
import os

fd = os.open(filename, os.O_RDONLY)
mapped = mmap.mmap(fd, 0)
matched = re.match(pattern, mapped)

Если у вас есть несколько файлов , вам нужно найти способ объединить их. Виртуально или физически. Если вы используете Linux, вы можете объединять файлы виртуально, используя сетевое блочное устройство, или вы можете использовать виртуальную файловую систему FUSE. См. Виртуальный файл, содержащий объединение других файлов .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...