文件输入/输出(File I/O)

下面是一个简单的文件输入输出(I/O)示例:

# 写入文件
with open("test.txt", "wt") as out_file:
    out_file.write("This Text is going to out file\nLook at it and see!")

# 读取文件
with open("test.txt", "rt") as in_file:
    text = in_file.read()

print(text)

输出和 test.txt 文件的内容为:

This Text is going to out file
Look at it and see!

请注意,它在你运行程序的目录中写入了一个名为 test.txt 的文件。字符串中的 \n 告诉 Python 在该位置插入一个换行符。

文件 I/O 概述:

  1. 使用 open 函数获取文件对象。
  2. 根据打开文件时指定的模式,读取或写入文件对象。
  3. 如果没有使用 with 来打开文件,你需要手动关闭文件。

第一步是获取文件对象。获取文件对象的方式是使用 open 函数。格式是 file_object = open(filename, mode),其中 file_object 是存放文件对象的变量,filename 是文件名的字符串,mode 是打开文件的模式,例如 "rt" 用于读取文本文件,"wt" 用于写入文本文件(还有一些我们此处不涉及的模式)。接下来,可以调用文件对象的函数。最常用的两个函数是 readwritewrite 函数将字符串添加到文件的末尾。read 函数读取文件中的下一个内容,并将其作为字符串返回。如果没有提供参数,它将返回整个文件内容(如示例所做的)。

现在,来看一个改进版的电话号码管理程序:

def print_numbers(numbers):
    print("Telephone Numbers:")
    for k, v in numbers.items():
        print("Name:", k, "\tNumber:", v)
    print()

def add_number(numbers, name, number):
    numbers[name] = number

def lookup_number(numbers, name):
    if name in numbers:
        return "The number is " + numbers[name]
    else:
        return name + " was not found"

def remove_number(numbers, name):
    if name in numbers:
        del numbers[name]
    else:
        print(name, " was not found")

def load_numbers(numbers, filename):
    in_file = open(filename, "rt")
    while True:
        in_line = in_file.readline()
        if not in_line:
            break
        in_line = in_line[:-1]
        name, number = in_line.split(",")
        numbers[name] = number
    in_file.close()

def save_numbers(numbers, filename):
    out_file = open(filename, "wt")
    for k, v in numbers.items():
        out_file.write(k + "," + v + "\n")
    out_file.close()

def print_menu():
    print('1. Print Phone Numbers')
    print('2. Add a Phone Number')
    print('3. Remove a Phone Number')
    print('4. Lookup a Phone Number')
    print('5. Load numbers')
    print('6. Save numbers')
    print('7. Quit')
    print()

phone_list = {}
menu_choice = 0
print_menu()

while True:
    menu_choice = int(input("Type in a number (1-7): "))
    if menu_choice == 1:
        print_numbers(phone_list)
    elif menu_choice == 2:
        print("Add Name and Number")
        name = input("Name: ")
        phone = input("Number: ")
        add_number(phone_list, name, phone)
    elif menu_choice == 3:
        print("Remove Name and Number")
        name = input("Name: ")
        remove_number(phone_list, name)
    elif menu_choice == 4:
        print("Lookup Number")
        name = input("Name: ")
        print(lookup_number(phone_list, name))
    elif menu_choice == 5:
        filename = input("Filename to load: ")
        load_numbers(phone_list, filename)
    elif menu_choice == 6:
        filename = input("Filename to save: ")
        save_numbers(phone_list, filename)
    elif menu_choice == 7:
        break
    else:
        print_menu()

print("Goodbye")

代码解释:

  1. print_numbers(numbers):打印电话号码字典 numbers 中的所有条目。
  2. add_number(numbers, name, number):将一个新的姓名和电话号码添加到字典中。
  3. lookup_number(numbers, name):查找某个姓名对应的电话号码,如果找到了返回号码,否则返回“未找到”。
  4. remove_number(numbers, name):删除某个姓名的电话号码,如果找不到该姓名则输出提示。
  5. load_numbers(numbers, filename):从指定的文件加载电话号码数据,并将其存入字典中。
  6. save_numbers(numbers, filename):将字典中的电话号码数据保存到指定文件中。
  7. print_menu():打印菜单,供用户选择操作。

