I am attempting to read in individual elements of a file. In this example, the first element of each line is to be the key of a dictionary. The next five elements will be a corresponding value for said key in list form.
max_points = [25, 25, 50, 25, 100]
assignments = ['hw ch 1', 'hw ch 2', 'quiz ', 'hw ch 3', 'test']
students = {'#Max': max_points}
def load_records(students, filename):
#loads student records from a file
in_file = open(filename, "r")
#run until break
while True:
#read line for each iteration
in_line = in_file.readline()
#ends while True
if not in_line: break
#deletes line read in
in_line = in_line[:-1]
#initialize grades list
grades = [0]*len(students['#Max'])
#set name and grades
name, grades[0], grades[1], grades[2], grades[3], grades[4] = in_line.split()
#add names and grades to dictionary
students[name] = grades
print name, students[name]
filename = 'C:\Python27\Python_prgms\Grades_list.txt'
print load_records(students, filename)
The method I have now is extremely caveman, and I would like to know what the more elegant, looping method would be. I have been looking for a while, but I can't seem to find the correct method of iteration. Help a brotha out.
Another way of doing it:
def load_records(students, filename):
with open(filename) as f:
for line in f:
line = line.split()
name = line[0]
students[name] = map(int, line[1:])
print name, students[name]
It seems a bit strange that the student dictionary contains both the scores and a parameter #Max though - a key has two meanings, is it a student's name or parameter's name? Might be better to separate them.
I had an assignment similar to this last year.
def load_records(students, filename):
file = open(filename, 'r')
s = ""
while s != None: # loop until end of file is reached
s = file.readline()
# manipulate s how you need
Also, you should use inline comments like above, it makes the code much easier to read compared to how you have it now.
Related
points = "temp"
a = "temp"
f = "temp"
def pointincrementer():
global points
points = 0
for line in f:
for word in a:
if word in line:
scorelen = int(len(user+","))
scoreval = line[0:scorelen]
isolatedscore = line.replace(scoreval,'')
if "," in line:
scorestr = isolatedscore.replace(",","")
score = int(scorestr)
points = score + 1
print(points)
def score2():
f = open('test.txt','r')
a = [user]
lst = []
for line in f:
for word in a:
if word in line:
pointincrementer()
print(points)
point = str(points)
winning = (user+","+point+","+"\n")
line = line.replace(line,winning)
lst.append(line)
f.close()
f = open('test.txt','w')
for line in lst:
f.write(line)
f.close()
print("Points updated")
user = input("Enter username: ") #change so user = winners userid
with open('test.txt') as myfile:
if user in myfile.read():
score2()
else:
f = open('test.txt','r')
f2 = f.read()
f3 = (f2+"\n"+user)
f.close()
f = open('test.txt','w')
f.write(f3)
f.close()
score2()
This is paired with test.txt, which looks like this:
one,1,
two,5,
three,4,
four,94,
When this code is run, it it will ask the user their name (as expected) and then will print 0 (when it should instead print the user's score) and then Points updated. Anybody know how to sort this out?
There are many problems with your code. You should not be using global variables like that. Each function should be passed what it needs, do its computing, and return values for the caller to handle. You should not be reading the file multiple times. And you can't write the file while you still have it open with the with statement.
Here, I read the file at the beginning into a Python dictionary. The code just updates the dictionary, then writes it back out at the end. This makes for a simpler and more maintainable structure.
def readdata(fn):
data = {}
for row in open(fn):
info = row.strip().split(',')
data[info[0]] = int(info[1])
return data
def writedata(fn,data):
f = open(fn,'w')
for k,v in data.items():
print( f"{k},{v}", file=f )
def pointincrementer(data,user):
return data[user] + 1
def score2(data, user):
points = pointincrementer(data, user)
print(points)
data[user] = points
print("Points updated")
user = input("Enter username: ")
data = readdata( 'test.txt' )
if user not in data:
data[user] = 0
score2(data, user)
writedata( 'test.txt', data )
The f in pointincrementer() refers to the "temp" string declared on the third line. The f in score2() refers to the file handle declared immediately below the function header. To get around this, you can pass the file handle into pointincrementer():
def pointincrementer(file_handle):
global points
points = 0
for line in file_handle:
for word in a:
if word in line:
scorelen = int(len(user+","))
scoreval = line[0:scorelen]
isolatedscore = line.replace(scoreval,'')
if "," in line:
scorestr = isolatedscore.replace(",","")
score = int(scorestr)
points = score + 1
print(points)
def score2():
file_handle = open('test.txt','r')
a = [user]
lst = []
for line in f:
print(line)
for word in a:
if word in line:
pointincrementer(file_handle)
print(points)
point = str(points)
winning = (user+","+point+","+"\n")
line = line.replace(line,winning)
lst.append(line)
f.close()
f = open('test.txt','w')
for line in lst:
f.write(line)
f.close()
print("Points updated")
This leads to a parsing error. However, as you haven't described what each function is supposed to do, this is the limit to which I can help. (The code is also extremely difficult to read -- the lack of readability in this code snippet is likely what caused this issue.)
This is currently my code for reading through a CSV file, Creating a person object, and adding each person to a list. One line Example input: John,Langley,1,2,2,3,5
When i print(per) each time after creating a person object. My output is correct, but as soon as i add that person to the list i made, the numeric values AKA 'traits' for that person are all the same as the last persons traits in the CSV file.
For Example:
John,Langley,1,2,2,3,5 --(add to list)-->John,Langley,1,1,1,1,1
Isabel,Smith,3,2,4,4,0 --(add to list)-->Isabel,Smith,1,1,1,1,1
John,Doe,1,1,1,1,1 --(add to list)-->John,Doe,1,1,1,1,1
This is impacting me with continuing because i need the person objects' traits to be valid in order to perform analysis on them in the next couple methods. PLEASE IGNORE MY PRINT STATEMENTS. THEY WERE FOR MY DEBUGGING PURPOSES
def read_file(filename):
file = open(filename, "r", encoding='utf-8-sig')
Traits_dict = {}
pl = []
next(file)
for line in file:
line = line.rstrip('\n')
line = line.split(',')
first = str(line[0].strip())
last = str(line[1].strip())
w = line[2].strip()
hobby = line[3].strip()
social = line[4].strip()
eat = line[5].strip()
sleep = line[6].strip()
Traits_dict["Work"] = w
Traits_dict["Hobbies"] = hobby
Traits_dict["Socialize"] = social
Traits_dict["Eat"] = eat
Traits_dict["Sleep"] = sleep
per = Person(first, last, Traits_dict)
print(per)
pl.append(per)
print(pl[0])
print(pl[1])
print(pl[2])
print(pl[3])
print(pl[4])
return pl
All the Traits_dict = {} are the same to all object since you initiating the dict before the loop so it's giving each Person object the same dict reference in it.
You can put the Traits_dict = {} inside the loop that it will create each Person a new dict
for line in file:
Traits_dict = {}
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.
I am trying to read from a file into a dictionary. The lane.split() method will not work as I am formatting my file over separate lines, with too many spaces.
in inventory2
(item, description) = line.split()
ValueError: too many values to unpack
Here is my text file. Key \n Value.
Key
A rusty old key, you used it to gain entry to the manor.
A stick
You found it on your way in, it deals little damage.
Health potion
A health potion, it can restore some health.
Any solutions to this would be much appreciated.
def inventory2():
inventory_file = open("inventory_test.txt", "r")
inventory = {}
for line in inventory_file:
(item, description) = line.split()
inventory[(item)] = description
#invenory = {inventory_file.readline(): inventory_file.readline()}
print(line)
inventory_file.close
You are looping over each line in the file, so there will never be a line with both key and value. Use the next() function to get the next line for a given key instead:
def inventory2():
with open("inventory_test.txt", "r") as inventory_file:
inventory = {}
for line in inventory_file:
item = line.strip()
description = next(inventory_file).strip()
inventory[item] = description
return inventory
or, more compact with a dict comprehension:
def inventory2():
with open("inventory_test.txt", "r") as inventory_file:
return {line.strip(): next(inventory_file).strip() for line in inventory_file}
Here is another way:
def inventory2():
inventory_file = open("inventory_test.txt", "r")
inventory = {}
lines = inventory_file.readlines()
x = 0
while (x < len(lines)):
item = lines[x].strip()
description = lines[x+1].strip()
inventory[item] = description
x += 2
print inventory
return inventory
Outputs:
{'Health potion': 'A health potion, it can restore some health.', 'A stick': 'You found it on your way in, it deals little damage.', 'Key': 'A rusty old key, you used it to gain entry to the manor.'}
I have this function in python 3 that works almost as I want it to work:
def read_people_from_file(filename):
"""Function that reads a file and adds them as persons"""
print("reading file")
try:
with open(filename, 'rU') as f:
contents = f.readlines()
except IOError:
print("Error: Can not find file or read data")
sys.exit(1)
#Remove blank lines
new_contents = []
for line in contents:
if not line.strip():
continue
else:
new_contents.append(line)
#Remove instructions from file
del new_contents[0:3]
#Create persons (--> Here is my problem/question! <--)
person = 1*[None]
person[0] = Person()
person[0] = Person("Abraham", "m", 34, 1, 140, 0.9, 90, 0.9, 0.9)
for line in new_contents:
words = line.split()
person.append(Person(words[0], words[1], words[2], words[3], words[4], words[5], words[6], words[7], words[8]))
return person
In the last chunk of code, below "#Create persons", is a thing that I have not figured out how to do.
How do I create the empty list of persons and then add persons from the file?
If I remove the hard coded person named "Abraham", my code does not work.
The file is a text file with one person per row with the attributes coming after the name.
Part of the Person class looks like this:
class Person:
def __init__(self, name=None, gender=None, age=int(100 or 0), beauty=int(0), intelligence=int(0), humor=int(0), wealth=int(0), sexiness=int(0), education=int(0)):
self.name = name
self.gender = gender
self.age = age
self.beauty = beauty
self.intelligence = intelligence
self.humor = humor
self.wealth = wealth
self.sexiness = sexiness
self.education = education
I hope that the above code is self explanatory.
I suspect that there is some more pythonian way of doing what I want.
Any help is appreciated.
You can do
persons = []
...
for line in new_contents:
words = line.split()
persons.append(Person(...))
There's always:
persons = [Person(*line.split()) for line in new_contents]
This is probably the simplest way to do what you want:
def readfile():
data = open("file path to read from","r") #opens file in read mode
people = []
for line in data: #goes through each line
people.append(Person(*line.split())) #creates adds "Person" class to a list. The *line.split() breaks the line into a list of words and passes the elements of the list to the __init__ function of the class as different arguments.
return people