Python:如何将其他信息添加到包含名称和分数的文本文件中? [英] Python: How do i add additional information to a text file containing names and scores?

查看:61
本文介绍了Python:如何将其他信息添加到包含名称和分数的文本文件中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为学校数学课开发一个简单的数学程序,孩子们在其中写下自己的名字,然后程序会问10个简单的数学问题.之后,根据孩子的类别,孩子的名字和分数将存储在不同的文本文件中.

i'm working on a simple math programme for a school maths class where kids writes their name in and then programme asks 10 easy maths questions. After that, kids' names and scores would be stored in different text file, according to their classes.

这是我的问题.

目前,它们的名称以以下格式存储:William W-10

At the moment, their names are stored in this format: William W - 10

所以第一部分是他们的名字,最后一部分是他们的分数.我不确定是否有更好的方法.

so first part is their name and last part is their score. I'm not sure if there's better way of doing this.

我如何做到这一点,以便如果同一个孩子再次进行测试,它将与以前的分数平均吗?

How do i make it so that if same kid does the test again, it would average with previous score?

此外,如果孩子获得更高的分数,我该如何做才能使孩子的分数被改写? (这是与平均得分不同的文本文件)

Also, how do i make it so that the kid's score will over write if kid gets higher score? (which would be different text file to one with average score)

我正在尝试建立一个排名榜,其中得分最高的孩子出现在顶部,而最低的则出现在底部.

i'm trying to make a ranking board where kid with highest point shows up in the top and lowest on the bottom.

当显示在排行榜上时,我希望它看起来像这样.

When shown on leader board, i want it to look like this.

  1. 威廉W-10
  2. 日航E-9

等我到处搜索,但似乎找不到答案.如果有人可以分享他们的知识,那就太好了.谢谢

etc. i have searched everywhere and i don't seem to find a answer. It would be nice if someone could be share their knowledge on this. Thank you

这是我未完成的代码.我是python XD的新手 我需要在排行榜中添加其他内容:平均,最高和字母顺序,以及每个学生在测试中的最高分数.

Here is my unfinished code. I'm new to python XD I need to add extra things to the leader board: average, highest and alphabetical order with each student's highest score for the tests.

def menu():
    print "Welcome to Simple-Maths Questions program"
    print "Your options are the following:"
    print "1 - Do a test"
    print "2 - Leaderboard"
    print "3 - Exit"
    question()


def question():
    ans = input("Please enter the number: ")
    if ans ==1:
        start()
    elif ans ==2:
        leaderboard()
    elif ans ==3:
        exitprogramme()
    else:
        print "Error - invalid choice"
        print " "
    menu()

def leaderboard():
    menu()


def exitprogramme():
    quit()

def start():
    f = open('names.txt', 'a')
    print "Here are 10 easy maths question for you to solve"
    name = raw_input("Please enter in your name and surname enitial (e.g      Willam G)")
    import random
    import re
    import operator

    n1 = random.randint(1,9)
    n2 = random.randint(1,9)
    ops = {'+': operator.add, '-': operator.sub, '*': operator.mul}
    op = random.choice(ops.keys())
    a = 1
    count = 0

    while a <=10:
        question = input("What is " + str(n1) + str(op) + str(n2) + "?")
        a = a+1
        ans = ops[op](n1, n2)
        n1 = random.randint(1,9)
        n2 = random.randint(1,9)
        op = random.choice(ops.keys())

        if question == ans:
        count = count + 1
            print "Well done"
        else:
            count = count + 0
            print "WRONG"

    print " "
    print "You got score of "+ str(count)+"."
    f.write(name+' - '+str(count)+'\n')
    f.close()
    f1 = open("names.txt", "r")
    sortToFile = open("sortedFile.txt", "w")
    for line in sorted(f1, key = str.lower):
        sortToFile.write(line)
    f1.close()
    sort_nicely()


    def sort_nicely():
        import re
    ''' this is my attempt to do sort the names with score with highest score at the top '''

    convert = lambda text: int(text) if text.isdigit() else text
    alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ]
    Leaderboard = open("Leaderboard.txt", "w")
    f2 = open("names.txt", "r")
    for line in sorted(f2, key=alphanum_key, reverse=True):
        Leaderboard.write(line)


    print ""


    menu()

