这个菜单驱动程序在二进制文件(姓名、卷、年龄、标记)中输入学生记录并执行以下操作的错误是什么

What is the error in this menu driven program to input records of students in a binary file (name,roll,age,marks) and perform the following operations

import pickle


def main():
    while True:
        print('''    1. Create Binary File.
    2.  Display Binary File.
    3.  Search for a given roll number.
    4.  Input roll number and mark, Update mark of the student.
    5.  Delete a record for a given roll number
    6.  Display the details of the students getting average marks more than 80.
    7.  Append new records at the end of file.
    8.  Exit''')

        choice=int(input('choose a command (1..8): '))

        if choice==1:
            f=open('student.dat','wb')
            entries=int(input('How many entries?: '))
            for entry in range(entries):
                name=str(input('enter a name: '))
                rollno=int(input('enter a roll no: '))
                age=int(input('Students age: '))
                avg_marks=int(input('avg mark= '))
                t=[name,rollno,age,avg_marks]
                pickle.dump(t,f)
            f.close()
            print('File created')

        elif choice==2:
            f=open('student.dat','rb')
            try:
                while True:
                    content=pickle.load(f)
                    print(content)
            except:
                f.close()

        elif choice==3:
            f=open('student.dat','rb')
            roll_find=int(input('roll no of the student to find: '))
            while True:
                content=pickle.load(f)
                if content[1]==roll_find:
                    print(content)
                    break
            f.close()
                
        elif choice==4:
            roll_find=int(input('roll to find'))
            new_marks=int(input('new_marks'))
            rows=[]
            f=open('student.dat','rb')
            try:
                while True:
                    content=pickle.load(f)
                    rows.append(content)
            except:
                pass

            for row in rows:
                if row[1]==roll_find:
                    row[3]=new_marks
                    print('row found and updated')
            f.close()
            
            f=open('studen.dat','wb')
            for row in rows:
                pickle.dump(row,f)
            f.close()
            
        elif choice==5:
            f=open('student.dat','rb')
            roll_find=int(input('roll no of the student: '))
           
            while True:
                content=pickle.load(f)
                if content[1]==roll_find:
                    del content[:]
                    print('roll no found and row deleted: ')
                    break
            f.close()

除了更新和删除卷号外,一切正常。 我能够输入新的详细信息,但由于某种原因它不会覆盖之前写的内容。 检查第 4 个选项和第 5 个选项是否有错误代码。

您的问题是您正在读取内容但没有再次存储它们。但是这段代码要求一些辅助功能,以便一切都保持干净。让我们开始制作一些辅助函数并同时解决问题。

我们要求用户提供几个整数,所以让我们创建一个函数来处理这个问题。它将 return 一个特殊数字(默认为 0),以防我们无法将用户提供的内容转换为整数。

def input_integer(request: str, *, error: int = 0) -> int:
    try:
        return int(input(request))
    except ValueError:  # If we can't convert to integer return error
        return error

注意:如果您不习惯类型注释,它们意味着 input_integer 函数需要一个字符串参数(这是请求整数的消息)和一个允许配置的关键字整数参数which integer to return as an error, defaulting to 0. 函数 returns 是一个整数。通过创建此注释,reader 将更好地理解函数的作用,IDE 将检测到一些类型不匹配错误,但不会在运行时对其进行评估。

好的,我们要打印一个菜单,让我们也为它做一个函数。实际上,我们将创建一个 return 函数。 outter 函数将自己构建菜单,returned 函数将打印它并请求用户输入。

def menu_factory(options: List[str], input_request: str) -> Callable[[], int]:
    # Number of options
    options_number = len(options)

    # Menu text
    menu_text_lines = ["MENU:"]
    for i, option in enumerate(options):
        menu_text_lines.append(f"\t{i+1}. {option}")
    menu_text = "\n".join(menu_text_lines)

    # Input request text
    input_request += f" (1..{options_number}): "

    # Create the function that will be called to print the menu
    def menu_function() -> int:
        # Print the menu text we have previously built
        print(menu_text)

        # Get the input from the user until he gives a valid one
        choice = input_integer(input_request)
        while not(0 < choice <= options_number):
            print(f"It must be a number between 1 and {options_number}.")
            choice = input_integer(input_request)

        # Return the choice of the user
        return choice

    return menu_function

如您所见,函数可以在其他块内定义,并且可以像任何其他对象一样 returned。该函数接受一个字符串列表,这些字符串是选项和一个附加字符串以请求用户输入。它将构建添加数字的完整消息并准备我们稍后将调用的函数。在这种情况下,menu_factory 函数将这样调用:

menu = menu_factory(
    [
        "Create Binary File.",
        "Display Binary File.",
        "Search for a given roll number.",
        "Input roll number and mark, Update mark of the student.",
        "Delete a record for a given roll number.",
        "Display the details of the students getting average marks "
        "more than 80.",
        "Append new records at the end of file.",
        "Exit",
    ],
    "Choose a command"
)

现在,menu() 将打印菜单,请求用户选择,并且 return 验证后选择是正确范围内的正确数字。

接下来我们将有一个 class 来存储每个学生的信息而不是列表。它将具有 4 个属性(姓名、卷号、年龄和标记)。构造函数 (__init__) 只会保存它们。我还添加了一个 __repr__ 告诉 python 如何 print 学生对象。 class 定义顶部的 __slots__ 是内存优化。通过设置 __slots__ 我们告诉 class 它将拥有哪些确切的属性,而不必将它们存储为通用字典。如果您删除此行,它仍然会完全相同,但您加载的每个学生都会消耗更多的 RAM。

