Как я могу выполнить буферизованный поиск и заменить? - PullRequest
2 голосов
/ 08 октября 2011

У меня есть XML-файлы, которые содержат недопустимые последовательности символов , которые вызывают сбой анализа.Они похожи на .Чтобы решить проблему, я избегаю их, заменяя целую последовательность escape-последовательностью:  --> !#~10^.Затем, после того, как я закончу синтаксический анализ, я могу восстановить их до того, что они были.

buffersize = 2**16   # 64 KB buffer

def escape(filename):
    out = file(filename + '_esc', 'w') 

    with open(filename, 'r') as f:
        buffer = 'x'     # is there a prettier way to handle the first one?
        while buffer != '':
            buffer = f.read(buffersize)
            out.write(re.sub(r'&#x([a-fA-F0-9]+);', r'!#~\1^', buffer))

    out.close()

Файлы очень большие, поэтому я должен использовать буферизацию (mmap дал мне MemoryError).Поскольку буфер имеет фиксированный размер, я сталкиваюсь с проблемами, когда буфер оказывается достаточно маленьким, чтобы разбить последовательность.Представьте, что размер буфера равен 8, а файл выглядит так:

 123456789
 hello!&x10;

Буфер будет читать только hello!&x, что позволяет &x10; проскальзывать сквозь трещины.Как мне это решить?Я думал о том, чтобы получить больше символов, если последние несколько выглядят так, как будто они могут принадлежать последовательности символов, но логика, о которой я думал, очень некрасива.

Ответы [ 2 ]

2 голосов
/ 08 октября 2011

Во-первых, не беспокойтесь о чтении и записи файла, вы можете создать файлоподобный объект, который обернет ваш открытый файл и обработает данные, прежде чем они будут обработаны синтаксическим анализатором. Во-вторых, ваша буферизация может просто позаботиться о концах прочитанных байтов. Вот некоторый рабочий код:

class Wrapped(object):
    def __init__(self, f):
        self.f = f
        self.buffer = ""

    def read(self, size=0):
        buf = self.buffer + self.f.read(size)
        buf = buf.replace("!", "!!")
        buf = re.sub(r"&(#x[0-9a-fA-F]+;)", r"!\1", buf)
        # If there's an ampersand near the end, hold onto that piece until we
        # have more, to be sure we don't miss one.
        last_amp = buf.rfind("&", -10, -1)
        if last_amp > 0:
            self.buffer = buf[last_amp:]
            buf = buf[:last_amp]
        else:
            self.buffer = ""
        return buf

Затем в вашем коде замените это:

it = ET.iterparse(file(xml, "rb"))

с этим:

it = ET.iterparse(Wrapped(file(xml, "rb")))

В-третьих, я использовал замену, заменяя «&» на «!» И «!» с "!!", так что вы можете исправить их после разбора, и вы не рассчитываете на непонятные последовательности. В конце концов, это данные переполнения стека, поэтому множество странных случайных знаков препинания может произойти естественным образом.

1 голос
/ 08 октября 2011

Если ваша последовательность длиной 6 символов, вы можете использовать буферы с 5 перекрывающимися символами. Таким образом, вы уверены, что ни одна последовательность не проскользнет между буферами.

Вот пример, который поможет вам визуализировать:

--&#x10
  --

--
   #x10;--

Что касается реализации, просто добавьте 5 последних символов последнего буфера к новому буферу:

buffer = buffer[-5:] + f.read(buffersize)

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

f.seek(-5, os.SEEK_CUR)

В обоих случаях вам придется немного изменить сценарий для обработки первой итерации.

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