该程序可以让用户:

  • 打印所有电话号码。
  • 添加、删除电话号码。
  • 查找电话号码。
  • 从文件加载电话号码。
  • 将电话号码保存到文件。
  • 退出程序。

通过使用 open 函数,你可以实现文件的读写操作,存储和加载数据。

注意,现在该程序已经包括了文件的保存和加载。下面是我运行程序两次的输出:

1. Print Phone Numbers
2. Add a Phone Number
3. Remove a Phone Number
4. Lookup a Phone Number
5. Load numbers
6. Save numbers
7. Quit

Type in a number (1-7): 2
Add Name and Number
Name: Jill
Number: 1234
Type in a number (1-7): 2
Add Name and Number
Name: Fred
Number: 4321
Type in a number (1-7): 1
Telephone Numbers:
Name: Jill     Number: 1234
Name: Fred     Number: 4321

Type in a number (1-7): 6
Filename to save: numbers.txt
Type in a number (1-7): 7
Goodbye

1. Print Phone Numbers
2. Add a Phone Number
3. Remove a Phone Number
4. Lookup a Phone Number
5. Load numbers
6. Save numbers
7. Quit

Type in a number (1-7): 5
Filename to load: numbers.txt
Type in a number (1-7): 1
Telephone Numbers:
Name: Jill     Number: 1234
Name: Fred     Number: 4321

Type in a number (1-7): 7
Goodbye

该程序的新部分是:

def load_numbers(numbers, filename):
    in_file = open(filename, "rt")
    while True:
        in_line = in_file.readline()
        if not in_line:
            break
        in_line = in_line[:-1]  # 去除行末的换行符
        name, number = in_line.split(",")
        numbers[name] = number
    in_file.close()

def save_numbers(numbers, filename):
    out_file = open(filename, "wt")
    for k, v in numbers.items():
        out_file.write(k + "," + v + "\n")  # 写入姓名、号码和换行符
    out_file.close()

首先,我们来看一下程序的保存部分。首先它使用 open(filename, "wt") 创建一个文件对象。接着,它通过命令 out_file.write(k + "," + v + "\n") 遍历电话号码字典,为每个电话号码创建一行。这将写入一行,包含姓名、逗号、号码和换行符。

加载部分稍微复杂一些。它首先获取一个文件对象,然后使用 while True: 循环不断读取文件直到遇到 break 语句。接着,它通过 in_line = in_file.readline() 获取一行数据。readline 函数在读取到文件末尾时会返回一个空字符串。if 语句检查这一点,文件结束时退出循环。当然,如果 readline 函数没有去除行末的换行符,就无法判断空字符串是空行还是文件结束,因此换行符被保留在读取的数据中。因此,我们需要通过 in_line = in_line[:-1] 来去除换行符。接下来,name, number = in_line.split(",") 会通过逗号将每一行拆分成姓名和号码,并将它们添加到字典 numbers 中。

高级使用 .txt 文件

你可能会想,“我知道如何读取和写入文本文件,但如果我想在不打开其他程序的情况下打印文件呢?”

有几种不同的方式可以实现这一点。最简单的方式确实会打开另一个程序,但所有的操作都在 Python 代码中完成,用户无需指定要打印的文件。此方法涉及调用另一个程序的子进程。

记得上面我们写入的文件吗?我们将使用那个文件。请注意,为了避免一些错误,这个程序使用了下一章中的一些概念。在下一章之前,请随时重新访问此示例。

import subprocess

def main():
    try:
        print("This small program invokes the print function in the Notepad application")
        # 让我们打印在上面程序中创建的文件
        subprocess.call(['notepad', '/p', 'numbers.txt'])
    except WindowsError:
        print("The called subprocess does not exist, or cannot be called.")

main()

subprocess.call 接受三个参数。在这个示例中,第一个参数应该是你希望调用打印子进程的程序名称。第二个参数应该是该程序中的具体子进程。为了简单起见,只需理解在本程序中,/p 是用来通过指定的应用程序访问打印机的子进程。最后一个参数应该是你希望发送到打印子进程的文件名。在这个例子中,它是上面章节中使用的相同文件。