推荐答案

好吧,我计划只编写程序的相关部分,但最终我编写了整个工作程序.因为我不知道您知道多少Python,所以我将解释整个程序.对不起,如果我的脚步有点脚和/或浮躁.只需跳到底部并复制代码即可.

Well, I planned to just write the relevant parts of the program but I ended up writing an entire working program instead. Since I don't know how much Python you know I'm just going to explain the entire program; sorry if I have a somewhat pedantic and/or pompous air. Feel free to just skip to the bottom and copy the code.

我编写的代码演示了4个Python习惯用法:面向对象的设计,字节数组/字符串切片和格式化,xrange()函数以及错误捕获.

The code I wrote demonstrates 4 Python idioms: object oriented design, bytearray/string slicing and formatting, the xrange() function, and error catching.

我认为csv并不是解决问题的好角度.我选择将数据存储为文本表.每行采用以下形式: 30个字符的名称|总得分10个字符|尝试9个字符| 1个换行符 这样做的好处是每一行正好是50个字符宽.因此,与csv方法不同,我们不必扫描逗号来查找相关数据.我们只是以50个字符为增量浏览数据.这使得代码更具可读性,而且如今不像文本存储空间那样稀缺.

I think that csv would not be a good angle to approach the problem with. I opted to store the data as a text table. Each line takes the following form: 30 characters name | 10 characters total score | 9 characters attempts | 1 newline The benefit of this is that each line is exactly 50 characters wide. So, unlike the csv approach, we don't have to scan through for commas to find relevent data; we just go through the data in increments of 50 characters. This makes for much more readable code, and it's not like text storage space is at a premium these days.

示例:Chell R在5次尝试中获得42分成为:

Example: Chell R earned 42 points in 5 attempts becomes:

"                       Chell R        42        5\n"

由于我们稍后将对这些数据进行排序,因此将学生的姓名和他/她的分数都存储在一个对象中将是有利的.这个对象使分类变得更加简单.因为名称是永久性地附加到分数中的,所以我们不必担心在排序数据时确保名称数据跟随分数数据.它只是隐式发生.

Since we're going to be sorting this data later, it would be advantageous to store both the student's name and his/her score in an object. This object makes sorting much simpler; because the name is permanently attatched to the score, we don't have to worry about making sure the name data follows the score data as the data is sorted. It just happens implicitly.

这是代码的第一部分:

import time, random, operator
directory_name = r"C:\Users\YOU\yourDirectoryHere\\"
#When you change the directory name, be SURE to include
#the double backslash at the end.
datafile = open(directory_name + "scores.txt")
data = bytearray(datafile.read())
datafile.close()

class entry():
    def __init__(self, i):
        #i is the location of the student's entry in the data.
        global data
        self.name = data[i:i + 30]
        self.total = int(data[i + 30:i + 40])
        self.attempts = int(data[i + 40:i + 49])
        if self.attempts:
            self.average = float(self.total) / self.attempts
        else: self.average = 0.0

    def update_score(self, score, i):
        global data, directory_name
        self.total += score
        self.attempts += 1
        self.average = float(self.total) / self.attempts
        data[i + 30: i + 40] = "%10i" % self.total
        data[i + 40: i + 49] = "%9i" % self.attempts
        datafile = open(directory_name + "scores.txt",'w')
        datafile.write(data)
        datafile.close()

init 下定义的方法称为魔术方法.这意味着与常规方法update_score不同,您不必自己调用该方法.它是由Python自动调用的;在这种情况下,创建条目对象时将调用它.

