triple fault
triple fault

Reputation: 14108

Modifying lines

I'm looking to load a file and modify several predefined lines to certain values. The following is what I tried:

with open("test.txt",'w') as f:
                for i,line in f:
                    if (i == 2):
                        f.writelines(serial1)
                    if (i == 3):
                        f.writelines(serial2)
                    if (i == 4):
                        f.writelines(serial3)
                    else:
                        f.writelines(line)

However, when running the code I got the following error:

for i,line in f:
io.UnsupportedOperation: not readable

What am I doing wrong?

Upvotes: 0

Views: 75

Answers (3)

jsbueno
jsbueno

Reputation: 110311

You open the file for write-only (w mode) and try to read it - which is done by the for statement.

That is: iterating over a file with the for statement is done when you are reading the file.

If you plan to write to it, for on the fileitself can't help you - just use a normal counter with "range" and write to it.

with open("test.txt",'w') as f:
    for i in range(desired_lines):
        if (i == 2):
            f.write(serial1 + "\n")
        if (i == 3):
            f.write(serial2 + "\n")
        if (i == 4):
            f.write(serial3 + "\n")
        else:
            f.write(line + "\n")

Also, writelines should be used when you have a list of strings you want to write, each ina separate line - yu don't show the content of your vaiables, but given that you want the exact line numbers, it looks like writelines is not what you want.

(On a side note - beware of indentation - you should ident a fixed ammount foreach block you enter in Python - it will work with an arbitrry identation like you did, but is not usual in Python code)

update It looks like you need to change just some lines of an already existing text file. The best approach for this is by far to recreate another file, replacing the lines you want, and rename everything afterwards. (Writing over a text file is barely feasible, since all text lines would have to be the same size as the previously existing lines - and still would gain nothing in low-level disk access).

import os

...

lines_to_change{
    2: serial1,
    3: serial2,
    4: serial3,
}

with open("test.txt",'rt') as input_file, open("newfile.txt", "wt") as output_file:
    for i, line in enumerate(input_file):
        if i in lines_to_change:
            output_file.write(lines_to_change[i] + '\n')
        else:
            output_file.write(line)

os.rename("test.txt", "test_old.txt")
os.rename("newfile.txt", "test.txt")
os.unlink("test_old.txt")

Upvotes: 1

Hai Vu
Hai Vu

Reputation: 40733

What you are doing is called editing in place. For that, Python standard library fileinput can help:

import fileinput

for line in fileinput.input('test.txt', inplace=True):
    if fileinput.lineno() == 2:
        print serial1
    elif fileinput.lineno() == 3:
        print serial2
    elif fileinput.lineno() == 4:
        print serial3
    else:
        print line

Update

As for "What I'm doing wrong?" there are a couple:

  • You opened the file for writing, how can you read from it? You might attempt to change the mode from "w" to "r+", which is read and write. That brings up another dilemma: After reading line 2, the file pointer is positioned at the beginning of line 3, anything you write will overwrite line 3, not to mention the length differences between the old and new lines. It is better to write to a new temp file, then copy it back to the original when done, or you can use fileinput as shown above.
  • for i, line in f does not work, I think you meant for i, line in enumerate(f, 1)
  • What's with the extremely deep indentation?
  • The if statement should be if ... elif ... else. Currently your else clause is attached only to the if i == 4 statement, not the other two.

Upvotes: 2

Aif
Aif

Reputation: 11220

As pointed out, your issue is that you try to read and write in the same file. The easiest way is to open a new file, and replace the first one when you have the desired output.

To get the file lines number, you can use enumerate:

with open("test.txt",'r') as f:
                for i,line in enumerate(f):
                    if (i == 2):
                        f.writelines(serial1)
                    if (i == 3):
                        f.writelines(serial2)
                    if (i == 4):
                        f.writelines(serial3)
                    else:
                        f.writelines(line)

Upvotes: 0

Related Questions