how to replace (update) text in a file line by line

I am trying to replace text in a text file by reading each line, testing it, then writing if it needs to be updated. I DO NOT want to save as a new file, as my script already backs up the files first and operates on the backups.

Here is what I have so far... I get fpath from os.walk() and I guarantee that the pathmatch var returns correctly:

fpath = os.path.join(thisdir, filename)
with open(fpath, 'r+') as f:
    for line in f.readlines():
        if '<a href="' in line:
            for test in filelist:
                pathmatch = file_match(line, test)
                    if pathmatch is not None: 
                        repstring = filelist[test] + pathmatch
                        print 'old line:', line
                        line = line.replace(test, repstring)
                        print 'new line:', line
                        f.write(line)

But what ends up happening is that I only get a few lines (updated correctly, mind you, but repeated from earlier in the file) corrected. I think this is a scoping issue, afaict.

*Also: I would like to know how to only replace the text upon the first instance of the match, for ex., I don't want to match the display text, only the underlying href.


First, you want to write the line whether it matches the pattern or not. Otherwise, you're writing out only the matched lines.

Second, between reading the lines and writing the results, you'll need to either truncate the file (can f.seek(0) then f.truncate()), or close the original and reopen. Picking the former, I'd end up with something like:

fpath = os.path.join(thisdir, filename)
with open(fpath, 'r+') as f:
    lines = f.readlines()
    f.seek(0)
    f.truncate()
    for line in lines:
        if '<a href="' in line:
            for test in filelist:
                pathmatch = file_match(line, test)
                    if pathmatch is not None: 
                        repstring = filelist[test] + pathmatch
                        line = line.replace(test, repstring)
        f.write(line)

  1. Open the file for read and copy all of the lines into memory. Close the file.
  2. Apply your transformations on the lines in memory.
  3. Open the file for write and write out all the lines of text in memory.

with open(filename, "r") as f:
    lines = (line.rstrip() for line in f)
    altered_lines = [some_func(line) if regex.match(line) else line for line in lines]
with open(filename, "w") as f:
    f.write('\n'.join(altered_lines) + '\n')

A (relatively) safe way to replace a line in a file.

#!/usr/bin/python 
# defensive programming style
# function to replace a line in a file
# and not destroy data in case of error

def replace_line(filepath, oldline, newline ):
  """ 
  replace a line in a temporary file, 
  then copy it over into the 
  original file if everything goes well

  """

 # quick parameter checks 
  assert os.exists(filepath)          # ! 
  assert ( oldline and str(oldline) ) # is not empty and is a string
  assert ( newline and str(newline) )

  replaced = False
  written  = False

  try:

    with open(filepath, 'r+') as f:    # open for read/write -- alias to f       

      lines = f.readlines()            # get all lines in file

      if oldline not in lines:
          pass                         # line not found in file, do nothing

      else:
        tmpfile = NamedTemporaryFile(delete=True)  # temp file opened for writing

        for line in lines:           # process each line
          if line == oldline:        # find the line we want 
            tmpfile.write(newline)   # replace it 
            replaced = True  
          else:
            tmpfile.write(oldline)   # write old line unchanged

        if replaced:                   # overwrite the original file     
          f.seek(0)                    # beginning of file
          f.truncate()                 # empties out original file

          for tmplines in tmpfile: 
            f.write(tmplines)          # writes each line to original file
          written = True  

      tmpfile.close()              # tmpfile auto deleted    
      f.close()                          # we opened it , we close it 

  except IOError, ioe:                 # if something bad happened.
    printf ("ERROR" , ioe)
    f.close()                        
    return False

  return replaced and written        # replacement happened with no errors = True 

(note: this replaces entire lines only , and all of the lines that match in the file)