The method defined under init is known as a magic method. This means that unlike the regular method update_score, you don't call the method yourself. It's automatically called by Python; in this case it's called when an entry object is created.

五件事要指出: 1:只是为了快速解释语法烦恼,每个方法都必须将self作为第一个参数,并且存储在对象中的每个变量都需要一个self.前缀.

Five things to point out: 1: Just to quickly explain a syntax annoyance, each method must have self as the first argument and each variable stored in the object needs a self. prefix.

2:请注意,从文件读取的数据将转换为字节数组类型.字节数组很有用,因为与字符串不同,它们是可变的,因此我们可以在必要时更新学生的分数,而不会低效率地重新创建整个字符串.我选择将数据作为全局变量而不是常规参数传递,以避免必须将其传递给多层函数.

2: Notice that the data read from the file is converted to the bytearray type. Bytearrays are useful because unlike strings they are mutable, so we can update students' scores when neccessary without inefficiently recreating the entire string. I opted to pass data as a global variable instead of a regular argument in order to avoid having to pass it through several layers of functions.

3:表达式foo = bar [x:y]是切片操作.这意味着用bar的字符x到y-1填充变量foo".我在 init 函数中使用了它,以初始化条目的名称和得分值.如update_score方法所示,相反的方法也可以.

3: The expression foo = bar[x:y] is a slice operation. This means "Fill variable foo with characters x through y-1 of bar". I used it in the init function in order to initialize the entry's name and score values. The reverse also works, as seen in the update_score method.

4:字符串或字节数组格式的另一个有用技巧是从C继承的.由于每个总分条目必须占用10个字符才能使每个条目保持50个字符的宽度,因此请按顺序使用字符串%10i"%self.total强制整数到10个字符的宽度.

4: Another useful trick for string or bytearray formatting inherits from C. Since each total score entry must take up 10 characters in order to keep each entry 50 characters wide, use the string "%10i" % self.total in order to force the interger to a 10 character width.

5:最后,我只写了一个简短的注释,即我写了float(self.total)/self.attempts.将整数除以整数会导致舍入不正确的整数.

5: Finally, just a quick note that I wrote float(self.total) / self.attempts. Dividing an integer by an integer results in a rounded, inaccurate integer.

程序的下一部分是主要的play()函数,该函数调用find_entry():

The next part of the program is the main play() function, which calls find_entry():

def find_entry(name):
    global data
    for i in xrange(0,len(data),50):
        if data[i:i + 30] == "%30s" % name:
            return i
    print "Name not found. Please check the spelling of your name."
    print "If you spelled your name correctly please ask your teacher for help."
    return None

首先在find_entry函数中看到xrange函数.与while循环相比,这提供了一种更具可读性的pythonic方式进行迭代. 对于xrange(0,len(data),50)中的i:"等效于. "i = 0;而我

The xrange function is first seen in the find_entry function. This provides a much more readable, pythonic way to iterate than while loops. "for i in xrange(0,len(data),50):" is equivalent to. "i = 0; While i < len(data): ... i += 50. Anyway, all it does is go through the data 50 characters at a time (since each entry is 50 characters wide) and check if the first 30 characters match up with the inputted name. (Note that I use data[i:i + 30] == "%30s" % name. The inputted name must be pumped up to 30 characters, otherwise the equality operator will always fail). It returns the index i of the found entry if successful and returns None to indicate failure.

接下来,我们将检查主要的play()函数:

Next we'll examine the main play() function:

def play():
    global data
    entry_num = None
    while entry_num == None:
        entry_num = find_entry(raw_input("Please enter your name and surname initial (e.g. William G): "))

    student_entry = entry(entry_num)
    score = 0
    for i in xrange(10): #same as xrange(0,10,1)
        score += question()

    student_entry.update_score(score, entry_num)
    print "Your score this time was %i / 10" % score

