deleting a line from a file

Mark Wooding mdw at distorted.org.uk
Mon Mar 31 14:10:03 EDT 2008


Paul Rubin <http> wrote:
> You could do it "in place" in all those systems afaik, either opening
> the file for both reading and writing, or using something like mmap.
> Basically you'd leave the file unchanged up to line N, then copy lines
> downward starting from line N+1.  At the end you'd use ftrunc to
> shrink the file, getting rid of the duplicate last line.

Making a new copy and renaming it when you're finished is probably both
easier (don't have to keep seeking about all the time) and more reliable
(doesn't leave your file corrupted if you crash half-way through).

Is there a standard wossname which does this?

from __future__ import with_statement
from contextlib import contextmanager
import os, sys, errno

def fresh_file(base, mode = 'w'):
  """
  Return a file name and open file handle for a fresh file in the same
  directory as BASE.
  """
  for seq in xrange(50):
    try:
      name = '%s.new.%d' % (base, seq)
      fd = os.open(name, os.O_WRONLY | os.O_CREAT | os.O_EXCL)
      f = os.fdopen(fd, mode)
      return name, f
    except OSError, err:
      if err.errno == errno.EEXIST:
        pass
      else:
        raise
  raise IOError(errno.EEXIST, os.strerror(errno.EEXIST), base)

@contextmanager
def safely_writing(filename, mode = 'w'):
  """
  Context manager for updating files safely.

  It produces a file object.  If the controlled suite completes successfully,
  the file named by FILENAME is atomically replaced by the material written
  to the file object; otherwise the file is left alone.

  Safe in the presence of multiple simultaneous writers, in the sense that
  the resulting file is exactly the output of one of the writers (chosen
  nondeterministically).
  """
  f = None
  newname = None
  try:
    newname, f = fresh_file(filename, mode)
    yield f
    f.close()
    f = None
    os.rename(newname, filename)
  finally:
    if f is not None:
      f.close()
    if newname is not None:
      try:
        os.unlink(newname)
      except:
        pass

It seems like an obvious thing to want.

(Extra messing about will be needed on Windows, which doesn't have
proper atomic-rename semantics.  Playing with the transactional
filesystem stuff is left as an exercise to the interested student.)

-- [mdw]



More information about the Python-list mailing list