本章内容:
- 处理文件,让程序能够快速地分析大量的数据;
- 学习错误处理,避免程序在面对意外情形时崩溃;
- 学习异常,它们是python创建的特殊对象,用于管理程序运行时出现的错误;
- 学习json模块,它能保存用户数据,以免在程序停止运行后丢失。
1、从文件中读取数据
要使用文本文件中的信息,首先需要将信息读取到内存中。为此,你可以一次性读取文件的全部内容,也可以以每次一行的方式逐步读取。
(1)读取整个文件
下面创建一个文件。它包含精确到小数点后30位的圆周率值,且在小数点后每10位处都换行:
pi_digits.txt
3.1415926535
8979323846
2643383279
将文件保存在程序所在目录中:

file_reader.py
#coding:utf-8
with open('pi_digits.txt') as file_object:
contents = file_object.read()
print(contents)
输出结果:
3.1415926535
8979323846
2643383279
上面例子中:
- 函数open()接受一个参数:要打开的文件的名称,python在当前执行的文件所在的目录中查找指定的文件, 本例中,在file_reader.py所在的目录中查找pi_digits.txt文件;
- 关键字with在不再需要访问文件后将其关闭;
- 有了表示pi_digits.txt的文件对象后,使用方法read()读取这个文件的全部内容,并将其作为字符串存储在变量contents中。
在输出结果最后一行会多出一个空格,因为read()到达文件末尾时返回一个空字符串,而将这个空字符串显示出来时就是一个空行。要删除末尾的空行,可在print语句中使用rstrip():
#coding:utf-8
with open('pi_digits.txt') as file_object:
contents = file_object.read()
print(contents.rstrip())
(2)文件路径
使用相对文件路径打开文件夹中的文件:
在Linux和OS X中:
with open('text_files/filename.txt') as file_object:
在Windows系统中,使用反斜杠(\)而不是斜杠(/):
with open('text_files\filename.txt') as file_object:
使用绝地文件路径打开文件夹中的文件:
在Linux和OS X中:
file_path = '/home/ehmatthes/other_files/text_files/filename.txt'
with open(file_path) as file_object:
在Windows系统中:
file_path = 'C:\python_study\text_files\filename.txt'
with open(file_path) as file_object:
注意:
如果你使用的是Windows系统,且结果不符合预期,请确保在文件路径中使用的是反斜杠。另外,由于反斜杠在python中被视为转义标记,为在Windows中确保万无一失,应以原始字符串的方式指定路径,即在开头的单引号前加上r。
(3)逐行读取
读取文件时,常常需要检查其中的每一行:你可能要在文件中查找特定的信息,或者要以某种方式修改文件中的文本。
要以每次一行的方式检查文件,可对文件对象使用for循环:
#coding:utf-8
filename = 'pi_digits.txt'
with open(filename) as file_object:
for line in file_object:
print(line)
输出结果:
3.1415926535
8979323846
2643383279
以上例子中:
- 我们将要读取的文件的名称存储在变量filename中,调用open()后,将一个表示文件及其内容的对象存储到了变量file_object中,为查看文件内容,我们通过对文件对象执行循环来遍历文件中的每一行;
- 输出结果会出现空白行,是因为在这个文件中,每行的末尾都有一个看不见的换行符,而print语句也会加上一个换行符,因此每行末尾都有两个换行符:一个来自文件,另一个来自print语句,要消除这些多余的空白行,可在print语句中使用rstrip():
#coding:utf-8
filename = 'pi_digits.txt'
with open(filename) as file_object:
for line in file_object:
print(line.rstrip())
输出结果:
3.1415926535
8979323846
2643383279
(4)创建一个包含文件各行内容的列表
使用关键字with时,open()返回的文件对象只在with代码块内可使用。如果要在with代码块以外访问文件的内容时,可在with代码块内将文件的各行存储在一个列表中,并在with代码块外使用该列表:你可以立即处理文件的各个部分,也可以推迟到程序后面再处理。
#coding:utf-8
filename = 'pi_digits.txt'
with open(filename) as file_object:
lines = file_object.readlines()
for line in lines:
print(line.rstrip())
输出结果:
3.1415926535
8979323846
2643383279
以上代码中,方法readlines()从文件中读取每一行,并将其存储再一个列表中;接下来,该列表被存储到变量lines中,在with代码块外,我们仍然可以使用这个变量。
(5)使用文件的内容
将文件读取到内存中后,就可以以任何方式使用这些数据了:
#coding:utf-8
filename = 'pi_digits.txt'
with open(filename) as file_object:
lines = file_object.readlines()
pi_string = ''
for line in lines:
pi_string += line.rstrip()
print(pi_string)
print(len(pi_string))
输出结果:
3.1415926535 8979323846 2643383279
36
在变量pi_string存储的字符串中,包含原来位于每行左边的空格,为删除这些空格,可使用strip()而不是rstrip():
#coding:utf-8
filename = 'pi_digits.txt'
with open(filename) as file_object:
lines = file_object.readlines()
pi_string = ''
for line in lines:
pi_string += line.strip()
print(pi_string)
print(len(pi_string))
输出结果:
3.141592653589793238462643383279
32
注意:读取文本文件时,python将其中的所有文本都解读为字符串,如果你读取的是数字,并要将其作为数值使用,就必须使用函数int()将其转换为整数,或使用函数float()将其转换为浮点数。
(6)包含一百万位的大型文件
#coding:utf-8
filename = 'pi_digits.txt'
with open(filename) as file_object:
lines = file_object.readlines()
pi_string = ''
for line in lines:
pi_string += line.strip()
print(pi_string[:52] + "...")
print(len(pi_string))
输出结果:
3.14159265358979323846264338327912345678952356565623...
36177008
对于你可以处理的数据量,python没有任何限制,只要系统的内存足够多,想处理多少数据都可以。
(7)圆周率中包含你的生日吗
#coding:utf-8
filename = 'pi_digits.txt'
with open(filename) as file_object:
lines = file_object.readlines()
pi_string = ''
for line in lines:
pi_string += line.strip()
birthday = input("Enter your birthday,in the form mmddyy:")
if birthday in pi_string:
print("Your birthday appears in the first million digits of pi!")
else:
print("Your birthday does not appear in the first million digits of pi.")
输出结果:
Enter your birthday,in the form mmddyy:910213
Your birthday appears in the first million digits of pi!
2、写入文件
保存数据的最简单的方式之一就是将其写入到文件中。通过将输出写入文件,即便关闭包含程序输出的终端窗口,这些输出也依然存在:你可以在程序结束运行后查看这些输出,可与别人分享输出文件,还可编写程序来将这些输出读取到内存中并进行处理。
(1)写入空文件
要将文本写入文件,你在调用open()时需要提供另一个实参,告诉python你要写入打开的文件:
#coding:utf-8
filename = 'programming.txt'
with open(filename,'w') as file_object:
file_object.write("I love programming.")
运行结果:

