第十章:文件和异常


2018年10月25日

10.1 从文件中读取数据

要使用文本文件中的信息,首先需要将信息读取到内存中。为此可以一次性读取文件的全部内容,也可以以每次一行的方式逐步读取。

10.1.1 读取整个文件

下面的程序打开并读取存有三行圆周率值的文件,并打印其内容:

with open('pi_digits.txt') as file_object:
    contents = file_object.read()
    print(contents)

要以任何方式使用文件,都得先打开文件,这样才能访问它。在这里,open('pi_digits.txt')返回一个表示文件pi_digits.txt的对象;Python将这个对象存储在我们将在后面使用的变量中。
关键字with在不再需要访问文件后将其关闭。在这个程序中,注意到我们调用了open(),但没有调用close()。如果在程序中过早地调用close(),你会发现需要使用文件时它已关闭,这会导致更多的错误。并非在任何情况下都能轻松确定变比文件的恰当时机,但通过使用前面所示的结构,可让Python去确定:你只管打开文件,并在需要时使用它,Python自会在合适的时候自动将其关闭。

10.1.2 逐行读取

读取文件时,常常需要检查其中的每一行,要以每次一行的方式检查文件,可对文件对象使用for循环:

filename = 'pi_digits.txt'

with open(filename) as file_object:
    for line in file_object:
        print(line)

10.1.3 创建一个包含文件各行内容的列表

使用关键字with时,open()返回的文件对象只在with代码块内可用。如果要在with代码块外访问文件内容,可在with代码块内将文件的各行存储在一个列表中,并在with代码块外使用该列表。

filename = 'pi_digits.txt'

with open(filename) as file_object:
    lines = file_object.readlines()

for line in lines:
    print(line.rstrip())

10.1.4 使用文件的内容

读取文本文件时,Python将其中所有的文本都解读为字符串,因此处理文件内容就是处理字符串(或每行字符串作为元素的列表)。处理字符串的函数(如去除末尾换行符的rstrip(),替换字符串的replace,检查特定字符串是否出现的in运算符等)都可以在处理文本文件时使用。

10.2 写入文件

要将文本写入文件,在调用open()时需要提供另一个形参,告诉Python你要写入打开的文件。

filename = 'pi_digits.txt'

with open(filename, 'w') as file_object:
    file_object.write("I love programming.")

调用open()提供的第二个实参('w')表示以写入模式打开这个文件,另外可指定的有读取模式('r'),附加模式('a')或者能够读取和写入文件的模式('r+')。忽略模式实参时,Python将以默认的只读模式打开文件。
如果要写入的文件不存在,函数open()将自动创建它。然而,以写入('w')模式打开文件时千万要小心,因为如果指定的文件已经存在,Python将在返回文件对象前清空该文件。
函数write()不会在你写入的文本文本末尾添加换行符,因此要写入多行时需要在write()语句中包含换行符。
如果要给文件添加内容,而不是覆盖原有的内容,可以附加模式打开文件。以附加模式打开文件时,Python不会再返回文件对象前清空文件,而写入到文件的行都将添加到文件末尾。如果指定文件不存在,Python将为你创建一个空文件。

filename = 'pi_digits.txt'

with open(filename, 'a') as file_object:
    file_object.write("I also love finding meaning in large datasets.\n")
    file_object.write("I love creating apps that can run in a browser.\n")

10.3 异常

10.3.1 使用try-except代码块

Python使用被称为异常的特殊对象来管理程序执行期间发生的错误。每当发生让Python不知所措的错误时,它都会创建一个异常对象。如果你编写了处理该异常的代码,程序将继续运行;如果你未对异常进行处理,程序将停止,并显示一个traceback,其中包含有关异常的报告。
程序崩溃可不好,但让用户看到traceback也不是好主意。不懂技术的用户会被它们搞糊涂,而且如果用户怀有恶意,他会通过traceback获悉你不希望他知道的信息。训练有素的攻击者可根据这些信息判断出可对你的代码发起什么样的攻击。
异常是使用try-except-else-finally代码块处理的,其中elsefinally代码块是可选的。使用了try-except代码块时,即便出现异常,程序也将继续运行:显示你编写的友好的错误信息,而不是令用户迷惑的traceback。

try:
    print(5/0)
except ZeroDivisionError:
    print("You can't divide by zero!")

上述代码中,我们将导致错误的代码行print(5/0)放在了一个try代码块中,ZeroDivisionError是一个异常对象;如果try代码块中的代码导致了错误,Python将查找这样的except代码块,并运行其中的代码,即其中指定的错误与引发的错误相同。
发生错误时,如果程序还有工作没有完成,妥善地处理错误就尤其重要。这种情况经常会出现在要求用户提供输入的程序中;如果程序能够妥善地处理无效输入,就能再提示用户提供有效输入,而不至于崩溃。
下面来创建一个只执行除法运算的简单计算器:

print("Give me two numbers, and I'll divide them.")

while True:
    first_number = input("\nFirst number: ")
    second_number = input("Second number: ")
    try:
        answer = int(first_number) / int(second_number)
        print(answer)
    except ZeroDivisionError:
        print("You can't divide by 0!")
    except ValueError:
        print("Input are not numbers!")
    else:
        break;        

try-except-else-finally代码块的工作原理大致如下:Python尝试执行try代码块中的代码;只有可能引发异常的代码才需要放在try语句中。有时候,有一些仅在try代码块成功执行时才需要运行的代码;这些代码应放在·else代码块中。except代码块告诉Python,如果它尝试运行try代码块中的代码时引发了指定的异常,该怎么办。
这里需要注意的是,try块中可能引发错误的是第一行除法运算的代码,如果这一行代码中引发异常,那么程序会直接跳转到处理相应异常的except部分,后续的print语句不会执行,并且循环会继续下去,只有当除法运算成功执行时,才会运行后续的输出结果与else代码块中的跳出循环部分。

10.3.2 决定处理异常的方式

并非每次捕获到异常时都需要告诉用户,也可以在发生异常时一声不吭,就像什么都没有发生一样继续运行。要让程序在失败时一声不吭,可像通常那样编写try代码块,但在except代码块中使用pass语句来明确告诉Python什么都不要做。pass语句还充当了占位符,它提醒在程序的某个地方什么都没有做,并且以后也许要在这里做些什么。
应该根据实际情况来选择向用户报告错误或者在失败时一声不吭,Python的错误处理结构让能够细致地控制与用户分享错误信息的程度。
编写得很好并且经过详尽测试的代码不容易出现内部错误,如语法或逻辑错误,但只要程序依赖于外部因素,如用户输入、存在指定的文件、有网络链接,就有可能出现异常。

你可能感兴趣的:(第十章:文件和异常)