练习
现在修改字典章节中的成绩程序,使其使用文件I/O来保存学生的记录。

解决方案
修改字典章节中的成绩程序,使其使用文件I/O来保存学生的记录。

assignments = ['hw ch 1', 'hw ch 2', 'quiz   ', 'hw ch 3', 'test']
students = { }

def load_grades(gradesfile):
    inputfile = open(gradesfile, "r")
    grades = [ ]
    while True:
        student_and_grade = inputfile.readline()
        student_and_grade = student_and_grade[:-1]
        if not student_and_grade:
            break
        else:
            studentname, studentgrades = student_and_grade.split(",")
            studentgrades = studentgrades.split(" ")
            students[studentname] = studentgrades
    inputfile.close()
    print("Grades loaded.")

def save_grades(gradesfile):
    outputfile = open(gradesfile, "w")
    for k, v in students.items():
        outputfile.write(k + ",")
        for x in v:
            outputfile.write(str(x) + " ")
        outputfile.write("\n")
    outputfile.close()
    print("Grades saved.")

def print_menu():
    print("1. Add student")
    print("2. Remove student")
    print("3. Load grades")
    print("4. Record grade")
    print("5. Print grades")
    print("6. Save grades")
    print("7. Print Menu")
    print("9. Quit")

def print_all_grades():
    if students:
        keys = sorted(students.keys())
        print('\t', end=' ')
        for x in assignments:
            print(x, '\t', end=' ')
        print()
        for x in keys:
            print(x, '\t', end=' ')
            grades = students[x]
            print_grades(grades)
    else:
        print("There are no grades to print.")

def print_grades(grades):
    for x in grades:
        print(x, '\t', end=' ')
    print()

print_menu()
menu_choice = 0
while menu_choice != 9:
    print()
    menu_choice = int(input("Menu Choice: "))
    if menu_choice == 1:
        name = input("Student to add: ")
        students[name] = [0] * len(assignments)
    elif menu_choice == 2:
        name = input("Student to remove: ")
        if name in students:
            del students[name]
        else:
            print("Student:", name, "not found")
    elif menu_choice == 3:
        gradesfile = input("Load grades from which file? ")
        load_grades(gradesfile)
    elif menu_choice == 4:
        print("Record Grade")
        name = input("Student: ")
        if name in students:
            grades = students[name]
            print("Type in the number of the grade to record")
            print("Type a 0 (zero) to exit")
            for i, x in enumerate(assignments):
                print(i + 1, x, '\t', end=' ')
            print()
            print_grades(grades)
            which = 1234
            while which != -1:
                which = int(input("Change which Grade: "))
                which -= 1
                if 0 <= which < len(grades):
                    grade = input("Grade: ")  # 修改为input()而非float(input()),以避免保存时出错
                    grades[which] = grade
                elif which != -1:
                    print("Invalid Grade Number")
        else:
            print("Student not found")
    elif menu_choice == 5:
        print_all_grades()
    elif menu_choice == 6:
        gradesfile = input("Save grades to which file? ")
        save_grades(gradesfile)
    elif menu_choice != 9:
        print_menu()

程序说明:

  • load_grades:该函数从指定文件加载成绩记录,并将数据存储在字典 students 中,格式为学生姓名和对应的成绩。
  • save_grades:该函数将学生的姓名和成绩保存到指定的文件中,每个学生的姓名和成绩之间以逗号隔开,成绩之间以空格隔开。
  • print_menu:此函数打印菜单选项,用户可以选择不同的操作。
  • print_all_grades:此函数打印所有学生的成绩,按照作业顺序列出。
  • print_grades:此函数打印单个学生的成绩。

操作菜单:

  1. 添加学生
  2. 移除学生
  3. 从文件加载成绩
  4. 记录成绩
  5. 打印成绩
  6. 保存成绩到文件
  7. 打印菜单
  8. 退出

通过文件I/O操作,程序可以持久化存储学生成绩,确保数据的保存和加载。

最后修改: 2025年01月11日 星期六 11:33