136

I have a program which writes a user's highscore to a text file. The file is named by the user when they choose a playername.

If the file with that specific username already exists, then the program should append to the file (so that you can see more than one highscore). And if a file with that username doesn't exist (for example, if the user is new), it should create a new file and write to it.

Here's the relevant, so far not working, code:

try: 
    with open(player): #player is the varible storing the username input
        with open(player, 'a') as highscore:
            highscore.write("Username:", player)

except IOError:
    with open(player + ".txt", 'w') as highscore:
        highscore.write("Username:", player)

The above code creates a new file if it doesn't exist, and writes to it. If it exists, nothing has been appended when I check the file, and I get no errors.

alex
  • 6,818
  • 9
  • 52
  • 103
Bondenn
  • 1,701
  • 3
  • 15
  • 19
  • What is the purpose of the first `with open(player):` statement? Also, in your exception handler, you are creating a different file, `player + '.txt'`, than you were trying to write to in the first place. – Markku K. Dec 06 '13 at 20:30
  • @MarkkuK I took that from another question at stackoverflow where you check if a file exist. So the with open(player): checks if the file exist, at least so I thought. Okey I see, just thought I had to use the ".txt" for the file to be a text-file. – Bondenn Dec 06 '13 at 20:33
  • 2
    in any case, you can just use `open(whatever,'a') as highscore:`, and it will do what you want: create the file if it does not exist, or append to it if it does exist – Markku K. Dec 06 '13 at 20:36
  • @MarkkuK Gosh you're right, gotta love python for it simplicity. Thank you! – Bondenn Dec 06 '13 at 20:49

5 Answers5

159

Have you tried mode 'a+'?

with open(filename, 'a+') as f:
    f.write(...)

Note however that f.tell() will return 0 in Python 2.x. See https://bugs.python.org/issue22651 for details.

Eric des Courtis
  • 5,135
  • 6
  • 24
  • 37
  • 2
    Upvote, though you need to prefix this suggestion with "if you're not concerned with the value reported by `f.tell()`". – personal_cloud Oct 06 '17 at 21:42
  • 1
    I'm concerned about portability of `a+` because of this in the docs: `on some Unix systems means that all writes append to the end of the file regardless of the current seek position`. – sleep Mar 26 '18 at 23:32
  • @JarrodSmith Not a real concern IMO. From docs: `'a' for appending (which on some Unix systems means that all writes append to the end of the file regardless of the current seek position).` I think this is fine for most uses of files (always appending for example). If you need to seek at specific offsets then you probably won't open files this way anyway. – Eric des Courtis Apr 10 '18 at 15:01
  • 1
    I've tried both `a` and `a+` on mac os high sierra on python 2.7.15 and both append to the end of the file, and `f.tell()` reports the same in both (not 0). The bug linked also has some portable workarounds, e.g. manually set the seek position to the end of the file, or use the `io` module instead which doesn't have that bug. – Davos Aug 28 '18 at 06:18
  • 1
    Why `a+` when you can just `a`? – Mateen Ulhaq Oct 05 '20 at 04:33
  • 2
    This is most correct. If the file does not exist, 'a+' will create it whereas 'a' will fail with a file not found exception (using Python 3.7 on Raspian). – tomwhipple Apr 21 '21 at 14:48
67

It's not clear to me exactly where the high-score that you're interested in is stored, but the code below should be what you need to check if the file exists and append to it if desired. I prefer this method to the "try/except".

import os
player = 'bob'

filename = player+'.txt'

if os.path.exists(filename):
    append_write = 'a' # append if already exists
else:
    append_write = 'w' # make a new file if not

