Why is this code just deleting the whole contents of the file? - python

I am making a code that checks if a certain user name is in a text file.
If it is, it stores the score. However, once it reaches more than 3 scores it deletes the oldest to maintain it at 3 scores.
Here is my code:
if userclass=="1":
filefordataclass1 = open("Class1scores.txt", "a"); #this opens/creates a new text file
filefordataclass1.write(str(username) + ":" + str(score))#this converts the
filefordataclass1.write("\n")
user_scores = {}
with open("Class1scores.txt", "r+")as file:
file.seek(0)
scores = file.readlines()
for line in scores:
name, scores = line.rstrip('\n').split(':',1)
if name not in user_scores:
user_scores[name] = deque(maxlen=3)
temp_q = user_scores[name]
temp_q.append(str(score))
user_scores[name] = temp_q
filehandle=open("Class1scores.txt", "w")
for key, values in user_scores.items():
filehandle.write(name + ',')
filehandle.write(','.join(list(values)) + '\n')
filehandle.close()# Initialize score list
filefordataclass1.close
If you can tell me what is wrong with the python code and how to fix it It would be much appreciated.

Don't chance your file multiple times. First read the contents, then add the new score, then write everything:
from collections import defaultdict, deque
if userclass=="1":
user_scores = defaultdict(lambda: deque(maxlen=3))
with open("Class1scores.txt", "r") as lines:
for line in lines:
name, scores = line.rstrip('\n').split(':',1)
user_scores[name].extend(scores.split(','))
user_scores[username].append(str(score))
with open("Class1scores.txt", "w") as output:
for key, values in user_scores.items():
filehandle.write('%s:%s\n' % (key, ','.join(list(values))))
Otherwise you are lost in searching for errors.

You should open the output file with "a" (append) instead of "w" (write).

no need to open the file again in write mode as you have already opened the file in read/write mode with r+.Use seek and truncate after storing the file data in variable. Code is as follows:
from collections import defaultdict, deque
userclass = "1"
if userclass=="1":
user_scores = defaultdict(lambda: deque(maxlen=3))
f = open("Class1scores.txt", "r+")
lines = f.readlines()
print lines
for line in lines:
name, scores = line.rstrip().split(':')
user_scores[name].extend(scores.split(','))
if len(user_scores) > 0:
f.seek(0)
f.truncate()
for key, values in user_scores.items():
f.write('%s:%s\n' % (key, ','.join(list(values))))
f.close()
hope this helps :)

Related

JSON file rewrites data after I re-run the program

I wanted to make a program that tracks the progress of our competition. I made a library containing our names as the key, and our wins as the value. I then made a JSON file to save the progress. But for some reason, when I re-run the program, it goes back to it's initial values and adds the new one; as if it was the first time I used the program.
Here is my code:
import os, json, sys
Numbers = {
"Peter" : 1,
"Drew" : 1,
}
def q1():
New_numbers = {}
q = input("Name? ")
if q not in Numbers:
Numbers[q] =1
with open("list.json", "w") as f:
json.dump(Numbers, f)
f.close()
with open("list.json", "r") as f:
New_numbers = json.load(f)
for key,value in New_numbers.items():
print(key, ":", value)
elif q in Numbers:
Numbers[q] += 1
with open("list.json", "w") as f:
json.dump(Numbers, f)
f.close()
with open("list.json", "r") as f:
New_numbers = json.load(f)
for key,value in New_numbers.items():
print(key, ":", value)
q1()
The first use, it works perfectly. However, as I've mentioned before, when I use it again, it loads the initial library; not the JSON file.

Code doesn't save to file

