22

I am trying to append a string to a file, if the string doesn't exit in the file. However, opening a file with a+ option doesn't allow me to do at once, because opening the file with a+ will put the pointer to the end of the file, meaning that my search will always fail. Is there any good way to do this other than opening the file to read first, close and open again to append?

In code, apparently, below doesn't work.

file = open("fileName", "a+")

I need to do following to achieve it.

file = open("fileName", "r")
... check if a string exist in the file
file.close()

... if the string doesn't exist in the file
file = open("fileName", "a")
file.write("a string")
file.close()
Yui Park
  • 289
  • 2
  • 4
  • 9

3 Answers3

38

To leave the input file unchanged if needle is on any line or to append the needle at the end of the file if it is missing:

with open("filename", "r+") as file:
    for line in file:
        if needle in line:
           break
    else: # not found, we are at the eof
        file.write(needle) # append missing data

I've tested it and it works on both Python 2 (stdio-based I/O) and Python 3 (POSIX read/write-based I/O).

The code uses obscure else after a loop Python syntax. See Why does python use 'else' after for and while loops?

Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • 6
    @glglgl: perhaps a downvoter is not familiar with the "`else` after a loop" syntax and they thought that I've used the indentation incorrectly. – jfs Feb 07 '15 at 18:00
  • 2
    good stuff. FIY as this was new to me; the else statement in conjunction with the for/while-loops is a concept called "completion clause" . – Stelios Apr 04 '18 at 13:16
  • Thanks. I also need to create file if it does not exists. However, using "a+" on this does not work. – Abiral Jan 21 '20 at 10:05
  • @Abiral: you could catch `FileNotFoundError` and create the new file. – jfs Jan 21 '20 at 17:42
  • Nice code. The .write() does not add a ('\n'), so I recommend `file.write(needle + '\n')` so its a valid text file. – run_the_race Mar 17 '22 at 15:12
8

You can set the current position of the file object using file.seek(). To jump to the beginning of a file, use

f.seek(0, os.SEEK_SET)

To jump to a file's end, use

f.seek(0, os.SEEK_END)

In your case, to check if a file contains something, and then maybe append append to the file, I'd do something like this:

import os

with open("file.txt", "r+") as f:
    line_found = any("foo" in line for line in f)
    if not line_found:
        f.seek(0, os.SEEK_END)
        f.write("yay, a new line!\n")
Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
Carsten
  • 17,991
  • 4
  • 48
  • 53
  • Thanks for this answer. I rearranged the contents of the file I was checking through and somehow the string I was doing the check for was getting duplicated in the file. I had to jump to the beginning of the file first using `f.seek(0, os.SEEK_SET)` before doing the check which is `line_found` in your case, then jump to the end of the file before writing to it. – Kosy Anyanwu May 16 '17 at 19:56
2

There is a minor bug in the previous answers: often, the last line in a text file is missing an ending newline. If you do not take that that into account and blindly append some text, your text will be appended to the last line.

For safety:

needle = "Add this line if missing"
with open("filename", "r+") as file:
    ends_with_newline = True
    for line in file:
        ends_with_newline = line.endswith("\n")
        if line.rstrip("\n\r") == needle:
           break
    else: # not found, we are at the eof
        if not ends_with_newline:
            file.write("\n")
        file.write(needle + "\n") # append missing data
Sam
  • 101
  • 1
  • 5