highscore = open(filename,append_write)
highscore.write("Username: " + player + '\n')
highscore.close()
qmorgan
  • 4,794
  • 2
  • 19
  • 14
  • 15
    Isn't this prone to a race condition? – user541686 Apr 21 '17 at 08:21
  • 31
    why not just open it with mode "a+"? – Eric des Courtis Sep 25 '17 at 17:32
  • 37
    what if we just use `open(filename, 'a')`, it also creates the file if does not exist and append if exists. – Abolfazl Feb 12 '18 at 13:08
  • @EricdesCourtis "a" and "a+" will not let you seek to arbitrary offsets (only to the end of the file). – naktinis Feb 04 '20 at 19:09
  • 2
    _I prefer this method to the "try/except"._ Is personal preference more important than the fact that the [EAFP](https://docs.python.org/3/glossary.html?highlight=eafp#term-eafp) style is generally considered better in Python? – AMC Jul 13 '20 at 22:58
  • It seems that using `open(file, 'a')` is enough to append to a file (if the file does not exit, it will be created). Tested on Python 3.6 in CentOS. – jdhao Jan 16 '21 at 05:15
  • 1
    @jdhao that may be true in Python 3, but when the question was asked, Python 2 was very common – Chris H Jan 18 '21 at 10:31
  • 1
    append_write = ( 'a' if os.path.exists(filename) else 'w') – user1438233 May 09 '21 at 20:20
52

Just open it in 'a' mode:

a   Open for writing. The file is created if it does not exist. The stream is positioned at the end of the file.

with open(filename, 'a') as f:
    f.write(...)

To see whether you're writing to a new file, check the stream position. If it's zero, either the file was empty or it is a new file.

with open('somefile.txt', 'a') as f:
    if f.tell() == 0:
        print('a new file or the file was empty')
        f.write('The header\n')
    else:
        print('file existed, appending')
    f.write('Some data\n')

If you're still using Python 2, to work around the bug, either add f.seek(0, os.SEEK_END) right after open or use io.open instead.

user
  • 23,260
  • 9
  • 113
  • 101
3

Notice that if the file's parent folder doesn't exist you'll get the same error:

IOError: [Errno 2] No such file or directory:

Below is another solution which handles this case:
(*) I used sys.stdout and print instead of f.write just to show another use case

# Make sure the file's folder exist - Create folder if doesn't exist
folder_path = 'path/to/'+folder_name+'/'
if not os.path.exists(folder_path):
     os.makedirs(folder_path)

print_to_log_file(folder_path, "Some File" ,"Some Content")

Where the internal print_to_log_file just take care of the file level:

# If you're not familiar with sys.stdout - just ignore it below (just a use case example)
def print_to_log_file(folder_path ,file_name ,content_to_write):

   #1) Save a reference to the original standard output       
    original_stdout = sys.stdout   
    
    #2) Choose the mode
    write_append_mode = 'a' #Append mode
    file_path = folder_path + file_name
    if (if not os.path.exists(file_path) ):
       write_append_mode = 'w' # Write mode
     
    #3) Perform action on file
    with open(file_path, write_append_mode) as f:
        sys.stdout = f  # Change the standard output to the file we created.
        print(file_path, content_to_write)
        sys.stdout = original_stdout  # Reset the standard output to its original value

Consider the following states:

'w'  --> Write to existing file
'w+' --> Write to file, Create it if doesn't exist
'a'  --> Append to file
'a+' --> Append to file, Create it if doesn't exist

In your case I would use a different approach and just use 'a' and 'a+'.

Rot-man
  • 18,045
  • 12
  • 118
  • 124
0

Using the pathlib module (python's object-oriented filesystem paths)

Just for kicks, this is perhaps the latest pythonic version of the solution.

from pathlib import Path 

path = Path(f'{player}.txt')
path.touch()  # default exists_ok=True
with path.open('a') as highscore:
   highscore.write(f'Username:{player}')
ohailolcat
  • 891
  • 8
  • 13
  • 1
    This answer's clean and relatively new compared to the rest. @ohailolcat - what makes it more pythonic, exactly? Not that this isn't pythonic, but it also does require importing a new module. Why not just 'a+'? – JayRugMan Aug 05 '21 at 19:21