我们在这里看到,find_entry在失败时返回None的事实得到了很好的利用. while循环表示程序将继续尝试,直到输入了有效的名称为止.还请注意raw_input函数的使用.这迫使Python将输入解释为字符串.在下一个功能(问题功能)中,这将变得更有意义:

We see here that the fact that find_entry returns None upon failure is put to good use. The while loop means the program keeps on trying until a valid name is inputted. Also note the use of the raw_input function. This forces Python to interpret the input as a string. This'll make more sense in the next function, the question function:

def question():
    n1 = random.randint(1,9)
    n2 = random.randint(1,9)
    ops = {'+': operator.add, '-': operator.sub, 'x': operator.mul}
    op = random.choice(ops.keys())
    answer = raw_input("What is %i %s %i?  " % (n1, op, n2))
    try: 
        answer = int(answer)
        if answer == ops[op](n1, n2):
            print "Well done"
            return 1
        else:
            print "incorrect, %i %s %i = %i" % (n1, op, n2, ops[op](n1,n2))
            return 0
    except ValueError:
        print "'%s' does not appear to be a number." % answer
        return 0

我几乎只原样复制了您的代码.唯一的主要区别是添加了try ... except语句,并使用了raw_input()来代替input(). input()的问题在于它是非常不安全的,因为Python可以自由地将其输入解释为任何类型,包括变量名,虚假的输入会破坏各种破坏. raw_input安全得多:由于Python始终将输入解释为字符串,因此程序员有权解释输入并预测错误.在这种情况下,try ... except语句尝试使用int()将输入的字符串转换为整数.如果int()无法将字符串转换为整数,则会引发ValueError,因此ValueError语句将在失败时执行替代的Error语句,将失败传达给用户,并允许程序进行恢复而不是崩溃.

I pretty much just copied your code verbatim for this part. The only major difference is the addition of the try... except statement and the use of raw_input() in lieu of input(). The issue with input() is that it's pretty unsafe, since Python is free to interpret its input as any type, including a variable name, a bogus input can wreck all sorts of havoc. raw_input is much safer: since Python always interprets the input as a string the programmer gets the right to interpret the input and anticipate errors. In this case, the try... except statement tries to convert the inputted string to an integer with int(). int() raises ValueError if it fails to convert the string to an integer, so an except ValueError statement would execute instead upon failure, communicating the failure to the user and allowing the program to recover instead of crashing.

现在已经解决了,我们可以继续讨论问题中最有趣的部分:对条目进行排序.

Now that that's dealt with, we can move on to the actually interesting part of the question: sorting the entries.

回到entry()类定义,还有两种其他有助于排行榜创建的魔术方法, cmp str :

Going back to the entry() class definition, there are two additional magic methods which facilitate leaderboard creation, cmp and str:

class entry():
    def __init__(self, i):
        ...

    def __cmp__(self, other):
        return self.average - other.average

    def __str__(self):
        return "%s | %2.2f" % (self.name, self.average)

    def update_score(self, score, i):
        ...

cmp 方法在Python需要比较两个条目时调用.由于我们希望排行榜按平均分数而不是按字母顺序或按总分数排序,因此我们返回此条目的平均值与另一个条目的平均值的比较(Python将负返回值解释为self< other,将正返回值解释为自我>其他)

The cmp method is called when Python needs to compare two entries. Since we want the leaderboards to be sorted by average score, not alphabetically or by total score, we return a comparison of this entry's average with the other entry's average (Python interprets a negative return value as self < other and a positive return value as self > other)

str 方法是遇到str(a)或print(a)时所调用的方法,其中a是条目类型.由于我们现在必须格式化两个数据,即学生的姓名和平均值,因此我们将两个变量放在%符号后的元组中. %2.2f告诉Python打印一个浮点数,小数点后仅2位.任何其他数字都将被忽略.

