十、文件和异常
1.从文件中读取数据 with open(…) as file_object
open():函数,打开文件并返回一个表示文件的对象;
close():函数,关闭文件(一般不用此函数);
with:关键字,在不再需要访问文件后将其关闭;
使用方法read读取文件的全部内容。
(1)读取整个文件
相同文件夹下的文件和代码:
with open ('pi_digits.txt') as file_object:
contents = file_object.read()
print(contents)
输出结果:
3.1415926535
8979323846
2643383279
read() 到达文件末尾时返回一个空字符串,所以输出的末尾可能会多一个空行,可以在print 语句中使用rstrip()删除多出来的空行:
print(contents.rstrip())
(2)文件路径
文件在相同文件夹下的子文件夹中:
with open('files\pi_digits10.txt') as file_object:
contents = file_object.read()
print(contents)
使用绝对路径:
file_path='C:/Users/MM/AppData/Local/Programs/Python/Python37/wsx_practise/ygz_python/chapter10文件/pi_digits.txt'
with open(file_path) as file_object:
(3)逐行读取 for 循环
with open ('pi_digits.txt') as file_object:
for line in file_object:
print(line)
#print(line.rstrip())
输出结果:
3.1415926535
8979323846
2643383279
(4)创建一个包含文件各行内容的列表
使用关键字with 时,open() 返回的文件对象只在with 代码块内可用。如果要在with 代码块外访问文件的内容,可在with 代码块内将文件的各行存储在一个列表中,并在with 代码块外使用该列表:你可以立即处理文件的各个部分,也可推迟到程序后面再处理。
with open ('pi_digits.txt') as file_object:
lines = file_object.readlines() #存储在lines列表中
for line in lines:
print(line.rstrip())
(5)使用文件的内容
filename = 'pi_digits.txt'
with open(filename) as file_object:
lines = file_object.readlines()
pi_string = ''
for line in lines:
#pi_string += line.rstrip()
pi_string += line.strip()
print(pi_string)
print(len(pi_string))
输出结果:
3.141592653589793238462643383279
32
注:读取文本文件时,Python将其中的所有文本都解读为字符串。如果你读取的是数字,并要将其作为数值使用,就必须使用函数int() 将其转换为整数,或使用函数float() 将其转换为浮点数。
print(pi_string[:15] + "...")
print(len(pi_string))
输出结果:
3.1415926535897...
32
(6)找生日
file_name='pi_million_digits.txt'
with open(file_name) as file_object:
lines=file_object.readlines()
pi_string = ''
for line in lines:
pi_string += line.rstrip()
birthday = input("Please input your birthday:")
if birthday in pi_string:
print("Your birthday appears in the first part of pi!")
else:
print("Your birthday doesn't appear in the first part of pi.")
输出结果:
Please input your birthday:543
Your birthday appears in the first part of pi!
(7)练习
使用replace(old,new)替换,替换不改变原文件。
filename='dog.txt'
with open(filename) as fileobject:
lines = fileobject.readlines()
for line in lines:
message = line.replace('dog','cat')
print(message)
输出结果:
I have a cat.
My cat's name is doudou.
I like my cat very much.
2.写入文件
(1)写入空文件 open(文件名,模式)
‘r’:读取模式(默认)
‘w’:写入模式
‘a’:附加模式
‘r+’:读写模式
保存数据的最简单的方式之一是将其写入到文件中。通过将输出写入文件,即便关闭包含程序输出的终端窗口,这些输出也依然存在:你可以在程序结束运行后查看这些输出,可与别人分享输出文件,还可编写程序来将这些输出读取到内存中并进行处理。
filename='programming.txt'
with open(filename,'w') as fileobject:
fileobject.write("I love programming very much.")
如果你要写入的文件不存在,函数open() 将自动创建它。然而,以写入(‘w’ )模式打开文件时千万要小心,因为如果指定的文件已经存在,Python将在返回文件对象前清空该文件。
输出结果:programming.txt
注:Python只能将字符串写入文本文件。要将数值数据存储到文本文件中,必须先使用函数str() 将其转换为字符串格式。
(2)写入多行
函数write() 不会在你写入的文本末尾添加换行符,因此要让每个字符串都单独占一行,需要在write() 语句中包含换行符\n:
filename='programming.txt'
with open(filename,'w') as fileobject:
fileobject.write("I love programming.\n")
fileobject.write("I love creating new games.\n")
输出结果:programming.txt
还可以使用空格、制表符和空行来设置这些输出的格式.
(3)附加到文件
如果你要给文件添加内容,而不是覆盖原有的内容,可以附加模式 打开文件。你以附加模式打开文件时,Python不会在返回文件对象前清空文件,而你写入到文件的行都将添加到文件末尾。如果指定的文件不存在,Python将为你创建一个空文件。
filename='programming.txt'
with open(filename,'a') as fileobject:
fileobject.write("I also love reading.\n")
fileobject.write("I love reading books.\n")
3.异常
Python使用被称为异常 的特殊对象来管理程序执行期间发生的错误。每当发生让Python不知所措的错误时,它都会创建一个异常对象。如果你编写了处理该异常的代码,程序将继续运行;如果你未对异常进行处理,程序将停止,并显示一个traceback,其中包含有关异常的报告。
异常是使用try-except代码块处理的。try-except 代码块让Python执行指定的操作,同时告诉Python发生异常时怎么办。使用了try-except 代码块时,即便出现异常,程序也将继续运行:显示你编写的友好的错误消息,而不是令用户迷惑的traceback。
(1)处理ZeroDivisionError异常
print(5/0) #报错
(2)使用try-except代码块
try:
print(5/0)
except ZeroDivisionError:
print("You can't divide by zero!")
输出结果:
You can't divide by zero!
(3)使用异常避免崩溃
让用户看到traceback不是好主意。不懂技术的用户会被它们搞糊涂,而且如果用户怀有恶意,他会通过traceback获悉你不希望他知道的信息。例如,他将知道你的程序文件的名称,还将看到部分不能正确运行的代码。有时候,训练有素的攻击者可根据这些信息判断出可对你的代码发起什么样的攻击。
(4)else代码块(接在try-except代码块后)
print("Input two numbers,and I will divide them.")
print("Enter 'q' to quit.")
while True:
first_num = input("First number:")
if first_num == "q":
break
second_num = input("second number:")
try:
answer=int(first_num)/int(second_num)
except ZeroDivisionError:
print("You can't divide by zero!")
else:
print(answer)
输出结果:
Input two numbers,and I will divide them.
Enter 'q' to quit.
First number:8
second number:5
1.6
First number:5
second number:0
You can't divide by zero!
First number:q
(5)处理FileNotFoundError异常
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)
(6)分析文本
计算文件大致包含多少个单词(调用split()拆分文本,再求长度):
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()
num_words = len(words)
print("The file "+filename+" has about "+ str(num_words) +" words.")
输出结果:
The file alice.txt has about 29461 words.
(7)使用多个文件
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()
num_words = len(words)
print("The file "+filename+" has about "+ str(num_words) +" words.")
filenames = ['alice.txt','siddhartha.txt','little_women.txt','moby_dict.txt']
for filename in filenames:
count_words(filename)
输出结果:
The file alice.txt has about 29461 words.
Sorry,the file siddhartha.txt doesn't exist.
The file little_women.txt has about 189079 words.
The file moby_dict.txt has about 215136 words.
使用try-except 代码块提供了两个重要的优点:避免让用户看到traceback;让程序能够继续分析能够找到的其他文件。
(8)失败时一声不吭
在except代码块中使用pass语句:
except FileNotFoundError:
pass
(9)决定报告哪些错误
4.存储数据
使用模块json(JavaScript Object Notation)来存储数据。模块json 让你能够将简单的Python数据结构转储到文件中,并在程序再次运行时加载该文件中的数据。你还可以使用json 在Python程序之间分享数据。更重要的是,JSON数据格式并非Python专用的,这让你能够将以JSON格式存储的数据与使用其他编程语言的人分享。
(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) #参数:要存储的数据,存储数据的文件对象
文件numbers.json:
- 使用json.load()来读取数字列表到内存中:
import json
filename = 'numbers.json'
with open(filename) as f_obj:
numbers = json.load(f_obj)
print(numbers)
(2)保存和读取用户生成的数据
用户首次运行程序时被提示输入自己的名字,这样再次运行程序时就记住他了。
import json
# 如果以前存储了用户名,就加载它
# 否则,就提示用户输入用户名并存储它
filename = 'username.json'
try:
with open(filename) as f_obj:
username = json.load(f_obj)
except FileNotFoundError:
username = input("What's 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+"!")
(3)重构
代码能够正确地运行,但可做进一步的改进——将代码划分为一系列完成具体工作的函数。这样的过程被称为重构 。
将上面代码放到greet_user()函数中并进行划分:
import json
def get_stored_username():
"""如果存储了用户名,就读取它"""
filename = 'username.json'
try:
with open(filename) as f_obj:
username = json.load(f_obj)
except FileNotFoundError:
return None
else:
return username
def get_new_username():
"""提示用户输入用户名"""
filename = 'username.json'
username = input("What's your name? ")
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()
这样,每个函数都执行单一而清晰的任务。要编写出清晰而易于维护和扩展的代码,这种划分工作必不可少。
参考文献:袁国忠,Python编程:从入门到实践