以上示例中:
- 调用open()时提供了两个实参,第一个实参也是要打开的文件名称,第二个实参('w')告诉python,我们要以写入模式打开这个文件。打开文件时,可以指定读取模式('r')、写入模式('w')、附加模式('a')或让你能够读取和写入文件的模式('r+')。如果你省略了模式实参,python将以默认只读的方式打开文件。
- 如果你要写入的文件不存在,函数open()将自动创建它。然而,以写入('w')模式打开文件时千万小心,因为如果指定的文件已经存在,python将在返回文件对象清空该文件。
注意:python只能将字符串写入文本文件中,要将数值数据存储到文本文件中,必须先使用函数str()将其转换为字符串格式。
(2)写入多行
函数write()不会在你写入的文本末尾添加换行符:
#coding:utf-8
filename = 'programming.txt'
with open(filename,'w') as file_object:
file_object.write("I love programming.")
file_object.write("I love creating new games.")
运行结果:

要让每个字符串都单独占一行,需要在write()语句中包含换行符:
#coding:utf-8
filename = 'programming.txt'
with open(filename,'w') as file_object:
file_object.write("I love programming.\n")
file_object.write("I love creating new games.\n")
运行结果:

(3)附加到文件
如果你要给文件添加内容,而不是覆盖原有的内容,可以附加模式打开文件:
#coding:utf-8
filename = 'programming.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")
运行结果:

最终运行的结果是,文件原来的内容还在,他们后面是我们刚添加的内容。
3、异常
python使用被称为异常的特殊对象来管理程序执行期间发生的错误。每当发生让python不知所措的错误时,它都会创建一个异常对象。如果你编写了处理异常的代码,程序将继续运行,如果未对异常进程处理,程序将停止,并显示一个traceback,其中包含有关异常的报告。
异常是使用try-except代码块处理的。try-except代码块让python执行指定的操作,同时告诉python发生异常时怎么办。使用了try-except代码块时,即便出现异常,程序也将继续运行:显示你编写的友好的错误信息,而不是令用户迷惑的traceback。
(1)处理ZeroDivisionError异常
下面来看一种导致python引发异常的简单错误:

(2)使用try-except代码块
#coding:utf-8
try:
print(5/0)
except ZeroDivisionError:
print("You can't divide by zero!")
输出结果:
You can't divide by zero!
上述代码中:
- 将导致错误的代码行print(5/0)放在了一个try代码块中;
- 如果try代码块中的代码运行起来没有问题,python将跳过except代码块;
- 如果try代码块中的代码导致了错误,python将查找这样的except代码块,并运行其中的代码,即其中指定的错误与引发的错误相同;
- 如果try-except代码块后面还有其他代码,程序将接着运行,因为已经告诉了python何处处理这种错误。
(3)使用异常避免崩溃
发生错误时,如果程序还有工作没有完成,妥善的处理错误就尤为重要。这种情况经常会出现在要求用户输入的程序中;如果程序能够妥善处理无效输入,就能再提示用户提供有效输入,而不至于崩溃。
print("Give me two numbers,and I'll divide them.")
print("Enter 'q' to quit." )
while True:
first_number = input("\nFirst number:")
if first_number == 'q':
break
second_number = input("Second number:")
if second_number == 'q':
break
answer = int(first_number)/int(second_number)
print(answer)
以上程序中没有采取任何处理错误的措施,因此让他执行除数为0的除法运算时,它将崩溃:
d:\>python test.py
Give me two numbers,and I'll divide them.
Enter 'q' to quit.
First number:2
Second number:0
Traceback (most recent call last):
File "test.py", line 12, in <module>
answer = int(first_number)/int(second_number)
ZeroDivisionError: division by zero
(4)else代码块
通过将可能引发错误的代码放在try-except代码块中,可提高这个程序抵御错误的能力:
print("Give me two numbers,and I'll divide them.")
print("Enter 'q' to quit." )
while True:
first_number = input("\nFirst number:")
if first_number == 'q':
break
second_number = input("Second number:")
if second_number == 'q':
break
try:
answer = int(first_number)/int(second_number)
except ZeroDivisionError:
print("You can't divide by 0!")
else:
print(answer)
运行结果:
Give me two numbers,and I'll divide them.
Enter 'q' to quit.
First number:2
Second number:0
You can't divide by 0!
First number:5
Second number:1
5.0
try-except-else代码块的工作原理大致如下:python尝试执行try代码块中的代码;只有可能引发异常的代码才需要放在try语句中。有时候,有一些仅在try代码块成功执行时才需要运行的代码;这些代码应放在else代码块中。except代码块告诉python,如果它尝试运行try代码块中的代码时引发了指定的异常,该怎么办。
(5)处理FileNotFoundError异常
使用文件时,一种常见的问题是找不到文件:你要查找的文件可能在其他地方、文件名可能不正确或者这个文件根本就不存在。对于所有这些情形,都可使用try-except代码块以直观的方式进行处理。
对于不存在的文件:
filename = 'alice.txt'
with open(filename) as f_obj:
contents = f_obj.read()
运行时,python无法读取不存在的文件,会发生下面的错误:
d:\>python test.py
Traceback (most recent call last):
File "test.py", line 3, in <module>
with open(filename) as f_obj:
FileNotFoundError: [Errno 2] No such file or directory: 'alice.txt'
上面示例中,这个错误是函数open()导致的,所以将try语句放在包含open()的代码行之前:
filename = 'alice.txt'
try:
with open(filename) as f_obj:
contents = f_obj.read()
except FileNotFoundError:
msg = "Sorry,the file " + filename + " doesn't exist."
print(msg)
运行结果:
d:\>python test.py
Sorry,the file alice.txt doesn't exist.
(6)分析文本
下面来提取通话Alice in Wonderland的文本,并尝试计算它包含多少个单词。我们使用方法split(),它根据一个字符串创建一个单词列表:

方法split()以空格为分隔符将字符串分拆成多个部分,并将这些部分都存储到一个列表中。
现将alice.txt文件放在正确的目录下:

filename = 'alice.txt'
try:
with open(filename) as f_obj:
contents = f_obj.read()
except FileNotFoundError:
msg = "Sorry,the file " + filename + " doesn't exist."
print(msg)
else:
#计算文件大致包含几个单词
words = contents.split()
print(words)
num_words = len(words)
print("The file " + filename +" has about " +str(num_words) +" words.")
运行结果:
d:\>python test.py
['Study', 'hard,', 'and', 'make', 'prograss', 'everyday!']
The file alice.txt has about 6 words.
(7)使用多个文件

#coding:utf-8
def count_words(filename):
"""计算一个文件大致包含几个多少个单词"""
try:
with open(filename) as f_obj:
contents = f_obj.read()
except FileNotFoundError:
msg = "Sorry,the file " + filename + " doesn't exist."
print(msg)
else:
#计算文件大致包含几个单词
words = contents.split()
print(words)
num_words = len(words)
print("The file " + filename +" has about " +str(num_words) +" words.")
filenames = ['alice.txt','siddhartha.txt','moby_dick.txt','little_women.txt']
for filename in filenames:
count_words(filename)
输出结果:
d:\>python test.py
['Study', 'hard,', 'and', 'make', 'prograss', 'everyday!']
The file alice.txt has about 6 words.
['Gautama', 'Buddha', '(c.', '563', 'BCE/480', 'BCE', '–', 'c.', '483', 'BCE/400', 'BCE),', 'also', 'known', 'as', 'Siddhārtha', 'Gautama', '[sid???ɑ?rt???', 'g?ut??m?],', 'Shakyamuni', 'Buddha', '[?ɑ?kj?mun?i', 'bud????],[4]', 'or', 'simply', 'the', 'Buddha,', 'after', 'the', 'title', 'of', 'Buddha,', 'was', 'an', 'ascetic', '(?rama?a)', 'and', 'sage,[4]', 'on', 'whose', 'teachings', 'Buddhism', 'was', 'founded.[5]', 'He', 'is', 'believed', 'to', 'have', 'lived', 'and', 'taught', 'mostly', 'in', 'the', 'eastern', 'part', 'of', 'ancient', 'India', 'sometime', 'between', 'the', 'sixth', 'and', 'fourth', 'centuries', 'BCE.[6][note', '3]', 'Gautama', 'taught', 'a', 'Middle', 'Way', 'between', 'sensual', 'indulgence', 'and', 'the', 'severe', 'asceticism', 'found', 'in', 'the', '?rama?a', 'movement[7]', 'common', 'in', 'his', 'region.', 'He', 'later', 'taught', 'throughout', 'other', 'regions', 'of', 'eastern', 'India', 'such', 'as', 'Magadha', 'and', 'Kosala.[6][8]', 'Gautama', 'is', 'the', 'primary', 'figure', 'in', 'Buddhism.', 'He', 'is', 'recognized', 'by', 'Buddhists', 'as', 'an', 'enlightened', 'teacher', 'who', 'attained', 'full', 'Buddhahood,', 'and', 'shared', 'his', 'insights', 'to', 'help', 'sentient', 'beings', 'end', 'rebirth', 'and', 'suffering.', 'Accounts', 'of', 'his', 'life,', 'discourses,', 'and', 'monastic', 'rules', 'are', 'believed', 'by', 'Buddhists', 'to', 'have', 'been', 'summarized', 'after', 'his', 'death', 'and', 'memorized', 'by', 'his', 'followers.', 'Various', 'collections', 'of', 'teachings', 'attributed', 'to', 'him', 'were', 'passed', 'down', 'by', 'oral', 'tradition', 'and', 'first', 'committed', 'to', 'writing', 'about', '400', 'years', 'later.']
The file siddhartha.txt has about 181 words.
Sorry,the file moby_dick.txt doesn't exist.
[]
The file little_women.txt has about 0 words.
(8)失败时一声不吭
在上面示例中,我们告诉用户有一个文件找不到。但并非每次捕获到异常时都需要告诉用户,有时候你希望程序在发生异常时一声不吭,就像什么都没有发生一样继续运行。python有一个pass语句,可在代码块中使用它来让python什么都不做:
#coding:utf-8
def count_words(filename):
"""计算一个文件大致包含几个多少个单词"""
try:
with open(filename) as f_obj:
contents = f_obj.read()
except FileNotFoundError:
pass
else:
#计算文件大致包含几个单词
words = contents.split()
print(words)
num_words = len(words)
print("The file " + filename +" has about " +str(num_words) +" words.")
filenames = ['alice.txt','siddhartha.txt','moby_dick.txt','little_women.txt']
for filename in filenames:
count_words(filename)
运行结果:
d:\>python test.py
['Study', 'hard,', 'and', 'make', 'prograss', 'everyday!']
The file alice.txt has about 6 words.
['Gautama', 'Buddha', '(c.', '563', 'BCE/480', 'BCE', '–', 'c.', '483', 'BCE/400', 'BCE),', 'also', 'known', 'as', 'Siddhārtha', 'Gautama', '[sid???ɑ?rt???', 'g?ut??m?],', 'Shakyamuni', 'Buddha', '[?ɑ?kj?mun?i', 'bud????],[4]', 'or', 'simply', 'the', 'Buddha,', 'after', 'the', 'title', 'of', 'Buddha,', 'was', 'an', 'ascetic', '(?rama?a)', 'and', 'sage,[4]', 'on', 'whose', 'teachings', 'Buddhism', 'was', 'founded.[5]', 'He', 'is', 'believed', 'to', 'have', 'lived', 'and', 'taught', 'mostly', 'in', 'the', 'eastern', 'part', 'of', 'ancient', 'India', 'sometime', 'between', 'the', 'sixth', 'and', 'fourth', 'centuries', 'BCE.[6][note', '3]', 'Gautama', 'taught', 'a', 'Middle', 'Way', 'between', 'sensual', 'indulgence', 'and', 'the', 'severe', 'asceticism', 'found', 'in', 'the', '?rama?a', 'movement[7]', 'common', 'in', 'his', 'region.', 'He', 'later', 'taught', 'throughout', 'other', 'regions', 'of', 'eastern', 'India', 'such', 'as', 'Magadha', 'and', 'Kosala.[6][8]', 'Gautama', 'is', 'the', 'primary', 'figure', 'in', 'Buddhism.', 'He', 'is', 'recognized', 'by', 'Buddhists', 'as', 'an', 'enlightened', 'teacher', 'who', 'attained', 'full', 'Buddhahood,', 'and', 'shared', 'his', 'insights', 'to', 'help', 'sentient', 'beings', 'end', 'rebirth', 'and', 'suffering.', 'Accounts', 'of', 'his', 'life,', 'discourses,', 'and', 'monastic', 'rules', 'are', 'believed', 'by', 'Buddhists', 'to', 'have', 'been', 'summarized', 'after', 'his', 'death', 'and', 'memorized', 'by', 'his', 'followers.', 'Various', 'collections', 'of', 'teachings', 'attributed', 'to', 'him', 'were', 'passed', 'down', 'by', 'oral', 'tradition', 'and', 'first', 'committed', 'to', 'writing', 'about', '400', 'years', 'later.']
The file siddhartha.txt has about 181 words.
[]
The file little_women.txt has about 0 words.
4、存储数据
模块json()让你能够将简单的python数据结构转储到文件中,并在程序再次运行时加载该文件中的数据。
(1)使用json.dump()和json.load()
我们来编写一个存储一组数字的简短程序,再编写一个将这些数字读取到内存中的程序。第一个程序将使用json.dump()来存储这组数字,第二个程序将使用json.load()。
函数json.dump()接受两个实参:要存储的数据以及可用于存储数据的文件对象:
import json
numbers = [2,3,5,7,11,13]
filename = 'numbers.json'
with open(filename,'w') as f_obj:
json.dump(numbers,f_obj)
以上程序中:
- 指定了要将数字列表存储到其中的文件的名称;
- 通常使用文件扩展名.json来指出文件存储的数据为JSON格式;
-
以写入模式打开文件,让json能够将数据写入其中,使用函数json.dump()将数字列表存储到文件numbers.json中。
图片.png
下面使用函数json.load()将这个列表读取到内存中:
import json
filename = 'numbers.json'
with open(filename) as f_obj:
numbers = json.load(f_obj)
print(numbers)
输出结果:
D:\>python test_01.py
[2, 3, 5, 7, 11, 13]