The str method is what's called when str(a) or print(a) is encountered, where a is an entry type. Since we now must format two pieces of data, the student's name and average, we put the two variables in a tuple after the % sign. The %2.2f tells Python to print a floating point number with only 2 digit after the decimal point; any further digits are ignored.

所有工作都写完了方法.因此,编写Leaderboard()函数就变得很简单:

All the work was done writing the methods. Writing the leaderboard() function thus becomes a trivial affair:

def leaderboard():
    global data
    entries = []
    for i in xrange(0, len(data),50):
        entries += [entry(i)]

    entries.sort()
    entries.reverse()

    for i in entries:
        print i

我们在这里看到了面向对象编程的美丽. xrange循环一遍又一遍地调用条目的 init 方法,并在列表中填充条目.该稍微复杂的功能可以安全地存放在入口类声明中,从而使排行榜功能保持整洁且易于阅读.接下来,将调用Python的内置列表对象的sort()方法,因此我们无需重新编写轮子就可以编写另一种排序算法.这将调用我们的 cmp 函数,以确保Python将按预期的平均分数正确地对条目进行排序.接下来,我们反转列表,因为我们首先想要最高的分数.同样,无需自己编写此函数;它与Python中的列表类型一起提供.最后,我们打印排行榜.这将调用条目类的 str 函数,因此在排行榜函数本身中,我们不必担心在数据文件中找到正确的数据.干净整洁.

We see the beauty of object oriented programming at work here. The xrange loop calls the entry's init method over and over again, populating the list with entries. This somewhat complex function is safely stowed away in the entry class declaration, keeping the leaderboard function clean and easy to read. Next, the sort() method of Python's built in list object is called, so we don't need to reinvent the wheel writing another sorting algorithm. This calls our cmp function, ensuring that Python will correctly sort the entries by average score as intended. Next, we reverse() the list, since we want the highest scores first. Again, no need to write this function ourselves; it comes along with the list type in Python. Finally, we print the leaderboard. This calls the entry class's str function, so in the leaderboard function itself we don't need to worry about finding the correct data in the data file. Nice and clean.

这里已经完成了很多工作,现在要做的就是编写主循环:

Pretty much done here, now all there's to do is write the main loop:

while True:
    print """\
Welcome to Simple-Maths Questions program
Please type one of the following:
Play | Leaderboard | Exit"""
    command = raw_input()
    command = command.upper()
    if command in "PLAY":
        play()
    elif command in "LEADERBOARD":
        leaderboard()
    elif command in "EXIT":
        break #You don't have to call quit() to terminate the program, just reach EOF.
    elif command in "ADD NAME":
        add_student()
    else:
        print "unknown command"

同样,raw_input()比input()更安全.关键字将转换为全部大写字母,并且仅查找关键字中是否包含输入内容,因此输入不区分大小写,因此无需键入full关键字(这是调试时的节省时间).另外,还有隐藏的命令"ADD NAME";我不想解释该功能.那就是我要说的几乎全部.

Again, raw_input() is safer than input(). The keyword is converted to all caps and only finds whether the input is in the keyword, so the input is not case sensitive and there's no need to type the full keyword (This was a timesaver for debugging). Also, there's the hidden command "ADD NAME"; I didn't feel like explaining that function. And that's, uh, pretty much all I have to say.

这是完整的代码:

import time, random, operator
directory_name = r"C:\Users\YOU\yourDirectoryHere\\"
#When you change the directory name, be SURE to include
#the double backslash at the end. (and keep the r)
datafile = open(directory_name + "scores.txt")
data = bytearray(datafile.read())
datafile.close()

backup_directory = directory_name
def backup():
    #Note: I didn't explain this function. It just randomly
    #makes backups of the data file in case something unexpected happens.
    global data, backup_directory
    datafile = open(backup_directory + str(int(time.time())) + '.txt', 'w')
    datafile.write("Remove this timestamp, message, and newline before restoring this data %s\n" % time.asctime())
    datafile.write(data)
    datafile.close()

