как очистить объект stringio? - PullRequest
52 голосов
/ 02 декабря 2010

У меня есть объект stringio, и в нем есть текст.Я хотел бы очистить его существующие значения и использовать его вместо того, чтобы вспомнить его.Есть ли способ сделать это?

Ответы [ 3 ]

85 голосов
/ 02 декабря 2010

TL; DR

Не пытайтесь очистить его, просто создайте новый - он быстрее.

Метод

Python 2

Вот как я могу найти такие вещи:

>>> from StringIO import StringIO
>>> dir(StringIO)
['__doc__', '__init__', '__iter__', '__module__', 'close', 'flush', 'getvalue', 'isatty', 'next', 'read', 'readline', 'readlines', 'seek', 'tell', 'truncate', 'write', 'writelines']
>>> help(StringIO.truncate)
Help on method truncate in module StringIO:

truncate(self, size=None) unbound StringIO.StringIO method
    Truncate the file's size.

    If the optional size argument is present, the file is truncated to
    (at most) that size. The size defaults to the current position.
    The current file position is not changed unless the position
    is beyond the new file size.

    If the specified size exceeds the file's current size, the
    file remains unchanged.

Итак, вы хотите .truncate(0).Но, вероятно, дешевле (и проще) инициализировать новый StringIO.Ниже приведены тесты.

Python 3

(Спасибо tstone2077 за , указывающим на разницу .)

>>> from io import StringIO
>>> dir(StringIO)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno', 'flush', 'getvalue', 'isatty', 'line_buffering', 'newlines', 'read', 'readable', 'readline', 'readlines', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'writelines']
>>> help(StringIO.truncate)
Help on method_descriptor:

truncate(...)
    Truncate size to pos.

    The pos argument defaults to the current file position, as
    returned by tell().  The current file position is unchanged.
    Returns the new absolute position.

Важно отметить, что теперь текущая позиция файла не изменяется , тогда как усечение до нулевого размера приведет к сбросу позиции в варианте Python 2.

Таким образом, для Python 2,вам нужно только

>>> from cStringIO import StringIO
>>> s = StringIO()
>>> s.write('foo')
>>> s.getvalue()
'foo'
>>> s.truncate(0)
>>> s.getvalue()
''
>>> s.write('bar')
>>> s.getvalue()
'bar'

Если вы сделаете это в Python 3, вы не получите ожидаемый результат:

>>> from io import StringIO
>>> s = StringIO()
>>> s.write('foo')
3
>>> s.getvalue()
'foo'
>>> s.truncate(0)
0
>>> s.getvalue()
''
>>> s.write('bar')
3
>>> s.getvalue()
'\x00\x00\x00bar'

Так что в Python 3 вам также нужно сбросить позицию:

>>> from cStringIO import StringIO
>>> s = StringIO()
>>> s.write('foo')
3
>>> s.getvalue()
'foo'
>>> s.truncate(0)
0
>>> s.seek(0)
0
>>> s.getvalue()
''
>>> s.write('bar')
3
>>> s.getvalue()
'bar'

Если в коде Python 2 используется метод truncate, безопаснее одновременно вызывать seek(0) (до или после, это не имеет значения), чтобы код победил 'не работает, когда вы неизбежно портируете его на Python 3. И есть еще одна причина, почему вы должны просто создать новый StringIO объект!

Times

Python 2

>>> from timeit import timeit
>>> def truncate(sio):
...     sio.truncate(0)
...     return sio
... 
>>> def new(sio):
...     return StringIO()
... 

Когда пусто, с StringIO:

>>> from StringIO import StringIO
>>> timeit(lambda: truncate(StringIO()))
3.5194039344787598
>>> timeit(lambda: new(StringIO()))
3.6533868312835693

С 3KB данных, с StringIO:

>>> timeit(lambda: truncate(StringIO('abc' * 1000)))
4.3437709808349609
>>> timeit(lambda: new(StringIO('abc' * 1000)))
4.7179079055786133

И то же самоеith cStringIO:

>>> from cStringIO import StringIO
>>> timeit(lambda: truncate(StringIO()))
0.55461597442626953
>>> timeit(lambda: new(StringIO()))
0.51241087913513184
>>> timeit(lambda: truncate(StringIO('abc' * 1000)))
1.0958449840545654
>>> timeit(lambda: new(StringIO('abc' * 1000)))
0.98760509490966797

Итак, игнорируя потенциальные проблемы с памятью (del oldstringio), быстрее усечь StringIO.StringIO (на 3% быстрее для пустых, на 8% быстрее для 3 КБ данных), но этобыстрее (также «быстрее») для создания нового cStringIO.StringIO (на 8% быстрее для пустых, на 10% быстрее для 3 КБ данных).Поэтому я бы порекомендовал использовать самый простой - предположим, что вы работаете с CPython, используйте cStringIO и создайте новые.

Python 3

Тот же код, только с seek(0) вставлено.

>>> def truncate(sio):
...     sio.truncate(0)
...     sio.seek(0)
...     return sio
... 
>>> def new(sio):
...     return StringIO()
...

Когда пусто:

>>> from io import StringIO
>>> timeit(lambda: truncate(StringIO()))
0.9706327870007954
>>> timeit(lambda: new(StringIO()))
0.8734330690022034

С 3KB данных в:

>>> timeit(lambda: truncate(StringIO('abc' * 1000)))
3.5271066290006274
>>> timeit(lambda: new(StringIO('abc' * 1000)))
3.3496507499985455

Так что для Python 3 вместо этого создайте новыйповторное использование пустого - на 11% быстрее, а создание нового вместо повторного использования 3K - на 5% быстрее.Опять же, создайте новый StringIO вместо усечения и поиска.

8 голосов
/ 23 апреля 2013

Есть кое-что важное, на что следует обратить внимание (по крайней мере, в Python 3.2):

seek (0) IS необходимо перед усечением (0). Вот некоторый код без поиска (0):

from io import StringIO
s = StringIO()
s.write('1'*3)
print(repr(s.getvalue()))
s.truncate(0)
print(repr(s.getvalue()))
s.write('1'*3)
print(repr(s.getvalue()))

Какие выходы:

'111'
''
'\x00\x00\x00111'

при поиске (0) перед усечением мы получаем ожидаемый результат:

'111'
''
'111'
2 голосов
/ 23 марта 2012

Как мне удалось оптимизировать обработку (чтение в чанках, обработку каждого чанка, запись обработанного потока в файл) многих файлов в последовательности, я повторно использую один и тот же экземпляр cStringIO.StringIO, но всегда reset() послеиспользуя, затем напишите ему, а затем truncate().Делая это, я только обрезаю часть в конце, которая мне не нужна для текущего файла.Похоже, это дало мне увеличение производительности примерно на 3%.Любой, кто более опытен в этом, может подтвердить, действительно ли это оптимизирует распределение памяти.

sio = cStringIO.StringIO()
for file in files:
    read_file_chunks_and_write_to_sio(file, sio)
    sio.truncate()
    with open('out.bla', 'w') as f:
        f.write(sio.getvalue())
    sio.reset()
...