11

My aim is to read line from the file , strip the blank spaces at the end of it and write back into the same file. I have tried the following code:

with open(filename, 'r+') as f:
    for i in f:
        f.write(i.rstrip()+"\n")

This seems to write at the end of the file, keeping initial data in the file intact . I know that using f.seek(0) would take the pointer back to start of the file , which I am assuming would be somehow required for this solution.

Can you please advise if there is different approach for this or am I on the right patch just need to add more logic into the code?

misguided
  • 3,699
  • 21
  • 54
  • 96

2 Answers2

7

Use a temporary file. Python provides facilities for creating temporary files in a secure manner. Call example below with: python modify.py target_filename

 import tempfile
 import sys

 def modify_file(filename):

      #Create temporary file read/write
      t = tempfile.NamedTemporaryFile(mode="r+")

      #Open input file read-only
      i = open(filename, 'r')

      #Copy input file to temporary file, modifying as we go
      for line in i:
           t.write(line.rstrip()+"\n")

      i.close() #Close input file

      t.seek(0) #Rewind temporary file to beginning

      o = open(filename, "w")  #Reopen input file writable

      #Overwriting original file with temporary file contents          
      for line in t:
           o.write(line)  

      t.close() #Close temporary file, will cause it to be deleted

 if __name__ == "__main__":
      modify_file(sys.argv[1])

References here: http://docs.python.org/2/library/tempfile.html

JonnyRo
  • 1,844
  • 1
  • 14
  • 20
  • Nice example and great explanation. – misguided Jul 15 '13 at 05:00
  • Thanks. I like using temporary files even if memory is available. Also check out the spooled temporary file in the tempfile docs, which gives an interesting compromise. – JonnyRo Jul 15 '13 at 17:50
4

The problem with your approach is that you need both an input stream and an output stream, which can point at different places in the same file. If you want to use f.seek() then you will need to store the position using f.tell() after each read and write. For example:

f = open(filename, 'r+')
while True:
    i = f.readline()
    if i == '': break
    in = f.tell()
    f.seek(out)
    f.write(i.rstrip()+"\n")
    out = f.tell()
    f.seek(in)

But that's confusing and prone to errors. If the file isn't too big, why not read it all into memory and then write it back out again?

in = open(filename, 'r')
lines = in.read()
in.close()
out = open(filename, 'w')
out.write([line.rstrip()+'\n' for line in lines.split('\n')])
out.close()

If the file is too large to fit into memory, then write the lines to a temporary file and then rename the file when you are done:

out = open(filename+'.tmp', 'w')
with open(filename, 'r') as f:
    for i in f:
        out.write(i.rstrip()+"\n")
out.close()
os.rename(filename+'.tmp', filename)
Brent Washburne
  • 12,904
  • 4
  • 60
  • 82
  • your last solution seems to almost solve the issue , the only issue remaining being , I want the new file name to to change i.e, the new file should replace the old file. If I do it by the method specified above , I get `WindowsError: [Error 183] Cannot create a file when that file already exists` – misguided Jul 15 '13 at 04:52
  • 2
    That's easy, simply remove the old file after closing it but before renaming it: `os.remove(filename)` – Brent Washburne Jul 15 '13 at 05:18
  • hi I decided to go with method 2, storing the contents of the file into memory but when I am executing the code I am getting the error `TypeError: write() argument must be str, not list` can you please help? – Parik Tiwari Nov 06 '18 at 18:41