本章中你将学习处理文件,让程序能够快速地分析大量数据;你将学习错误处理,避免程序在面对意外情形时崩溃;你将学习异常,他们是Python创建的特殊对象,用于管理程序运行时出现的错误;你还将学习模块json,它让你能够保存用户数据,以免在程序停止运行后丢失。
文本文件可存储的数据量多得难以置信,天气数据,交通数据,社会经济数据等。因此读取文件很重要,对数据分析应用程序来说尤其如此。
示例:编写一个程序,实现读取一个文本文件内容,重新设置这些数据的格式再写入文件中,让浏览器可以显示这些内容。
首先需要将信息读取到内存中,你可以一次性读取文件的全部内容,也可以每次一行的方式逐步读取。
首先创建一个名为pi_digits.txt的文件,并写入一些文字。
读取文件
with open('pi_digits.txt') as file_object:
contents=file_object.read()
print(contents)
提示:
要让Python打开不与程序文件位于同一个目录下的文件,需要提供文件路径,它让Python到特定的位置去查找。
可以使用相对路径和绝对路径。
相对路径
with open('text_files/filename.txt') as file_object:
注意:在Windows系统中,路径要使用反斜杠 \ ;另外由于反斜杠在Python中被视为转义字符,为确保万无一失,应以原始字符串的方式指定路径,即在开头的单引号前加上r。
绝对路径
file_path='/home/ehmatthes/other_files/text_files/filename.txt'
with open(file_path) as file_object:
要以每次一行的方式检查文件,可对文件对象使用for循环。
fielname='pi_digits.txt'
with open(filename) as file_object:
for line in file_object:
print(line)
注意:输出结果为每行后有两个空行,因为在这个文件中,每行的末尾都有一个看不见的换行符,而print语句也会加上一个换行符,因此每行后都有两个换行符:一个来自文件,另一个来自print语句。
要消除这些多余的空白行,可在print语句后使用rstrip().
filename='pi_digits.txt'
with open(filename) as file_object:
for line in file_object:
print(line.rstrip())
使用关键字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())
将文件读取到内存后就可以以任何方式读取这些数据了。
示例:将包含圆周率的文本打开,把其中的数字放入一个字符串中,且没有任何空格。
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))
文件中包含一百万位的数字,这里我们只打印到小数点后两位,以免终端为显示全部而不断翻滚。
filename='pi_million_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))
扩展刚才编写的程序,以确定某人的生日是否包含在圆周率的前一百万位中。
可将生日表示为一个由数字组成的字符串,再检查这个字符串是否包含在其中。
filename='pi_million_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.")
保存数据最简单的方式之一是将其写入到文件中。通过将输出写入文件,即便关闭包含程序输出的最终端口,这些输出也依旧存在:你可以在程序结束运行后查看这些输出,可以与别人分享输出文件,还可以编写程序来将这些输出读取到内存中并处理。
调用open()时提供两个实参,一个为要打开文件的名称,第二个为告诉Python为写入。
打开文件可指定读取r,写入 ‘w’,附加 ‘a’,或让你能够读取和写入的’ r+’
注意:如果你要写入的文件不存在,函数open()将自动创建它。然而以写入(‘w’)模式打开文件时,如果指定的文件已经存在,Python将在返回文件对象前清空该文件。
Python只能将字符串写入文本文件,要将数值写入文本文件,必须要先使用函数str()将其转换为字符串格式。
filename='programming.txt'
with open(filename,'w') as file_object:
file_object.write("I love programming.")
函数write()不会在你写入文件的末尾添加换行符,因此如果你写入多行时没有指定换行符,文件看起来可能不是你希望的那样。
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")
如果你要给文件添加内容,而不是覆盖原有的内容,可以附加模式打开文件。同时,如果文件不存在,Python也将为你创建一个空文件。
filename='programming.txt'
with open(filename,'a') as file_object:
file_object("I am love finding meaning in large datasets.\n")
file_object("I love creating apps that can run in a browser.\n")
Python使用被称为异常的特殊对象来管理程序执行期间发生的错误。每当发生让Python不知所措的错误时,它都会创建一个异常对象,如果你编写了处理异常的代码,程序将继续运行;否则,程序将停止运行,并显示一个traceback。
异常是使用try–except代码块处理的。
你可能知道不能将一个数字除以0,但我们还是让Python这样做吧!
print(5/0)
在这种情况下,Python将停止运行程序,并指出引发了那种异常,而我们可根据这些信息对程序进行修改。下面我们将告诉Python,发生这种错误该怎么办。
try:
print(5/0)
except ZeroDivisionError:
print("You can't divide by zero!")
发生错误时,如果程序还有工作没有完成,妥善地处理错误就尤其重要。这种情况会出现在要求用户提供输入的程序中;如果程序能够妥善地处理无效输入,就能再提示用户输入有效数据,而不会崩溃。
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:")
try:
answer=int(first_number)/int(second_number)
except ZeroDivisionError:
print("you can't divide by 0 !")
else:
print(answer)
在使用文件时,一种常见的问题是找不到文件:你要查找的文件可能在其他地方,文件名可能不正确或者这个文件根本就不存在。对于这些情形,都可以使用try–except代码块来处理。
filename='alice.txt'
with open(filename) as f_obj:
contents=f_obj.read()
更改为:
filename='alice.txt'
try:
with open(filename) as f_obj:
contents=f_obj.read()
except FileNotFoundError:
msg="sorry,the file "+filename+" does not exist."
print(msg)
使用方法split(),方法sploit(),以空格为分隔符将字符串拆分为多个部分,并将这些部分都存储到一个列表中。结果为一个包含字符串所有单词的列表。
title="Alice in wonderland"
a=title.split()
print(a)
故我们用这个方法来计算小说的单词量。
将小说放入文本alice.txt
filename='alice.txt'
try:
with open(filename) as f_obj:
contents=f_obj.read()
except FileNotFoundError:
msg="sorry the file "+filename+" does not exist."
print(msg)
else:
words=contents.split()
num_words=len(words)
print("The file "+filename+" has about "+str(num_words)+" words.")
下面多分析几本书,这样做之前,我们先将这个程序的大部分代码移到一个名为count_words()的函数中,方便调用。
def count_words(filename):
try:
with open(filename) as f_obj:
contents=f_obj.read()
except FileNotFoundError:
msg="sorry the file "+filename+" does not exist."
print(msg)
else:
words=contents.split()
num_words=len(words)
print("The file "+filename+" has about "+str(num_words)+" words.")
编写一个简单的循环,来计算分析任何文本包含多少个单词。
def count_words(filename):
--snip--
filenames=['alice.txt','siddhartha.txt','moby_dick.txt','little_women.txt']
for filename in filenames:
count_words(filename)
有时候,你需要让程序在发生异常时一声不吭,但在except代码中明确告诉Python什么都不要做。可以使用pass语句。
def count_words(filename):
try:
with open(filename) as f_obj:
contents=f_obj.read()
except FileNotFoundError:
pass
else:
words=contents.split()
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
提示用户输入数字,如果用户输入的不是数字会报错,尝试让程序不要报错并继续运行。
def number(num):
try:
result=int(num)
print(result)
except:
print("\ncan't input another.")
num=input("please input number:")
while num:
if num != 'q':
number(num)
num=input("please input number:")
else:
break
当用户把信息提供给程序时,程序都把数据存储在列表和字典等数据结构中。用户关闭程序时,你几乎总是要保存他们的信息;一种简单的方式是使用模块json来存储数据。
模块json让你能够将简单的python数据结构转储到文件中,并在程序再次运行时加载该文件中的数据。
你还可以使用json在Python程序中分享数据,更重要的是json并非Python专用,你还可以将以json格式存储的数据与其他编程语言的人分享。
注意:json格式最初是为JavaScript开发的,但随后成了一种常见格式,被包括Python的众多语言采用。
使用json.dump()来存储数字列表。
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.load()将这个列表读取到内存中。
import json
filename='numbers.json'
with open(filename) as f_obj:
numbers=json.load(f_obj)
print(number)
存储用户的名字
import json
username=input("what is your name?")
filename='username.json'
with open(filename,'w') as f_obj:
json.dump(username,f_obj)
print("\nWe'll remember you when you come back, "+username+"!")
再编写一个程序,向其名字被存储的用户发出问候。
import json
filename='username.json'
with open(filename) as f_obj:
username=json.load(f_obj)
print("welcome back, "+username+'.')
将这两个程序合并到一个程序,当程序运行时,我们将尝试从文件username.json中获取用户名,如果这个文件不存在,我们就在except代码块中提示用户输入用户名,并将其存储在username.json中,以便程序再次运行时能够获取它。
import json
filename='username.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+'!')
当代码能够正确地运行,但可以做进一步的改进–将代码划分为一系列完成具体工作的函数。这样的过程被称为重构。
重构让代码更清晰,更易于理解,更容易扩展。
重构remember_me.py
import json
def get_stored_username():
filename='username.json'
try:
with open(filename) as f_bjc:
username=json.load(f_obj)
except FileNotFoundError:
return None
else:
return username
def get_new_username():
username=input("What is your name?")
filename='username.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()
它将原来的程序,分成了三个部分,先调用get_stored_username(),这个函数只负责获取存储的用户名(如果存储了的话),如果没有存储将调用get_new_username(),这个函数只负责获取并存储新用户的用户名。