(2)保存和读取用户生成的数据
保存数据::
import json
username = input("What is your name?")
filename = 'username.json'
with open(filename,'w') as f_obj:
json.dump(username,f_obj)
print("We'll remember you when you come back, " + username + "!")
运行结果:
D:\>python test.py
What is your name?chuanchuan
We'll remember you when you come back, chuanchuan!

读取数据:
import json
filename = 'username.json'
with open(filename) as f_obj:
username = json.load(f_obj)
print("Welcome back, " + username + "!")
运行结果:
D:\>python test_01.py
Welcome back, chuanchuan!

下面将存储和读取过程合在一处:
#coding:utf-8
import json
#如果以前存储了用户名,就加载它,否则,就提示用户输入用户名并存储它
filename = 'username_01.json'
try:
with open(filename) as f_obj:
username = json.load(f_obj)
except FileNotFoundError:
username = input("What is your name?")
with open(filename,'w') as f_obj:
json.dump(username,f_obj)
print("We'll remember you when you come back, " + username + "!")
else:
print("Welcome back, " + username + "!")
运行结果:
D:\>python test.py
What is your name?yichuan
We'll remember you when you come back, yichuan!
D:\>python test.py
Welcome back, yichuan!

(3)重构
经常会遇到这样的情况:代码能够正确的运行,但可做进一步的改进——将代码划分为一系列完成具体工作的函数。这样的过程被称为重构。重构让代码更清晰、更易于理解、更容易扩展。
#coding:utf-8
import json
def greet_user():
"""问候用户,并指出其名字"""
filename = 'username_02.json'
try:
with open(filename) as f_obj:
username = json.load(f_obj)
except FileNotFoundError:
username = input("What is your name?")
with open(filename,'w') as f_obj:
json.dump(username,f_obj)
print("We'll remember you when you come back, " + username + "!")
else:
print("Welcome back, " + username + "!")
greet_user()
运行结果:
D:\>python test.py
What is your name?yisan
We'll remember you when you come back, yisan!
D:\>python test.py
Welcome back, yisan!

