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

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

Related

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

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

Python: Writing to same rows in a CSV file

I have a text file that has numerous lines. I want to extract certain lines and write them to a CSV file. However, I want to write particular lines to the same row in the CSV file. For example, my text file is like this:
Name= Sarah F
Location= Baltimore MD
Name= Bob M
Location= Sacramento CA
Name= Tom M NY
Location= Brooklyn NY
Name= Anne F
Location= Morristown NJ
My CSV file I want to generate will include the name of the person, their sex, the city and state they reside in:
Sarah,F,Baltimore,MD
Bob,M,Sacramento,CA
Tom,M,Brooklyn,NY
Anne,F,Morristown,NJ
When I use csv.writerows([list]) I get the names,sex and the city,state written in separate rows:
Sarah,F
Baltimore,MD
Bob,M
Sacramento,CA
Tom,M
Brooklyn,NY
Anne,F
Morristown,NJ
When I try to append to the list with: [name, sex] the city and state the override the original list instead of appending.
Here is my code to do this:
import csv
file = open("file_to_use.txt", 'r')
csv_file = open("file_to_write.csv", 'wb')
writer = csv.writer(csv_file)
Row_lines =[]
for line in file:
if line.startswith("Name="):
name_line = line.replace(" ", ",")
name_line = name_line.strip("\n")
Row_lines.append(name_line)
if line.startswith("Location="):
loc_line = line.replace(" ", ",")
loc_line = loc_line.strip("\n")
Row_lines.append(loc_line)
writer.writerows(Row_lines)
csv_file.close()
I know I have some logical order in the incorrect place, but I can't seem to figure it out.
There are two parts to your task. First is joining the rows, you can use zip for that:
with open(inputfile) as propsfile:
data = [row.split("=")[1].split() for row in propsfile]
# join two at a time
data_tuples = zip(data[::2], data[1::2])
Second is writing the rows, you can use the csv module for that:
import csv
with open(outputfile, 'w') as csvfile:
writer = csv.writer(csvfile)
writer.writerows([name+location for name, location in data_tuples])
Now we have the data in outputfile:
Sarah,F,Baltimore,MD
Bob,M,Sacramento,CA
...
You are adding two different rows to Row_lines which represent one single csv row, you should add only one row to Row_lines for each row.
Each time you call Row_lines.append(), you are adding a new item to the list. Each item in the list is written as a separate line when you call writer.writerows(Row_lines).
Each time you encounter a name line, you should create a new string from that line, but don't add it to the Row_lines list yet. Each time you encounter a location line, you should append it to the name line string, creating a complete row which you can now add to the Row_lines list.
And instead of calling writerows() on each iteration of the loop, you should call it once after you have compiled the full list of rows.
import csv
file = open("file_to_use.txt", 'r')
csv_file = open("file_to_write.csv", 'wb')
writer = csv.writer(csv_file)
Row_lines =[]
for line in file:
if line.startswith("Name="):
name_line = line.replace(" ", ",")
name_line = name_line.strip("\n")
# start building the new line
current_line = name_line
if line.startswith("Location="):
loc_line = line.replace(" ", ",")
loc_line = loc_line.strip("\n")
# append the extra fields to the current line
current_line = current_line + ',' + loc_line
# add the current line to the output list
Row_lines.append(current_line)
# call this after you have added
# all lines, not after each one
writer.writerows(Row_lines)
csv_file.close()
Here is a code that does not use any external libraries.
Since all your lines are not necessarily consistent (e.g. "Name= Tom M NY" - NY should probably not be there), this code looks at the 2 first data entries following "Name=" or "Location=" and ignores any subsequent entries (like "NY" in the example above).
# Opening the file to use
input_file = open(r"C:\Temp\file_to_use.txt", 'r')
# Creating an empty CSV file
output_file = open(r"C:\Temp\output.csv", 'w')
# Going through the text file, it is checking whether the line holds name or location information
# If it holds location information, all saved information so far is saved to the CSV file
for line in input_file:
line = line.split("=")
if line[0] == "Name":
first_name, last_name = line[1].strip().split()[:2]
elif line[0] == "Location":
city, state = line[1].strip().split()[:2]
output_file.write('%s,%s,%s,%s\n' % (first_name, last_name, city, state))
# Closes the opened files
input_file.close()
output_file.close()