class entry():
    def __init__(self, i):
        #i is the location of the student's entry in the data.
        global data
        self.name = data[i:i + 30]
        self.total = int(data[i + 30:i + 40])
        self.attempts = int(data[i + 40:i + 49])
        if self.attempts:
            self.average = float(self.total) / self.attempts
        else: self.average = 0.0

    def __cmp__(self, other):
        #BUGGED CODE return self.average - other.average
        #FIXED BELOW
        if self.average > other.average: return 1
        elif self.average < other.average: return -1
        else: return 0

    def __str__(self):
        return "%s | %2.2f" % (self.name, self.average)

    def update_score(self, score, i):
        global data, directory_name
        self.total += score
        self.attempts += 1
        self.average = float(self.total) / self.attempts
        data[i + 30: i + 40] = "%10i" % self.total
        data[i + 40: i + 49] = "%9i" % self.attempts
        datafile = open(directory_name + "scores.txt",'w')
        datafile.write(data)
        datafile.close()

        if not random.randrange(5):
            backup()


def find_entry(name):
    global data
    for i in xrange(0,len(data),50):
        if data[i:i + 30] == "%30s" % name:
            return i
    print "Name not found. Please check the spelling of your name."
    print "If you spelled your name correctly please ask your teacher for help."
    return None

def question():
    n1 = random.randint(1,9)
    n2 = random.randint(1,9)
    ops = {'+': operator.add, '-': operator.sub, 'x': operator.mul}
    op = random.choice(ops.keys())
    answer = raw_input("What is %i %s %i?  " % (n1, op, n2))
    try: 
        answer = int(answer)
        if answer == ops[op](n1, n2):
            print "Well done"
            return 1
        else:
            print "incorrect, %i %s %i = %i" % (n1, op, n2, ops[op](n1,n2))
            return 0
    except:
        print "'%s' does not appear to be a number." % answer
        return 0

def play():
    global data
    entry_num = None
    while entry_num == None:
        entry_num = find_entry(raw_input("Please enter your name and surname initial (e.g. William G): "))

    student_entry = entry(entry_num)
    score = 0
    for i in xrange(10):
        score += question()

    student_entry.update_score(score, entry_num)
    print "Your score this time was %i / 10" % score

def leaderboard():
    global data
    entries = []
    for i in xrange(0, len(data),50):
        entries += [entry(i)]

    entries.sort()
    entries.reverse()

    for i in entries:
        print i

def add_student():
    #This wasn't explained either. It just appends a properly formatted 50 character
    #long entry to the bytearray, initialized with the inputted name and zeroes.
    global data
    while True:
        student = raw_input("Add a student, press enter to quit: ")
        if student:
            cancel = raw_input("Confirm '%s'. Press enter to confirm, any key to cancel: " % student)
            if not cancel:
                data += bytearray("%30s%10i%9i\n" % (student,0,0))
            else: print "cancelled"
        else:
            break
    datafile = open(directory_name + "scores.txt",'w')
    datafile.write(data)
    datafile.close()

while True:
    print """\
Welcome to Simple-Maths Questions program
Please type one of the following:
Play | Leaderboard | Exit"""
    command = raw_input()
    command = command.upper()
    if command in "PLAY":
        play()
    elif command in "LEADERBOARD":
        leaderboard()
    elif command in "EXIT":
        break
    elif command in "ADD NAME":
        add_student()
    else:
        print "unknown command"

注意:我还没有对它进行彻底的错误测试,因此请做一些测试以确保它能正常工作.

Note: I haven't exactly exhaustively bug tested this, so please do a bit of testing yourself to make sure this works.

注意2:修正了cmp功能; .sort的行为不正确,带有接近的浮动pt.数字.

NOTE 2: Amended cmp function; .sort behaved erratically with close floating pt. numbers.

这篇关于Python:如何将其他信息添加到包含名称和分数的文本文件中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