本文正在参加「Python主题月」,详情查看 活动链接
学习处理文件,能够让程序快速地分析大量数据;学习错误处理,可以避免程序在面对意外情形时崩溃;学习异常,它们是 Python 创建的特殊对象,用于管理程序运行时出现的错误;学习模块 json,它能够让你保存用户数据,以免在程序停止运行后丢失。
从文件中读取数据
- 要使用文本文件中的信息,首先需要将信息读取到内存中。为此,你可以一次性读取文件的全部内容,也可以以每次一行的方式逐步读取。
读取整个文件
- 要读取文件,需要一个包含几行文本的文件。
- 先创建一个文件:
# pi_digits.txt
3.1415926535
8979323846
2643383279
复制代码
- 下面的程序打开并读取这个文件,再将其内容显示到屏幕上:
with open('pi_digits.txt') as file_object:
contents = file_object.read()
print(contents)
复制代码
- 要以任何方式使用文件,那怕仅仅是打印其内容,都得先打开文件,才能访问它。
- 函数
open()
接受一个参数:要打开的文件的名称。Python 在当前执行的文件所在的目录中查找指定的文件。 - 在这里,
open('pi_digits.txt')
返回一个表示文件pi_digits.txt的对象
,Python 将该对象赋给file_object
供以后使用。 - 关键字
with
不再需要访问文件后将其关闭。 - 相比于原始文件,该输出唯一不同的地方是末尾多了一个空行。为何会多出这个空行呢?因为
read()
到达文件末尾时返回一个空字符串,而将这个空字符串显示出来时就是一个空行。要删除多出来的空行,可在函数调用print()
中使用rstrip()
:
with open('pi_digits.txt') as file_object:
contents = file_object.read()
print(contents.rstrip())
复制代码
文件路径
with open('text_file/filename.txt') as file_object:
复制代码
- 通过相对路径,这行代码让 Python 到文件夹
python_work
下的文件夹text_file
中去查找指定的.txt
文件。 - 还可以给出具体的绝对路径,但绝对路径通常比相对路径长,因此将其赋给一个变量,再将该变量传递给
open()
会有所帮助。
逐行读取
- 要以每次一行的方式检查文件,可对文件对象使用
for
循环:
with open('pi_digits.txt') as file_object:
for line in file_object:
print(line)
复制代码
- 这里也使用了关键字
with
,让 Python 负责妥善地打开和关闭文件。为查看文件的内容,通过对文件对象执行循环来遍历文件中的每一行。 - 打印每一行时,空行却更多了。。。
- 那么要消除这些空行,还是要在
print()
函数中调用rstrip()
。
with open('pi_digits.txt') as file_object:
for line in file_object:
print(line.strip())
复制代码
创建一个包含文件各行内容的列表
- 使用关键字
with
时,open()
返回的文件对象只在with
代码块内可用。 - 如果要在
with
代码块外访问文件的内容,可在with
代码块内将文件的各行存储在一个列表中,并在with
代码块外使用该列表。
with open('pi_digits.txt') as file_object:
lines = file_object.readlines()
for line in lines:
print(line.rstrip())
复制代码
- 方法
readlines()
从文件中读取每一行,并将其存储在一个列表中。
使用文件的内容
- 将文件读取到内存中后,就能以任何方式使用这些数据了。
file_name = 'pi_digits.txt'
with open(file_name) as file_object:
lines = file_object.readlines()
pi_string = ''
for line in lines:
pi_string += line.rstrip()
print(pi_string)
print(len(pi_string))
复制代码
- 变量
pi_string
指向的字符串包含原来位于每行左边的空格,为删除这些空格,可使用strip()
而非rstrip()
:
file_name = 'pi_digits.txt'
with open(file_name) as file_object:
lines = file_object.readlines()
pi_string = ''
for line in lines:
pi_string += line.strip()
print(pi_string)
print(len(pi_string))
复制代码
读取文本文件时,Python将其中的所有文本都解读为字符串。如果读取的是数,并要将其作为数值使用,就必须使用函数
int()
将其转换为整数或使用函数float()
将其转换为浮点数。
包含一百万位的大型文件
- 如果我们有一个文本文件,其中包含精确到小数点后 1000000 位而不是 30 位的圆周率值,也可创建一个包含所有这些数字的字符串。
- 为此,无须对前面的程序做任何修改,只要将这个文件传递给它即可。
圆周率值中包含你的生日吗
- 判断圆周率中是否包含你的生日:
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.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 millions digits of pi.")
复制代码
读取文件
- 保存数据的最简单的方式之一是将其写入文件中。
写入空文件
- 要将文本写入文件,你在调用
open()
时需要提供另一个实参,告诉 Python 你要写入打开的文件。
write_filename = 'programming.txt'
with open(write_filename, 'w') as file_object:
file_object.write("I lvoe programming.")
复制代码
open()
函数第二个参数w
告诉 Python,要以写入模式打开这个文件。- 打开文件时,可以指定读取模式(‘r’)、写入模式(‘w’)、附加模式(‘a’) 或 读写模式(‘r+’)。
- 如果要写入的文件不存在,函数
open()
将自动创建它。
以 写入模式(‘w’) 打开文件时千万要小心,因为如果指定的文件已经存在,Python 将在返回文件对象前清空该文件的内容。
Python 只能将字符串写入文本文件。要将数值数据存储到文本文件中,必须先使用函数
str()
将其转换为字符串格式。
写入多行
- 函数
write()
不会在写入的文本末尾添加换行符,因此如果写入多行时不会指定换行符。
filename = 'programming.txt'
with open(filename, 'w') as file_object:
file_object.write("I lvoe programming.")
file_object.write("I love creating new games.")
复制代码
- 如果你打开
programming.txt
,将发现两行内容挤在一起。 - 要让每个字符串都单独占一行,需要在方法调用
write()
中包含换行符:
filename = 'programming.txt'
with open(filename, 'w') as file_object:
file_object.write("I lvoe programming.\n")
file_object.write("I love creating new games.\n")
复制代码
附加到文件
- 如果要给文件添加内容,而不是覆盖原有的内容,可以以附加模式打开文件。
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")
复制代码
异常
- Python 使用称为异常的特殊对象来管理程序执行期间发生的错误。
- 每当发生让 Python 不知所措的错误时,它都会创建一个异常对象。
- 如果你编写了处理该异常的代码,程序将继续运行;如果未对异常进行处理,程序将停止并显示
traceback
,其中包含有关异常的报告。
处理 ZeroDivisionError
异常
- 很简单的除零错误示例:
>>> print(5/0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
复制代码
- Python 无法执行除零错误,抛出一个
Traceback
。
使用 try-except
代码块
- 你认为可能会发生错误时,可编写一个
try-except
代码块来处理可能引发的异常。
try:
print(5/0)
except ZeroDivisionError:
print("You can't divide by zero!")
复制代码
- 将导致错误的代码行
print(5/0)
放在一个try
代码块中。如果try
代码块中的代码运行起来没有问题,Python 将跳过except
代码块;如果try
代码块中的代码导致了错误,Python 将查找与之匹配的except
代码块并运行其中的代码。
使用异常避免崩溃
- 发生错误时,如果程序还有工作尚未完成,妥善地处理错误就尤其重要。
print("Give me two numbers, and I'll divide them.")
print("Enter 'q' to quit.")
while True:
first_number = input("First 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)
复制代码
else
代码块
- 通过将可能引发错误的代码放在try-except代码块中,可提高程序抵御错误的能力。
print("Give me two numbers, and I'll divide them.")
print("Enter 'q' to quit.")
while True:
first_number = input("First 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 zero!")
else:
print(answer)
复制代码
处理 FileNotFoundError
异常
- 使用文件时,一种常见的问题是找不到文件:查找的文件可能在其他地方,文件名可能不正确,或者这个文件根本就不存在。对于所有这些情形,都可使用
try-except
代码块以直观的方式处理。
filename = 'alice.txt'
with open(filename, encoding='utf-8') as f:
contents = f.read()
复制代码
alice.txt
不在该程序所在的目录,抛出了Traceback
:
Traceback (most recent call last):
File "alice.py", line 3, in <module>
with open(filename, encoding='utf-8') as f:
FileNotFoundError: [Errno 2] No such file or directory: 'alice.txt'
复制代码
- 上述
traceback
的最后一行报告了FileNotFoundError
异常,这是 Python 找不到要打开的文件时创建的异常。 - 将
open()
函数放在try
函数里,处理该异常:
filename = 'alice.txt'
try:
with open(filename, encoding='utf-8') as f:
contents = f.read()
except FileNotFoundError:
print(f"Sorry, the file {filename} does not exit.")
复制代码
分析文本
- 你可以分析包含整本书的文本文件。
- 下面是对只包含童话名
"Alicein Wonderland"
的字符串调用方法split()
的结果:
>>> title
'Alice in Wonderland'
>>> title.split()
['Alice', 'in', 'Wonderland']
复制代码
- 方法
split()
以空格为分隔符将字符串分拆成多个部分,并将这些部分都存储到一个列表中。 - 为计算《爱丽丝漫游奇境记》包含多少个单词,我们将对整篇小说调用
split()
,再计算得到的列表包含多少个元素,从而确定整篇童话大致包含多少个单词:
filename = 'alice.txt'
try:
with open(filename, encoding='utf-8') as f:
contents = f.read()
except FileNotFoundError:
print(f"Sorry, the file {filename} does not exit.")
else:
# 计算该文件大致包含多少个单词。
words = contents.split()
num_words = len(words)
print(f"The file {filename} has about {num_words} words.")
复制代码
使用多个文件
- 下面多分析几本书。这此之前,先将这个程序的大部分代码移到一个名为
count_words()
的函数中。这样,对多本书进行分析时将更容易:
def count_words(filename):
"""计算一个文件大致包含多少个单词。"""
try:
with open(filename, encoding='utf-8') as f:
contents = f.read()
except FileNotFoundError:
print(f"Sorry, the file {filename} does not exit.")
else:
words = contents.split()
num_words = len(words)
print(f"The file {filename} has about {num_words} words.")
filename = 'alice.txt'
count_words(filename)
复制代码
- 现在可以编写一个简单的循环,计算要分析的任何文本包含多少个单词了。
def count_words(filename):
"""计算一个文件大致包含多少个单词。"""
try:
with open(filename, encoding='utf-8') as f:
contents = f.read()
except FileNotFoundError:
print(f"Sorry, the file {filename} does not exit.")
else:
words = contents.split()
num_words = len(words)
print(f"The file {filename} has about {num_words} words.")
filenames = ['alice.txt', 'siddhartha.txt', 'moby_dick.txt', 'little_women.txt']
for filename in filenames:
count_words(filename)
复制代码
- 文件
siddhartha.txt
不存在,但这丝毫不影响该程序处理其他文件:
The file alice.txt has about 29465 words.
Sorry, the file siddhartha.txt does not exit.
The file moby_dick.txt has about 215830 words.
The file little_women.txt has about 189079 words.
复制代码
静默失败
- 在前一个示例中,我们告诉用户有一个文件找不到。但并非每次捕获到异常都需要告诉用户,有时候你希望程序在发生异常时保持静默,就像什么都没有发生一样继续运行。
- 要让程序静默失败,可像通常那样编写
try
代码块,但在except
代码块中明确地告诉 Python 什么都不要做。Python 有一个pass
语句,可用于让 Python 在代码块中什么都不要做:
def count_words(filename):
"""计算一个文件大致包含多少个单词。"""
try:
with open(filename, encoding='utf-8') as f:
contents = f.read()
except FileNotFoundError:
pass
else:
words = contents.split()
num_words = len(words)
print(f"The file {filename} has about {num_words} words.")
filenames = ['alice.txt', 'siddhartha.txt', 'moby_dick.txt', 'little_women.txt']
for filename in filenames:
count_words(filename)
复制代码
- 现在,出现
FileNot-FoundError
异常时,将执行except
代码块中的代码,但什么都不会发生。
决定报告哪些错误
- 编写得很好且经过详尽测试的代码不容易出现内部错误,如语法或逻辑错误,但只要程序依赖于外部因素,如用户输入、存在指定的文件、有网络链接,就有可能出现异常。凭借经验可判断该在程序的什么地方包含异常处理块,以及出现错误时该向用户提供多少相关的信息。
存储数据
- 很多程序都要求用户输入某种信息,如让用户存储游戏首选项或提供要可视化的数据。不管关注点是什么,程序都把用户提供的信息存储在列表和字典等数据结构中。
- 用户关闭程序时,几乎总是要保存他们提供的信息。一种简单的方式是使用模块
json
来存储数据。 - 模块
json
让你能够将简单的 Python 数据结构转储到文件中,并在程序再次运行时加载该文件中的数据。 - 你还可以使用
json
在 Python 程序之间分享数据。更重要的是,JSON数据格式 并非 Python 专用的,这让你能够将以 JSON格式 存储的数据与使用其他编程语言的人分享。
JSON(JavaScript Object Notation)格式最初是为 JavaScript 开发的,但随后成了一种常见格式,被包括 Python 在内的众多语言采用。
使用 json.dump()
和 json.load()
- 我们来编写一个存储一组数的简短程序,再编写一个将这些数读取到内存中的程序。
- 第一个程序将使用
json.dump()
来存储这组数,而第二个程序将使用json.load()
。
import json
numbers = [2, 3, 5, 7, 11, 13]
filename = 'number.json'
with open(filename, 'w') as f:
json.dump(numbers, f)
复制代码
- 通常使用文件扩展名
.json
来指出文件存储的数据为 JSON格式。 - 下面再编写一个程序,使用
json.load()
将列表读取到内存中:
import json
filename = 'number.json'
with open(filename) as f:
numbers = json.load(f)
print(numbers)
复制代码
json
是一种在程序之间共享数据的简单方式。
保存和读取用户生成的数据
- 使用
json
保存用户生成的数据大有裨益,因为如果不以某种方式存储,用户的信息会在程序停止运行时丢失。 - 下面来看一个这样的例子:提示用户首次运行程序时输入自己的名字,并在再次运行程序时记住他。
- 先保存用户名到
json
文件:
import json
username = input("What is your name? ")
filename = 'username.json'
with open(filename, 'w') as f:
json.dump(username, f)
print(f"We'll remember you when you come back, {username}!")
复制代码
- 再从
json
文件中读取用户名:
import json
filename = 'username.json'
with open(filename) as f:
username = json.load(f)
print(f"Welcome back {username}!")
复制代码
- 现在,尝试将两个程序合并到一起:
import json
# 如果以前存储了用户名,就加载它。
# 否则,提示用户输入用户名并存储它。
filename = 'username.json'
try:
with open(filename) as f:
username = json.load(f)
except FileNotFoundError:
username = input("What is your name? ")
with open(filename, 'w') as f:
json.dump(username, f)
print(f"We'll remember you when you come back, {username}!")
else:
print(f"Welcome back {username}!")
复制代码
重构
- 你经常会遇到这样的情况:代码能够正确地运行,但通过将其划分为一系列完成具体工作的函数,还可以改进。这样的过程称为重构。
- 重构让代码更清晰、更易于理解、更容易扩展。
- 现在,重构上面合并的代码:
import json
def greet_user():
"""问候用户,并指出其名字"""
filename = 'username.json'
try:
with open(filename) as f:
username = json.load(f)
except FileNotFoundError:
username = input("What is your name? ")
with open(filename, 'w') as f:
json.dump(username, f)
print(f"We'll remember you when you come back, {username}!")
else:
print(f"Welcome back {username}!")
greet_user()
复制代码
- 接着重构
greet_user()
,增加了get_stored_username()
:
import json
def get_stored_username():
"""如果存储了用户名,就获取它。"""
filename = 'username.json'
try:
with open(filename) as f:
username = json.load(f)
except FileNotFoundError:
return None
else:
return username
def greet_user():
"""问候用户,并指出其名字。"""
username = get_stored_username()
if username:
print(f"Welcome back, {username}!")
else:
username = input("What is your name? ")
filename = 'username.json'
with open(filename, 'w') as f:
json.dump(username, f)
print(f"We'll remember you when you come back, {username}!")
greet_user()
复制代码
- 还需要重构
greet_user()
中的另一个代码块,将没有存储用户名时提示用户输入的代码放在一个独立的函数中:
import json
def get_stored_username():
"""如果存储了用户名,就获取它。"""
filename = 'username.json'
try:
with open(filename) as f:
username = json.load(f)
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:
json.dump(username, f)
return username
def greet_user():
"""问候用户,并指出其名字。"""
username = get_stored_username()
if username:
print(f"Welcome back, {username}!")
else:
username = get_new_username()
print(f"We'll remember you when you come back, {username}!")
greet_user()
复制代码
- 以上就是
greet_user()
的最终版本,每个函数都执行单一而清晰的任务。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END