class Student:
    __slots__ = 'name', 'roll_no', 'age', 'mark'

    def __init__(self, name: str, roll_no: int, age: int, mark: int) -> None:
        self.name = name
        self.roll_no = roll_no
        self.age = age
        self.mark = mark

    def __repr__(self) -> str:
        return f"Student<name={self.name}, roll_no={self.roll_no}, " \
               f"age={self.age}, mark={self.mark}>"

我们已经差不多完成了,但还有一件事我们还要做很多次,即读取和写入文件。让我们为此创建一些辅助函数。 read_file 函数将创建 listStundet。第一个 try: ... except FileNotFoundError: pass 块检测是否找不到文件(返回 Student 的空列表)。 with open(...) as f: ... 如果是比事后调用 f.close() 更好的方法。 Python 本身将在您离开 with 块时关闭文件,无论是由于异常还是任何其他原因。所以基本上我们肯定知道文件无论如何都会被关闭。里面的try: ... except EOFError: break是检测我们什么时候结束读取文件,然后跳出无限while True: ...循环。

def read_file(path: str) -> List[Student]:
    content = []
    try:
        with open(path, 'rb') as f:
            while True:
                try:
                    content.append(pickle.load(f))
                except EOFError:  # Break the loop when reached end of the file
                    break
    except FileNotFoundError:  # Don't raise if the file doesn't exist
        pass
    return content

写入函数很简单,打开文件,写入内容,大功告成。追加也是如此。

def write_file(path: str, content: List[Student]) -> None:
    with open(path, 'wb') as f:
        for row in content:
            pickle.dump(row, f)


def append_file(path: str, content: List[Student]) -> None:
    with open(path, 'ab') as f:
        for row in content:
            pickle.dump(row, f)

所以现在我们有一个 input_integer 函数可以从用户那里读取整数,一个 menu_factory 函数可以构建菜单,一个 Student class 可以存储每个学生的信息,以及 read_filewrite_fileappend_file 函数。我们可以开始实现我们的逻辑了。我将创建一个函数字典,其中键将是整数选择,值是我们将调用以实现该选项的函数。现在我们已经设置了这些辅助方法,您将看到 choice_X 函数将变得非常简单。

import pickle
from typing import Callable, List


FILENAME = 'student.bat'


def input_integer(request: str, *, error: int = 0) -> int:
    # ...


def menu_factory(options: List[str], input_request: str) -> Callable[[], int]:
    # ...


class Student:
    # ...


def read_file(path: str) -> List[Student]:
    # ...


def write_file(path: str, content: List[Student]) -> None:
    # ...


def append_file(path: str, content: List[Student]) -> None:
    # ...


if __name__ == '__main__' :
    # Create the menu function from the factory
    menu = menu_factory(
        [
            "Create Binary File.",
            "Display Binary File.",
            "Search for a given roll number.",
            "Input roll number and mark, Update mark of the student.",
            "Delete a record for a given roll number.",
            "Display the details of the students getting average marks "
            "more than 80.",
            "Append new records at the end of file.",
            "Exit",
        ],
        "Choose a command"
    )

    def choice_1() -> None:
        students = []
        for i in range(1, input_integer("How many entries? ") + 1):
            print(f"Entry number {i}:")
            students.append(Student(
                input("\tName:         "),
                input_integer("\tRoll number:  "),
                input_integer("\tStudent age:  "),
                input_integer("\tAverage mark: "),
            ))
        write_file(FILENAME, students)

    def choice_2() -> None:
        print(read_file(FILENAME))

    def choice_3() -> None:
        roll_no = input_integer("Roll number of the student to find: ")
        for student in read_file(FILENAME):
            if student.roll_no == roll_no:
                print(student)
                break
        else:  # This will only be executed if no break was found
            print(f"Roll number {roll_no} not found in data file.")

    def choice_4() -> None:
        roll_no = input_integer("Roll number of the student to update: ")
        students = read_file(FILENAME)
        for i, student in enumerate(students):
            if student.roll_no == roll_no:
                break
        else:  # This will only be executed if no break was found
            print(f"Roll number {roll_no} not found in data file.")
            return

        students[i].mark = input_integer("New average mark: ")
        write_file(FILENAME, students)

    def choice_5() -> None:
        roll_no = input_integer("Roll number of the student to delete: ")
        students = read_file(FILENAME)
        for i, student in enumerate(students):
            if student.roll_no == roll_no:
                break
        else:  # This will only be executed if no break was found
            print(f"Roll number {roll_no} not found in data file.")
            return

        del students[i]
        write_file(FILENAME, students)

    def choice_6() -> None:
        for i, student in enumerate(read_file(FILENAME)):
            if student.mark >= 80:
                print(student)

    def choice_7() -> None:
        students = []
        for i in range(1, input_integer("How many entries? ") + 1):
            print(f"Entry number {i}:")
            students.append(Student(
                input("\tName:         "),
                input_integer("\tRoll number:  "),
                input_integer("\tStudent age:  "),
                input_integer("\tAverage mark: "),
            ))
        append_file(FILENAME, students)

    menu_callbacks = {
        1: choice_1,
        2: choice_2,
        3: choice_3,
        4: choice_4,
        5: choice_5,
        6: choice_6,
        7: choice_7,
    }

    # Option number 8 is Exit
    while (choice := menu()) != 8:
        print()
        try:
            callback = menu_callbacks[choice]
        except KeyError:
            print("NOT IMPLEMENTED YET")
        else:
            callback()
        print("\n")