我如何清除一个 Stringio 对象?

我创建了一个 stringio 对象,其中包含一些文本。我想清除它现有的价值并重用它,而不是回忆它。有什么办法吗?

48839 次浏览

DR

不用费心去清理它,只要创建一个新的ーー它会更快。

方法

巨蟒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 可能更便宜(也更容易)。参见下面的基准测试。

巨蟒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.

值得注意的是,现在的 当前文件位置没有改变,而截断到大小为零将重置 Python2变体中的位置。

因此,对于 Python2,您只需要

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

如果在 Python3中这样做,就不会得到预期的结果:

>>> 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对象!

时代杂志

巨蟒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

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% ,对于3KB 的数据快8%) ,但是创建一个新的 cStringIO.StringIO会更快(“更快”)(对于空数据快8% ,对于3KB 的数据快10%)。所以我建议使用最简单的一个ーー假设您使用的是 CPython,那么使用 cStringIO并创建新的。

巨蟒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,而不是截断和寻找。

我如何设法优化我的处理(读入块,处理每个块,写出处理流到文件)的许多文件在一个序列是,我重用相同的 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()

有一些重要的事情需要注意(至少在 Python 3.2中是这样的) :

在截断(0)之前需要使用 find (0) 是的。下面是一些没有 find (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'

在截断前使用 find (0) ,我们可以得到预期的输出:

'111'
''
'111'