20

I'm doing it like this now, but I want it to write at the beginning of the file instead.

f = open('out.txt', 'a') # or 'w'?
f.write("string 1")
f.write("string 2")
f.write("string 3")
f.close()

so that the contents of out.txt will be:

string 3
string 2
string 1

and not (like this code does):

string 1
string 2
string 3
martineau
  • 119,623
  • 25
  • 170
  • 301
kristus
  • 447
  • 3
  • 7
  • 11

4 Answers4

14

Take a look at this question. There are some solutions there.

Though I would probably go that same way Daniel and MAK suggest -- maybe make a lil' class to make things a little more flexible and explicit:

class Prepender:

    def __init__(self, fname, mode='w'):
        self.__write_queue = []
        self.__f = open(fname, mode)

    def write(self, s):
        self.__write_queue.insert(0, s)

    def close(self):
        self.__exit__(None, None, None)

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        if self.__write_queue: 
            self.__f.writelines(self.__write_queue)
        self.__f.close()

with Prepender('test_d.out') as f:
    f.write('string 1\n')
    f.write('string 2\n')
    f.write('string 3\n')
Community
  • 1
  • 1
ktdrv
  • 3,602
  • 3
  • 30
  • 45
7

You could throw a f.seek(0) between each write (or write a wrapper function that does it for you), but there's no simple built in way of doing this.

EDIT: this doesn't work, even if you put a f.flush() in there it will continually overwrite. You may just have to queue up the writes and reverse the order yourself.

So instead of

f.write("string 1")
f.write("string 2")
f.write("string 3")

Maybe do something like:

writeList = []
writeList.append("string 1\n")
writeList.append("string 2\n")
writeList.append("string 3\n")
writeList.reverse()
f.writelines(writeList)
Daniel DiPaolo
  • 55,313
  • 14
  • 116
  • 115
2

A variation on kdtrv's answer. This version keeps the existing file contents, and offers a write_lines method that preserves line order.

class Prepender(object):
    def __init__(self,
                 file_path,
                ):
        # Read in the existing file, so we can write it back later
        with open(file_path, mode='r') as f:
            self.__write_queue = f.readlines()

        self.__open_file = open(file_path, mode='w')

    def write_line(self, line):
        self.__write_queue.insert(0,
                                  "%s\n" % line,
                                 )

    def write_lines(self, lines):
        lines.reverse()
        for line in lines:
            self.write_line(line)

    def close(self):
        self.__exit__(None, None, None)

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        if self.__write_queue:
            self.__open_file.writelines(self.__write_queue)
        self.__open_file.close()


with Prepender('test_d.out') as f:
    # Must write individual lines in reverse order
    f.write_line('This will be line 3')
    f.write_line('This will be line 2')
    f.write_line('This will be line 1')

with Prepender('test_d.out') as f:
    # Or, use write_lines instead - that maintains order.
    f.write_lines(
        ['This will be line 1',
         'This will be line 2',
         'This will be line 3',
        ]
    )
Christian Long
  • 10,385
  • 6
  • 60
  • 58
1

Elaborating on Daniel DiPaolo's answer:

Simply append all the lines that you want to write to a list. Reverse the list and then write its contents into the file.

f=open('output.txt','w')

l=[]
l.append("string 1")
l.append("string 2")
l.append("string 3")

for line in l:
    f.write(line)

f.close()

You could also use a deque and add lines at its beginning instead of using a list and reversing it.

MAK
  • 26,140
  • 11
  • 55
  • 86
  • What if the usage for this was more like a log file, where the script may write to the log on successive executions? Is the only recourse to hold the entire file in memory, seek to the beginning to write your stuff followed by all the previous contents? (excluding creating intermediate files) – Nick T Apr 20 '10 at 18:49
  • I'm not aware of anything in Python's `file` class that allows for something like this directly (i.e. without using some sort of intermediate buffer). So, right now, this looks like the simplest way. For a log file, wouldn't it make a lot more sense and make life a lot simpler simply to append the newest entries to the end of the file? – MAK Apr 20 '10 at 19:13
  • @Nick, @MAK, Not only is there nothing in Python that can handle this, there's nothing in the underlying C library or the filesystem. This is simply not how files operate, and you can't grow a file other than by appending unless you're willing to copy the content around. It could be done with a new file, or by moving data within the old file, but either way it's very likely a bad idea. Better change the requirements or the way you view the problem... – Peter Hansen Apr 20 '10 at 23:09