mjolnir
mjolnir

Reputation: 61

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:

  1. 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

  2. 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.

Upvotes: 1

Views: 1729

Answers (2)

Anand S Kumar
Anand S Kumar

Reputation: 90899

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

Upvotes: 1

Padraic Cunningham
Padraic Cunningham

Reputation: 180411

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

Upvotes: 0

Related Questions