18

In python, there are a few flags you can supply when opening a file for operation. I am a bit baffled at finding a combination that allow me to do random write without truncating. The behavior I am looking for is equivalent to C: create it if it doesn't exist, otherwise, open for write (not truncating)

open(filename, O_WRONLY|O_CREAT)

Python's document is confusing (to me): "w" will truncate the file first, "+" is supposed to mean updating, but "w+" will truncate it anyway. Is there anyway to achieve this without resorting to the low-level os.open() interface?

Note: the "a" or "a+" doesn't work either (please correct if I am doing something wrong here)

cat test.txt
eee

with open("test.txt", "a+") as f:
  f.seek(0)
  f.write("a")
cat test.txt
eeea

Is that so the append mode insist on writing to the end?

Oliver
  • 3,592
  • 8
  • 34
  • 37

4 Answers4

12

You can do it with os.open:

import os
f = os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), 'rb+')

Now you can read, write in the middle of the file, seek, and so on. And it creates the file. Tested on Python 2 and 3.

RemcoGerlich
  • 30,470
  • 6
  • 61
  • 79
0

You should try reading the file then open writing mode, as seen here:

with open("file.txt") as reading:
    r = reading.read()
with open("file.txt", "w") as writing:
    writing.write(r)
HellishBro
  • 11
  • 3
0

According to the discussion Difference between modes a, a+, w, w+, and r+ in built-in open function, the open with a mode will always write to the end of file irrespective of any intervening fseek(3) or similar.

If you only want to use python built-in function. I guess the solution is to first check if the file exist, and then open with r+ mode.

For Example:

import os
filepath = "test.txt"
if not os.path.isfile(filepath):
    f = open(filepath, "x") # open for exclusive creation, failing if the file already exists
    f.close()
with open(filepath, "r+") as f: # random read and write
    f.seek(1)
    f.write("a")
kowalski
  • 11
  • 4
-2

You need to use "a" to append, it will create the file if it does not exist or append to it if it does.

You cannot do what you want with append as the pointer automatically moves to the end of the file when you call the write method.

You could check if the file exists then use fileinput.input with inplace=True inserting a line on whichever line number you want.

import fileinput
import os


def random_write(f, rnd_n, line):
    if not os.path.isfile(f):
        with open(f, "w") as f:
            f.write(line)
    else:
        for ind, line in enumerate(fileinput.input(f, inplace=True)):
            if ind == rnd_n:
                print("{}\n".format(line) + line, end="")
            else:
                print(line, end="")

http://linux.die.net/man/3/fopen

a+ Open for reading and appending (writing at end of file). The file is created if it does not exist. The initial file position for reading is at the beginning of the file, but output is always appended to the end of the file.

fileinput makes a f.bak copy of the file you pass in and it is deleted when the output is closed. If you specify a backup extension backup=."foo" the backup file will be kept.

Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
  • 1
    see the example provided to show append mode doesn't allow you to do random write. – Oliver Mar 07 '15 at 18:22
  • so you want to write to the file on a random line? – Padraic Cunningham Mar 07 '15 at 18:27
  • yes, without going into too much details on use case - think of parallel write in cluster environment. – Oliver Mar 07 '15 at 18:30
  • 1
    I don't think it is possible with append, the pointer is always moved to the end of the file when you call write even if you seek – Padraic Cunningham Mar 07 '15 at 18:32
  • Downvoting, as **this is dangerous**. `open(f, "w")` will nuke a file which happens to show up after `os.path.isfile(f)`. Classical race condition. – Tino Oct 15 '19 at 22:34
  • Note to reviewers in the Very Low Quality queue: [wrong answers are not Very Low Quality](https://meta.stackoverflow.com/questions/345023/are-blatantly-wrong-answers-very-low-quality). That's what downvotes and comments are for. – EJoshuaS - Stand with Ukraine Sep 23 '21 at 18:36