Короче говоря: вы не можете избежать 2 копий, используя StringIO.
Некоторые предположения:
- Вы используете cStringIO, иначе было бы глупо так много оптимизировать.
- Вам нужна скорость, а не эффективность памяти. Если нет, см. Решение Jakob Bowyer's или используйте вариант, используя
file.read(SOME_BYTE_COUNT)
, если ваш файл является двоичным.
- Вы уже заявили об этом в комментариях, но для полноты: вы хотите на самом деле редактировать содержимое, а не просто просматривать его.
Длинный ответ : Поскольку строки python являются неизменяемыми, а буфер StringIO - нет, копия должна быть сделана рано или поздно; в противном случае вы изменили бы неизменный объект! Для того, что вы хотите сделать возможным, объект StringIO должен иметь специальный метод, который читает непосредственно из файлового объекта, заданного в качестве аргумента. Нет такого метода.
За пределами из StringIO существуют решения, позволяющие избежать дополнительной копии. Вдобавок ко всему, это будет считывать файл непосредственно в модифицируемый байтовый массив, без дополнительной копии:
import numpy as np
a = np.fromfile("filename.ext", dtype="uint8")
Работать с ним может быть затруднительно в зависимости от предполагаемого использования, поскольку это массив значений от 0 до 255, а не массив символов. Но он функционально эквивалентен объекту StringIO, и использование np.fromstring
, np.tostring
, np.tofile
и разделение на нотации приведут вас туда, куда вы хотите. Вам также могут понадобиться np.insert
, np.delete
и np.append
.
Я уверен, что есть другие модули, которые будут делать подобные вещи.
TIMEIT:
Насколько все это действительно имеет значение ? Ну что ж, посмотрим. Я сделал файл размером 100 МБ, largefile.bin
. Затем я читаю в файле оба метода и меняю первый байт.
$ python -m timeit -s "import numpy as np" "a = np.fromfile('largefile.bin', 'uint8'); a[0] = 1"
10 loops, best of 3: 132 msec per loop
$ python -m timeit -s "from cStringIO import StringIO" "a = StringIO(); a.write(open('largefile.bin').read()); a.seek(0); a.write('1')"
10 loops, best of 3: 203 msec per loop
Так что в моем случае использование StringIO на 50% медленнее, чем использование numpy.
Наконец, для сравнения, отредактируйте файл напрямую:
$ python -m timeit "a = open('largefile.bin', 'r+b'); a.seek(0); a.write('1')"
10000 loops, best of 3: 29.5 usec per loop
Итак, это почти в 4500 раз быстрее. Конечно, это очень зависит от того, что вы собираетесь делать с файлом. Изменение первого байта вряд ли является представительным. Но при использовании этого метода у вас есть преимущество перед двумя другими, и, поскольку большинство ОС имеют хорошую буферизацию дисков, скорость также может быть очень хорошей.
(Если вам не разрешено редактировать файл и вы хотите избежать затрат на создание рабочей копии, есть несколько возможных способов увеличить скорость. Если вы можете выбрать файловую систему, Btrfs имеет операцию копирования файла 1053 * copy-on-write - практически мгновенное создание копии файла. Этого же эффекта можно добиться, используя снимок LVM любой файловой системы.)