Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

in-place replacement in StringIO

How do I replace a string with another inside a StringIO? - I've heard it's possible if they're the same length.

Attempt:

from cStringIO import StringIO

c = 'can\nhaz\nfoo'

sio = StringIO(c)

for line in sio:
    if line == 'haz\n':
        # sio.write('bar\n')
        line = 'bar\n'
        break

sio.seek(0)
sio.readlines()   # [ 'can\n', 'haz\n', 'bar' ]

PS: Currently working on a solution in C, but would much rather make this work.

like image 544
A T Avatar asked Nov 27 '16 09:11

A T


People also ask

What is StringIO StringIO?

The StringIO module is an in-memory file-like object. This object can be used as input or output to the most function that would expect a standard file object. When the StringIO object is created it is initialized by passing a string to the constructor. If no string is passed the StringIO will start empty.

Do I need to close StringIO Python?

The documentation says: StringIO. close() : Free the memory buffer. Attempting to do further operations with a closed StringIO object will raise a ValueError.


1 Answers

StringIO tries to mock up a regular file in read/write, except that it's stored in memory. You have the same issues when trying to edit a text file in-place than with a regular file (replacing by longer string overwrites the following line). You also have the same options.

Small other change: cStringIO created with a default string at start is not writable. But you can workaround this by creating an empty object and write to it (you obviously have access to a write method now)

What you're looking for:

c = 'can\nhaz\nfoo'

sio = StringIO()
sio.write(c)
sio.seek(0)

offset = sio.tell()
for line in sio:
    if line == 'haz\n':
        sio.seek(offset)
        sio.write('bar\n')
        break
    offset = sio.tell()

It stores the offset before the lineread and if pattern is found, it seeks back to the previous offset, and writes the same number of bytes

Brutal solution if you want to use cStringIO: you can read the buffer fully, perform your replace, and write back to it (allows replacing by longer strings), but reads the whole buffer, which is probably not what you want:

c = 'can\nhaz\nfoo'

sio = StringIO(c)

v = sio.getvalue()
v = v.replace("haz","bar")
sio.seek(0)
sio.write(v)

Note that Python 3 removed those objects, now it's just io. So relying on such or such implementation will make your code non-portable.

like image 110
Jean-François Fabre Avatar answered Oct 08 '22 15:10

Jean-François Fabre