Is there any way to overwrite a specific line of a text file using Python?

Asked

Viewed 1,193 times

3

I would like to edit a specific line from a file, but the file write.() does not allow such manipulation.

Being the Text:

1 "Ola"
2 "Como vai?"
3 "Tudo bem?"

I’d like to edit only line 2:

1 "Ola"
2 "Boa tarde"
3 "Tudo bem?"

In python we can read a specific line using the file readline.(), but I couldn’t find a function to edit a specific line. There is a function for this?

  • 3

    Has as an example of the original text and the desired?

  • 2

    that question is very clear to me.

  • 1

    Now I understand the question perfectly

2 answers

5


The easiest way for you to do that, in my view, is to create a buffer temporary to store the contents of the final file, replacing the line that you want to change. The logic is simple: open the file in read mode, go through its lines and, if the line is the desired one, write in the buffer the new content, otherwise write the content of the line itself. At the end, replace the contents of the archive with the contents of the buffer.

from io import StringIO

buffer = StringIO()

with open('data.txt', 'r') as stream:
    for index, line in enumerate(stream):
        # index == 1 representa a segunda linha do arquivo:
        buffer.write('Novo conteúdo da linha\n' if index == 1 else line)

with open('data.txt', 'w') as stream:
    stream.write(buffer.getvalue())

See working on Repl.it

However, in this way, the content ends up being stored all in memory through of buffer, which may affect the performance of the application depending on the size of the file in question.

  • Thank you... that should help.

  • I needed to edit a small part so it could work in python 2.x: from io import BytesIO as StringIO

  • @Evinerrayzecksantos In Python 2 you can do from cStringIO import StringIO

  • vlw... It also worked

  • The implementation of Try: is not convenient because the error received was not Import. So the script goes through this part without error until it finds the error that I had received: Typeerror: Nicode argument expected, got 'str'

  • @Evinerrayzecksantos but this error was given on which line of the code? I think that then the error would be in the string, possibly in buffer.write, where it would be necessary to place u'Novo conteúdo...', prefixed u.

  • Traceback (most recent call last): File "<stdin>", line 1, in <module> File "teste.py", line 17, in <module> buffer.write('O teste tambem funcionou\n' if index == 1 else line) TypeError: unicode argument expected, got 'str'

  • But this happened even without the try to import the class, no?

  • As I said, the error is not Import, that is, the interpreter does not stop me from using the line from io import Stringio

Show 4 more comments

2

Complementing the another answer, which at the end commented on the following:

However, in this way, the content ends up being stored in memory through the buffer, which can affect the performance of the application according to the size of the file in question.

In fact, this may be a problem. But it’s not just that: when writing to the same file with the option w, the file is truncated and its contents are lost. And if any error occurs while writing, the file will get corrupted.

A solution would be to first write the new content in a temporary file, and only at the end, if all went well, rename this temporary file to the original:

import shutil, tempfile

# lê do arquivo e escreve em outro arquivo temporário
with open('data.txt', 'r') as arquivo, \
     tempfile.NamedTemporaryFile('w', delete=False) as out:
    for index, linha in enumerate(arquivo, start=1):
        if index == 2: # linha 2, mudar o conteúdo
            out.write('Novo conteúdo\n')
        else: # não é linha 2, escreve a linha sem modificação
            out.write(linha)

# move o arquivo temporário para o original
shutil.move(out.name, 'data.txt')

So, if any problem occurs while writing, only the temporary file will get corrupted (but as it is temporary, it is not so problematic), and the original will be intact.

To get the line number, I used enumerate, but like the default is to start counting at zero, I added the parameter start so it starts at 1 (so the second line will be at index 2 instead of 1).

Browser other questions tagged

You are not signed in. Login or sign up in order to post.