Printing to a new line after sorting a text file

For my task I have to be able to print the data that is in text file after it has been sorted. I have been able to sort it but it dosen't print it to a new line even though in notepad they are on seperate lines.
My Notepad Documents has this:
http://i.stack.imgur.com/Xn0pT.png
The code I already have set up is:
file = open(class_name , 'a') #opens the file in 'append' mode so you don't delete all the information
name = (name)
file.write(str(name + " : " )) #writes the information to the file
file.write(str(score))
file.write('\n')
file.close() #safely closes the file to save the information
viewscore = input("Do you wish to view previous results for your class").lower()
if viewscore == "yes".lower():
f = open(class_name , "r")
lines = [line for line in f if line.strip()]
f.close()
lines.sort()
print (lines)
The Variables I have are:
class_name = class_name + ".txt"
name = input().title()
Then when run the output I get is:
['Dan : 0\n', 'Jana : 0\n', 'Kyle : 0\n']
Please tell me if I have to add anything.
Here is a work version for me:
class_name = 'data.txt'
name = 'Jim'
score = 100
file = open(class_name , 'a') #opens the file in 'append' mode so you don't delete all the information
line_data = name + " : " + str(score) + "\n" # data to write
file.write(line_data)
file.close() #safely closes the file to save the information
viewscore = raw_input("Do you wish to view previous results for your class?").lower()
if viewscore == "yes".lower():
f = open(class_name , "r")
lines = [line for line in f if line.strip()]
f.close()
lines.sort()
for line in lines:
print line
else: # add else case to debug
print 'no for', viewscore
First, you can put the line you want to write in a variable, and then write it.
Second, if you use Python2.x, use raw_input() for input string.
Third, if you have a if, better to write an else for easy to debug the code.
You should try this code. Why are you writing 3 things separately? It is probably writing \n as a string, not putting things in new line.
f.write(name+' : '+str(score)+'\n')
What you are printing is the list you called lines and the way you see the output is the way the type list is displayed. What you want to do is print the content of the list as one string. For that you can join the content as follow:
In [1]: lines = ['Dan : 0\n', 'Jana : 0\n', 'Kyle : 0\n']
In [2]: print "".join(lines)
Dan : 0
Jana : 0
Kyle : 0

Read lines from a text file into variables

I have two different functions in my program, one writes an output to a txt file (function A) and the other one reads it and should use it as an input (function B).
Function A works just fine (although i'm always open to suggestions on how i could improve).
It looks like this:
def createFile():
fileName = raw_input("Filename: ")
fileNameExt = fileName + ".txt" #to make sure a .txt extension is used
line1 = "1.1.1"
line2 = int(input("Enter line 2: ")
line3 = int(input("Enter line 3: ")
file = (fileNameExt, "w+")
file.write("%s\n%s\n%s" % (line1, line2, line3))
file.close()
return
This appears to work fine and will create a file like
1.1.1
123
456
Now, function B should use that file as an input. This is how far i've gotten so far:
def loadFile():
loadFileName = raw_input("Filename: ")
loadFile = open(loadFileName, "r")
line1 = loadFile.read(5)
That's where i'm stuck, i know how to use this first 5 characters but i need line 2 and 3 as variables too.
f = open('file.txt')
lines = f.readlines()
f.close()
lines is what you want
Other option:
f = open( "file.txt", "r" )
lines = []
for line in f:
lines.append(line)
f.close()
More read:
https://docs.python.org/2/tutorial/inputoutput.html#reading-and-writing-files
from string import ascii_uppercase
my_data = dict(zip(ascii_uppercase,open("some_file_to_read.txt"))
print my_data["A"]
this will store them in a dictionary with lettters as keys ... if you really want to cram it into variables(note that in general this is a TERRIBLE idea) you can do
globals().update(my_data)
print A

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