155

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.

4
  • 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. Commented Dec 6, 2013 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. Commented Dec 6, 2013 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 Commented Dec 6, 2013 at 20:36
  • @MarkkuK Gosh you're right, gotta love python for it simplicity. Thank you! Commented Dec 6, 2013 at 20:49

5 Answers 5

181

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.

Sign up to request clarification or add additional context in comments.

6 Comments

Upvote, though you need to prefix this suggestion with "if you're not concerned with the value reported by f.tell()".
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.
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.
Why a+ when you can just a?
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).
|
70

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()

8 Comments

Isn't this prone to a race condition?
why not just open it with mode "a+"?
what if we just use open(filename, 'a'), it also creates the file if does not exist and append if exists.
I prefer this method to the "try/except". Is personal preference more important than the fact that the EAFP style is generally considered better in Python?
@jdhao that may be true in Python 3, but when the question was asked, Python 2 was very common
|
58

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.

Comments

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+'.

Comments

2

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}')

1 Comment

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+'?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.