numpy.memmap для массива строк? - PullRequest
3 голосов
/ 05 мая 2011

Можно ли использовать numpy.memmap для сопоставления большого дискового массива строк в память?

Я знаю, что это можно сделать для чисел с плавающей запятойи тому подобное, но этот вопрос конкретно о строках.

Меня интересуют решения как для строк фиксированной, так и для переменной длины.

Решение свободно диктует любой разумный формат файла.

Ответы [ 2 ]

2 голосов
/ 05 мая 2011

Если все строки имеют одинаковую длину, как предполагает термин «массив», это легко возможно:

a = numpy.memmap("data", dtype="S10")

будет примером для строк длиной 10.

Редактировать : Поскольку, очевидно, строки не имеют одинаковую длину, вам необходимо проиндексировать файл, чтобы разрешить доступ к элементу O (1). Это требует считывания всего файла один раз и сохранения начальных индексов всех строк в памяти. К сожалению, я не думаю, что существует чистый способ индексации NumPy без создания массива того же размера, что и файл в памяти. Этот массив можно удалить после извлечения индексов.

1 голос
/ 05 мая 2011

Наиболее гибкий вариант - переключиться на базу данных или другую более сложную файловую структуру на диске.

Однако, возможно, есть веская причина, по которой вы предпочитаете хранить текст в виде простого текстового файла....

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

Это потребует немного больше работы, но вы можете сделать что-то вроде этого:

class IndexedText(object):
    def __init__(self, filename, mode='r'):
        if mode not in ['r', 'w', 'a']:
            raise ValueError('Only read, write, and append is supported')
        self._mainfile = open(filename, mode)
        self._idxfile = open(filename+'idx', mode)

        if mode != 'w':
            self.indicies = [int(line.strip()) for line in self._idxfile]
        else:
            self.indicies = []

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        self._mainfile.close()
        self._idxfile.close()

    def __getitem__(self, idx):
        position = self.indicies[idx]
        self._mainfile.seek(position)
        # You might want to remove the automatic stripping...
        return self._mainfile.readline().rstrip('\n')

    def write(self, line):
        if not line.endswith('\n'):
            line += '\n'
        position = self._mainfile.tell()
        self.indicies.append(position)
        self._idxfile.write(str(position)+'\n')
        self._mainfile.write(line)

    def writelines(self, lines):
        for line in lines:
            self.write(line)


def main():
    with IndexedText('test.txt', 'w') as outfile:
        outfile.write('Yep')
        outfile.write('This is a somewhat longer string!')
        outfile.write('But we should be able to index this file easily')
        outfile.write('Without needing to read the entire thing in first')

    with IndexedText('test.txt', 'r') as infile:
        print infile[2]
        print infile[0]
        print infile[3]

if __name__ == '__main__':
    main()
...