Python Forum

Full Version: How to save files in a separate directory
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hey, i'm working on a games save/loading function.
this is my code that find the file path for the players save (loading the save)
it works by finding where the file is being run from, then replacing the program with the playerdata subfolder so it can find the save files.

 absolutepath = os.path.abspath(__file__)
      filename = os.path.basename(absolutepath)
      dirname = absolutepath.replace(filename, "playerdata")
      filename = dirname + "\\username.txt"
      file = open(filename, 'r')
      player = file.read()
      file.close()
      absolutepath = os.path.abspath(__file__)
      filename = os.path.basename(absolutepath)
      dirname = absolutepath.replace(filename, "playerdata")
      filename = dirname + "\\coins.txt"
      file = open(filename, 'r')
      coins = file.read()
      file.close()
how can I use this system to save the file?
this is an example of code I would use to save files if they were in the same directory as the program.

                os.remove('username.txt')
                file = open('username.txt', 'a+')
                file.write(player)
                file.close()
                os.remove('coins.txt')
                file = open('coins.txt', 'a+')
                coins = str(coins)
                file.write(coins)
                file.close()


TL;DR it deletes the text files and replaces them with a new save - but as the files are in a subfolder it cannot locate them - how do i get it to save to a certain directory?
Do you need a subfolder, or are you only using a subfolder because you have multiple files? I would save username and coins to a single file. In the code below I write both username and coins to the same file. And to cover all bases the file is in a subfolder of the module folder.
import pathlib

username = "Guido"
coins = 1000000

save_file = "save_files/data.txt"  # data save file relative path
with open(pathlib.Path(__file__).parent / save_file, "w") as file:
    file.write(f"{username}\n{coins}")

username = None
coins = None

with open(pathlib.Path(__file__).parent / save_file, "r") as file:
    username, coins = map(str.strip, file.readlines())
coins = int(coins) + 1
print(username, coins)
__file__ is the file name and path of the module currently running. pathlib.Path(__file__).parent strips off the filename. pathlib.Path(__file__).parent / save_file appends the subfolder and scores filename to the path.

I open the file for writing ("w" resets the file to empty) and write the username and coins separated by a linefeed. The resulting file looks like this:
Output:
Guido 1000000
To read the file back in I just do pretty much the same thing, but opening the file for reading "r" and read the lines. Because the file contains a linefeed after Guido, username has a linefeed at the end that we need to strip. Coins will be read in as a str and needs to be converted back to an int.

It is awkward that I have to strip the linefeed from the username and convert the coins to an int. This method wouldn't scale well to multiple users and multiple coins either. Maybe username and coins in your example are just that, examples, and in your real application you have a file full of usernames that map to a file full of coins. In that case I would still use a single file, but a different kind of file:

To save coins for multiple players you could use a CSV file.
import pathlib
import csv

players = {
   "Guido":1000000,
   "Dean":1,
   "Scordomaniac": 100
}

save_file = "save_files/data.csv"

print("Initial players", players)
with open(pathlib.Path(__file__).parent / save_file, "w", newline="") as file:
    writer = csv.writer(file)
    for player in players.items():
        writer.writerow(player)

players = {}
print("Before load", players)

with open(pathlib.Path(__file__).parent / save_file, "r") as file:
    reader = csv.reader(file)
    for player, coins in reader:
        players[player] = int(coins)
players["Guido"] += 1
print("After load", players)
This produces a file that looks like this:
Output:
Guido,1000000 Dean,1 Scordomaniac,100
And when I run my program I see that it accurately restores the player's scores.
Output:
Initial players {'Guido': 1000000, 'Dean': 1, 'Scordomaniac': 100} Before load {} After load {'Guido': 1000001, 'Dean': 1, 'Scordomaniac': 100}
It's nice that I don't have to strip the linefeeds, and the file is easy to read with player names and scores on the same line. But I still have to convert coins back to a string.

There are file types that save the data type information and restore the type automatically. My favorite of these is JSON because the files are relatively short and human readable when compared to something like a CSV.
import pathlib
import json

players = {
   "Guido":1000000,
   "Dean":1,
   "Scordomaniac": 100
}

save_file = "save_files/data.json"

print("Initial players", players)
with open(pathlib.Path(__file__).parent / save_file, "w") as file:
    json.dump(players, file)

players = None
print("Before load", players)

with open(pathlib.Path(__file__).parent / save_file, "r") as file:
    players = json.load(file)
players["Guido"] += 1
print("After load", players)
This produces a file that looks like this:
{"Guido": 1000000, "Dean": 1, "Scordomaniac": 100}
And when I run the program I see the player's scores are restored.
Output:
Initial players {'Guido': 1000000, 'Dean': 1, 'Scordomaniac': 100} Before load None After load {'Guido': 1000001, 'Dean': 1, 'Scordomaniac': 100}
Always tip Guido.
i've tried using the json code you sent but i get this error:
Error:
Traceback (most recent call last): File "O:\Jackpot Studios\PyMemer BETA\saving.py", line 13, in <module> with open(pathlib.Path(__file__).parent / playerdata, "w") as file: TypeError: invalid file: WindowsPath('O:/Jackpot Studios/PyMemer BETA/playerdata/data.json')
how can this be fixed? all the files exist...
Which version of Python are you using? Try this perhaps
with (pathlib.Path(__file__).parent / 'playerdata' / 'data.json').open("w") as file: