0

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?

kmario23
  • 57,311
  • 13
  • 161
  • 150
triple fault
  • 13,410
  • 8
  • 32
  • 45
  • 1
    You're opening the file using `w`, *writing-only* mode. Even so, this won't do even if you changed that; you can't simply write into the middle of a file (at least without *overwriting* the rest). – deceze Sep 29 '16 at 14:55
  • See this answer: http://stackoverflow.com/a/2081880/11208 – Assaf Lavie Sep 29 '16 at 14:56
  • 1
    I think you would want `for i, line in enumerate(f)` in any case. – chepner Sep 29 '16 at 14:58

3 Answers3

2

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.
Hai Vu
  • 37,849
  • 11
  • 66
  • 93
1

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")
jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • How I get the "current" line from the file? – triple fault Sep 29 '16 at 15:04
  • There is no way to get the "current line" - if you are writting to it, you destroyed the previous file to start with. It is possible to open a file to read and write - but that is usually done with binary files, where you have fixed-size fields, not text. – jsbueno Sep 30 '16 at 22:21
  • If you want to generate a text file that just modifies some lines from another, already existing file the approach is to re-create another file with where you rewrite all the information, changing the needed information, and delete the old file and rename the new one when you are done - I will update the answer with an example of this approach – jsbueno Sep 30 '16 at 22:23
0

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)
Aif
  • 11,015
  • 1
  • 30
  • 44