Question

J'ai un objet StringIO créé et il a un texte en elle. Je voudrais effacer ses valeurs existantes et la réutiliser au lieu de le rappeler. Y at-il de toute façon de le faire?

Était-ce utile?

La solution

TL; DR

Ne pas déranger l'effacer, il suffit de créer un nouveau-il est plus rapide.

La méthode

Python 2

Voici comment je trouverais ces choses:

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

, vous voulez .truncate(0). Mais il est sans doute moins cher (et plus facile) pour initialiser une nouvelle StringIO. Voir ci-dessous pour référence.

Python 3

(Merci à tstone2077 pour indiquant la différence ).

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

Il est important de noter ce que maintenant la position actuelle du fichier est inchangé , alors que tronquer à la taille zéro réinitialisait la position dans la variante Python 2.

Ainsi, pour Python 2, il vous suffit

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

Si vous faites cela en Python 3, vous ne serez pas obtenir le résultat que vous attendiez:

>>> 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'

Donc en Python 3 vous avez également besoin de réinitialiser la position:

>>> 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'

Si vous utilisez la méthode truncate dans le code Python 2, il est plus sûr de seek(0) d'appel en même temps (avant ou après, il n'a pas d'importance) afin que le code ne se cassera pas lorsque vous inévitablement le port à Python 3. et il y a une autre raison pour laquelle vous devez simplement créer un nouvel objet StringIO!

Temps

Python 2

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

vide, avec StringIO:

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

Avec 3 Ko de données, avec StringIO:

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

Et la même chose avec 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

Alors, sans tenir compte des problèmes de mémoire potentiels (del oldstringio), il est plus rapide de tronquer un StringIO.StringIO (3% plus rapide pour vide, 8% plus rapide pour 3KB des données), mais il est plus rapide ( « fasterer » trop) pour créer une nouvelle cStringIO.StringIO ( 8% plus rapide pour vide, 10% plus rapide pour 3KB de données). Donc, je vous recommande juste en utilisant le plus facile d'un si vous travaillez avec présumant CPython, l'utilisation cStringIO et créer de nouvelles.

Python 3

Le même code, juste avec mettre de seek(0) dans.

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

vide:

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

Avec 3 Ko de données:

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

Donc, pour Python 3 créer un nouveau lieu de réutiliser une vierge est 11% plus rapide et la création d'un nouveau lieu de réutiliser une 3K est un 5% plus rapide. Encore une fois, créez un nouveau StringIO plutôt que tronquer et à la recherche.

Autres conseils

Il y a quelque chose d'important à noter (au moins avec Python 3.2):

seek (0) IS nécessaire avant troncature (0). Voici un code sans seek (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()))

Quels sont les sorties:

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

avec recherche (0) avant la troncature, nous obtenons le résultat attendu:

'111'
''
'111'

Comment puis-je réussi à optimiser mon traitement (lu en morceaux, traiter chaque morceau, écrire flux traitées vers le fichier) de plusieurs fichiers dans une séquence est que je réutiliser la même instance de cStringIO.StringIO, mais toujours reset() après l'utilisation, puis écrire à, puis truncate(). En faisant cela, je ne tronquant la partie à la fin que je ne ai pas besoin pour le fichier en cours. Cela me semble avoir donné une ~ 3% d'augmentation de la performance. Toute personne qui est plus expert sur ce qui pourrait confirmer si cela optimise en effet l'allocation de mémoire.

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()
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top