I have code which runs but doesn't save anything to the text file?
def saving_multiple_scores():
with open(class_number) as file:
dic = {}
for line in file:
key, value = line.strip().split(':')
dic.setdefault(key, []).append(value)
file.write(dic)
with open(class_number, 'a') as file:
for key, value in dic.items():
file.write(key + ':' + ','.join(value) + '\n')
print(dic)
It should check if the name is already in the file, and if so: append a score
and if not then create a new list with the score.
However nothing is saving at all.
Python, IDLE V3.4.2
I am new to this so any help is appreciated
the first with is not working because the file is empty and the for loop iterates over lines in file
The default for mode for open (see https://docs.python.org/2/library/functions.html#open) is read only so file.write(dic) won't work
Expanding on Pawel’s suggestion, here is what you do to fix your code:
from collections import defaultdict
def saving_multiple_scores():
with open(class_number, 'r') as f: # don't use file
data = defaultdict(list)
for line in f:
line = line.strip()
if not line:
continue # skip over any blank lines in the file
key, value = line.split(':')
data[key.strip()].append(value.strip())
# file.write removed because we don't write in readmode
with open(class_number, 'a') as f:
# using 'a' mode will append the score lists
# to the end of the file
# to overwrite the file completely, use 'w'
for key, value in data.items():
line = '%s:%s\n' % (key, ','.join(value),)
f.write(line)
print '%s' % line,
Sample input file:
alice:1
alice:2
alice:3
bob:1
alice:4
bob:2
Sample output file:
alice:1
alice:2
alice:3
bob:1
alice:4
bob:2
bob:1,2
alice:1,2,3,4

Python: Storing data in a text file and appending particular/individual lines

This is a computer science project that I've been working on.
Test scores are saved in a text file against a students name. for example:
Rob 21
Terry 12
Mike 33
I can get the program to do this but I want it to read the lines of the text file and identify if the name is already in existence. If so, it should add the next score onto the end of the line. If Terry takes the test again the scores should then read:
Rob 21
Terry 12 23
Mike 33
This is the relevant piece of code. It starts after the test is complete and the user has input their name, class and received their score.
import fileinput
print("Well done " +name+ ", your score is "+ (str(score)))
entry = (name +" "+ (str(score)))
if classchoice == "A":
classfile = open("classa.txt")
for line in classfile:
if name in line:
oldline = line
newline = (oldline+" "+(str(score)))
print (newline)
classfile.close()
else:
classfile = open("classb.txt","a")
classfile.write(entry)
classfile.write('\n')
classfile.close()
for line in fileinput.input("classa.txt", inplace=1):
line = line.replace(oldline,newline)
line = line.strip()
I'm having difficulty with this because:
The first part where it reads the lines in the files and finds the student name and results works but when I try to put the line together with the new score it ends up putting the new score underneath when it print (newline) so it looks like:
Terry 12
23
another issue is the else doesn't work. I get: local variable 'oldline' referenced before assignment
Could anyone help me with this. I'm new to python and this is a little overwhelming at the moment.
This is because when you read the file , and you get each line, it already has the newline (\n) at the end, so when you do -
newline = (oldline+" "+(str(score)))
oldline already has \n at the end. And hence you get something like - Name oldscore\n newscoe , and hence it comes on a new line.
You would need to strip off the previous newline before create the newline, Example -
newline = (oldline.rstrip()+" "+(str(score)))
--
Also, what you are doing seems to be very inefficient, you can directly use fileinput.input() for your case -
if classchoice == "A":
write_flag = True
with fileinput.input("classa.txt", inplace=1) as f:
for line in f:
line = line.rstrip()
if name in line:
line = line + " " + str(score)
write_flag = False
print(line)
#This is if `name` was never found, meaning we have to add the name to the file with the score.
if write_flag:
with open("classa.txt",'a') as f:
f.write("{} {}\n".format(name,score))
As indicated in the comments, using in would result in wrong entries getting updated. A way to overcome that would be to split the line and compare the first entry in the split -
if classchoice == "A":
write_flag = True
with fileinput.input("classa.txt", inplace=1) as f:
for line in f:
line = line.rstrip()
words = line.split()
if name == words[0]:
line = line + " " + str(score)
write_flag = False
print(line)
#This is if `name` was never found, meaning we have to add the name to the file with the score.
if write_flag:
with open("classa.txt",'a') as f:
f.write("{} {}\n".format(name,score))
Your logic and any logic using in is flawed and can harm your existing data, to see why:
infile:
stephen 12
paula 10
new_name, new_score = "paul",20
"paul" in "paula 10" -> True
infile after:
stephen 12
paula 10 20
Where is paul and why does paula now have two scores? Because you are matching substrings not exact names.
You are also using the wrong structure to store the data, use a dict with json:
To create the dict initially from your file use:
import csv
import json
with open("in.txt") as f:
r = csv.reader(f, delimiter=" ")
dct = {row[0]: row[1:] for row in r}
Then dump it to the file:
with open("in.txt", "w") as out:
json.dump(dct, out)
When you want to add new data just json.load and access using the key to add data or adding a new key/value pairing if the name does not exist:
new_name, new_score = "Terry", 20
with open("in.txt") as f:
dct = json.load(f)
dct[new_name] = new_score if new_name not in dct else dct[new_name] + [new_score]
with open("in.txt", "w") as out:
json.dump(dct, out)
in.txt will contain your updated data:
{"Mike": ["33"], "Rob": ["21"], "Terry": ["12", 20]}
If you are going to use a regular file then the correct approach is to check the name not using in, you can use a tempfile and shutil.move t0 update the original file:
from tempfile import NamedTemporaryFile
import csv
new_name, new_score = "Terry", 20
with open("in.txt") as f, NamedTemporaryFile("w",dir=".", delete=False) as out:
r = csv.reader(f, delimiter=" ")
wr = csv.writer(out, delimiter=" ")
found = False
for row in r:
# actually check the name for an exact match
if row[0] == new_name:
wr.writerow(row + [new_score])
found = True
else:
wr.writerow(row)
# if name is new, found will still be False
if not found:
wr.writerow([new_name, new_score])
# replace original file with updated
move(out.name, "in.txt")
Which will output:
Rob 21
Terry 12 20
Mike 33
Terry 20

Write to file python - Linebreak issue (\n)

I have a problem where the function just overwrites the line thats already there in a .txt file. The function is supposed to write a highscore to a file when the game quits (I have made a snake game by following a youtube tutorial). I can't quite figure out why it won't start on a new line, can anyone please explain the logic behind it, and how I fix it? I read somewhere that instead of "w" in f.open(), I should type "rb" or something. Since I'm kinda new to this "writing-to-file" thingy, I find it difficult.
Also, I want to sort the highscores from highest to lowest in the file (in other words, sort finalScore from highest to lowest). I have no idea how I should go on and code that, so I'd appreicate some help. You see, I want to print out the current highscores to the console (in order to make a scoreboad)
Heres the code:
import random
import time
name = "Andreas"
finalScore = random.randint(1,10)
def scoreToFile(finalScore):
#Has to be generated here, since we need the exact current time
currentTime = time.strftime("%c")
print("Sucsessfully logged score (finalScore) to highscores.txt")
f = open("highscores.txt", "w")
#fileOutput = [(currentTime, ":", name, "-", finalScore)]
fileOutput = [(finalScore, "-", name, currentTime)]
for t in fileOutput:
line = ' '.join(str(x) for x in t)
f.write(line + "\n")
f.close()
scoreToFile(finalScore)
Anyways, merry christmas my fellow python geeks! :D
1) one option is to open the file in append mode.
replace:
f = open("highscores.txt", "w")
with:
f = open("highscores.txt", "a")
2) another option is to replace this block,
f = open("highscores.txt", "w")
#fileOutput = [(currentTime, ":", name, "-", finalScore)]
fileOutput = [(finalScore, "-", name, currentTime)]
for t in fileOutput:
line = ' '.join(str(x) for x in t)
myfile.write(line + "\n")
f.close()
and use a with style
with open("highscores.txt", "a") as myfile:
#fileOutput = [(currentTime, ":", name, "-", finalScore)]
fileOutput = [(finalScore, "-", name, currentTime)]
for t in fileOutput:
line = ' '.join(str(x) for x in t)
myfile.write(line + "\n")
I prefer the second style as it is more safe and clean.
Mode w overwrites an existing file; mode 'a' appends to it. Also, the best way to handle a file is usually with the with statement, which ensures the closing on your behalf; so:
fileOutput = [(finalScore, "-", name, currentTime)]
with open("highscores.txt", "a") as f:
for t in fileOutput:
line = ' '.join(str(x) for x in t)
f.write(line + "\n")
For sorting, you need be able to extract the final score as a number from a line:
def minus_score(line):
return -int(line.split()[0])
then the total work will be done as:
def sorted_by_score():
with open("highscores.txt", "r") as f:
result = list(f)
return sorted(result, key=minus_score)
This will give you a list lines sorted in ascending order of score (the latter's the reason score is negating the number, though one might also choose to have it just return the number and reverse the sorting), for you to loop on and further process.
Added: so on the OP's request here's how the whole program might be (assuming the existence of a function that either plays a game and returns player name and final score, or else returns None when no more games are to be played and the program must exit).
import time
def play_game():
""" play a game and return name, finalscore;
return None to mean no more games, program finished.
THIS function you'd better code yourself!-)
"""
def scoreToFile(name, finalScore):
""" Add a name and score to the high-scores file. """
currentTime = time.strftime("%c")
fileOutput = finalScore, "-", name, currentTime
line = ' '.join(str(x) for x in fileOutput)
with open("highscores.txt", "a") as f:
f.write(line + "\n")
def minus_score(line):
""" just for sorting purposes, not called directly. """
return -int(line.split()[0])
def sorted_by_score():
""" return list of score lines sorted in descending order of score. """
with open("highscores.txt", "r") as f:
return sorted(f, key=minus_score)
def main():
while True:
game_result = play_game()
if game_result is None: break
scoreToFile(*game_result)
for line in sorted_by_score:
print(line.strip())
As others have mentioned, the problem is you're not opening the file in append mode, so it overwrites it every time rather than adding to it.
However, if you also want to keep the data in the file sorted, you do want to overwrite it each time, since the order of its contents will likely have been changed with the addition. To do that requires first reading it contents in, updating the data, and then writing it back out.
Here's a modified version of your function that does that. I also changed how the data in file is stored to what is known as Comma (or Character) Separated Values (CSV) format, because Python includes acsvmodule which makes it very easy to read, write, and do other things with such files.
import csv
import random
import time
highscores_filename = "highscores.txt"
HighScoresFirst = True # Determines sort order of data in file
def scoreToFile(name, finalScore):
currentTime = time.strftime("%c")
# Try reading scores from existing file.
try:
with open(highscores_filename, "r", newline='') as csvfile:
highscores = [row for row in csv.reader(csvfile, delimiter='-')]
except FileNotFoundError:
highscores = []
# Add this score to the end of the list.
highscores.append([str(finalScore), name, currentTime])
# Sort updated list by numeric score.
highscores.sort(key=lambda item: int(item[0]), reverse=HighScoresFirst)
# Create/rewrite highscores file from highscores list.
with open(highscores_filename, "w", newline='') as csvfile:
writer = csv.writer(csvfile, delimiter='-')
writer.writerows(highscores)
print("successfully logged score (finalScore) to highscores.txt")
# Simulate using the function several times.
name = "Name"
for i in range(1, 4):
finalScore = random.randint(1,10)
scoreToFile(name + str(i), finalScore)
time.sleep(random.randint(1,3)) # Pause so time values will vary.

Read and write to a list of names and scores - python

I am trying to create a program that gives the user a short quiz and create a score, which I have done, then I would like to add them to a list in a .txt file. In the program I will ask them their name, so say I have a list such as this;
Bob,7
Bill,5
Jane,6
and someone takes the quiz and inputs the name Bob and gets a score 4 the list will update to;
Bob,4
Bill,5
Jane,6
or someone new takes a quiz, Sarah it will change to;
Bob,4
Bill,5
Jane,6
Sarah,7
So far I have;
import random
file = open("scores.txt", "r")
UserScore=random.randint(0,10)
lines = file.readlines()
file.close()
student=input('What is your name? ')
file = open("scores.txt", "w")
for line in lines:
line = line.strip()
name, score = line.strip().split(",")
if name!=student:
file.write(line)
else:
file.write(name +',' +str(UserScore))
I've randomised the score for now to make it easier to read, however that will be from what the user answered correctly, and I thought this code would read the file then check each name from each line and if the name they entered is the same to the name in the list the line will be replaced with the name and score. However, the file just ends up blank, what am I doing wrong?
Here is what I think is a better idea using the Python pickle module:
In [1]: import pickle
In [2]: scores={'Bob':75, 'Angie':60, 'Anita':80} #create a dict called scores
In [3]: pickle.dump(scores,open('scores.dat','wb')) #dump the pickled object into the file
In [4]: !ls scores.dat #verify that the file has been created
scores.dat
In [5]: !cat scores.dat #list out the file
(dp0
S'Bob'
p1
I75
sS'Angie'
p2
I60
sS'Anita'
p3
I80
s.
In [9]: tscores = pickle.load(open('scores.dat','rb')) #Verification: load the pickled object from the file into a new dict
In [10]: tscores #Verification: list out the new dict
Out[10]: {'Angie': 60, 'Anita': 80, 'Bob': 75}
In [11]: scores == tscores #Verify that the dict object is equivalent to the newly created dict object
Out[11]: True
I tried your code and the first time you run it, then you rewrite the file in one single line. So the next time you run the script on this single line file, you get an unpack exception in the split function and hence you write nothing to the file, resulting in an empty file.
A solution could be to add the newline char again when writing the lines to the file.
import random
file = open("scores.txt", "r")
UserScore=random.randint(0,10)
lines = file.readlines()
file.close()
student=input('What is your name? ')
file = open("scores.txt", "w")
for line in lines:
line = line.strip()
name, score = line.strip().split(",")
if name!=student:
file.write(line + '\n')
else:
file.write(name +',' +str(UserScore) + '\n')
This should do what you want
import random
file = open("scores.txt", "r")
UserScore=random.randint(0,10)
lines = file.readlines()
file.close()
student=input('What is your name? ')
flag = True
file = open("scores.txt", "w")
for line in lines:
line = line.strip()
name, score = line.strip().split(",")
if name!=student:
file.write(line + '\n')
else:
file.write(name +',' +str(UserScore) + '\n')
flag = False
if flag:
file.write(student +',' +str(UserScore) + '\n')
I adjusted a bit of your code and took the liberty to remove the random part and name, score part. But I got some working code. I assume you can make it work for your situation.
file = open("scores.txt", "r+")
lines = file.readlines()
file.close()
us = 15
student = input('What is your name? ')
ls = []
file = open("scores.txt", "r+")
found_student = False
for line in lines:
line = line.strip()
ls = line.split(",")
print("Parsing: " + str(ls))
if not line:
print("Empty line")
pass
elif ls[0] != student:
file.write(line + "\n")
else:
found_student = True
file.write(ls[0] + ',' + str(us) + "\n")
if not found_student:
file.write(student + ',' + str(us) + "\n" )
file.close()

Categories

Resources