下面来重构上述函数:
#coding:utf-8
import json
def get_stored_username():
"""如果存储了用户名,就获取他"""
filename = 'username_03.json'
try:
with open(filename) as f_obj:
username = json.load(f_obj)
except FileNotFoundError:
return None
else:
return username
def greet_user():
"""问候用户,并指出其名字"""
username = get_stored_username()
if username:
print("Welcome back, " + username + "!")
else:
username = input("What is your name?")
filename = "username_03.json"
with open(filename,'w') as f_obj:
json.dump(username,f_obj)
print("We'll remember you when you come back, " + username + "!")
greet_user()
运行结果:
D:\>python test.py
What is your name?chengchuanchuan
We'll remember you when you come back, chengchuanchuan!
D:\>python test.py
Welcome back, chengchuanchuan!

我们还可以将函数greet_user()中的另一个代码块提取出来:将没有存储用户名时提示用户名输入的代码放在一个独立的函数中:
#coding:utf-8
import json
def get_stored_username():
"""如果存储了用户名,就获取他"""
filename = 'username_04.json'
try:
with open(filename) as f_obj:
username = json.load(f_obj)
except FileNotFoundError:
return None
else:
return username
def get_new_username():
"""提示用户输入用户名"""
username = input("What is your name?")
filename = "username_04.json"
with open(filename,'w') as f_obj:
json.dump(username,f_obj)
return username
def greet_user():
"""问候用户,并指出其名字"""
username = get_stored_username()
if username:
print("Welcome back, " + username + "!")
else:
username = get_new_username()
print("We'll remember you when you come back, " + username + "!")
greet_user()
运行结果:
D:\>python test.py
What is your name?jack
We'll remember you when you come back, jack!
D:\>python test.py
Welcome back, jack!

网友评论