1. 从文件中读取数据
- 要使用文本文件中的信息,首先需要将信息读取到内存中。为此,你可以一次性读取文件的全部内容,也可以以每次一行的方式逐步读取
读取整个文件
with open('pi_digits.txt') as file_object:
contents = file_object.read()
print(contents)

- 要以任何方式使用文件,哪怕仅仅是打印其内容,都得先打开文件,才能访问它。
函数open()接受一个参数:要打开的文件的名称。Python在当前执行的文件所在的目录中查找指定的文件。
函数open()返回一个表示文件的对象
关键字with在不再需要访问文件后将其关闭
- 在这个程序中,注意到我们调用了
open(),但没有调用close()。也可以调用open()和close()来打开和关闭文件,但这样做时,如果程序存在bug导致方法close()未执行,文件将不会关闭。看似微不足道,但未妥善关闭文件可能导致数据丢失或受损。如果在程序中过早调用close(),你会发现需要使用文件时它已关闭(无法访问),这会导致更多的错误。
- 并非在任何情况下都能轻松确定关闭文件的恰当时机,但通过使用前面所示的结构,可让Python去确定:你只管打开文件,并在需要时使用它,Python自会在合适的时候自动将其关闭
- 有了表示pi_digits.txt的文件对象后,使用
方法read()读取这个文件的全部内容,并将其作为一个长长的字符串赋给变量contents
- 相比于原始文件,该输出唯一不同的地方是末尾多了一个空行。因为
read()到达文件末尾时返回一个空字符串,而将这个空字符串显示出来时就是一个空行。要删除多出来的空行,可在函数调用print()中使用rstrip()来删除字符串末尾的空白
文件路径
- 要让Python打开不与程序文件位于同一个目录中的文件,需要提供文件路径,让Python到系统的特定位置去查找
- 可以使用 相对文件路径 来打开其中的文件,相对文件路径让Python到指定的位置去查找,而该位置是是相对于当前运行的程序所在目录的
with open('text_files/filename.txt') as file_object:
- 还可以将文件在计算机中的准确位置告诉Python,这样就不用关心当前运行的程序存储在什么地方了。这称为 绝对文件路径 。通过使用绝对路径,可读取系统中任何地方的文件
- 绝对路径通常比相对路径长,因此将其赋给一个变量,再将该变量传递给
open()会有所帮助
file_path = '/home/ehmatthes/other_files/text_files/filename.txt'
with open(file_path) as file_object:
逐行读取
filename = 'pi_digits.txt'
with open(filename) as file_object:
for line in file_object:
print(line)

- 打印每一行时,会出现两个空行。因为在这个文件中,每行的末尾都有一个看不见的换行符,而函数调用
print()也会加上一个换行符。要消除这些多余的空行,可在函数调用print()中使用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())
使用文件的内容
- 读取文本文件时,Python将其中的所有文本都解读为字符串。如果读取的是数,并要讲其作为数值使用,就必须使用
函数int()将其转换为整数或使用函数float()将其转换为浮点数
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(f"len: {len(pi_string)}")
包含一百万位的大型文件
- 对于可处理的数据量,Python没有任何限制。只要系统的内存足够多,想处理多少数据都可以
filename = 'random_digits.txt'
with open(filename) as file_object:
lines = file_object.readlines()
random_string = ''
for line in lines:
random_string += line.strip()
birthday = input("Enter your birthday, in the form mmddyy: ")
if birthday in random_string:
print("Your birthday appears in the random number!")
else:
print("Your birthday does not appear in the random number!")
2. 写入文件
- 保存数据最简单的方式之一是将其写入文件中。通过将输出写入文件,即便关闭包含程序输出的终端窗口,这些输出依然存在:可以在程序结束运行后查看这些输出,可以与别人分享输出文件,还可以编写程序来将这些输出读取到内存中并进行处理
写入空文件
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将在返回文件对象前清空该文件的内容
写入多行
- 函数不会在写入的文本末尾添加换行符,要让每个字符串都单独占一行,需要在方法调用
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不会在返回文件对象前清空文件的内容,而是将写入文件的行添加到文件末尾。如果指定的文件不存在,Python将为你创建一个空文件
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
- 通过将可能引发错误的代码放在
try-except代码块中,可提高程序抵御错误的能力
处理ZeroDivisionError异常
print(5/0)

使用try-except代码块
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: ")
if second_number == 'q':
break
answer = int(first_number) / int(second_number)
print(answer)

else代码块
- 依赖
try代码块成功执行的代码都应放到else代码块中
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)

处理FileNotFoundError异常
filename = 'paper_1.txt'
with open(filename, encoding='utf-8') as f:
contents = f.read()

filename = 'paper_1.txt'
try:
with open(filename, encoding='utf-8') as f:
contents = f.read()
except FileNotFoundError:
print(f"Sorry, the file {filename} does not exist.")
分析文本
方法split()以空格为分隔符分拆成多个部分,并将这些部分都存储到一个列表中
filename = 'paper_1.txt'
try:
with open(filename, encoding='utf-8') as f:
contents = f.read()
except FileNotFoundError:
print(f"Sorry, the file {filename} does not exist.")
else:
words = contents.split()
num_words = len(words)
print(f"The file {filename} has about {num_words} 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 exist.")
else:
words = contents.split()
num_words = len(words)
print(f"The file {filename} has about {num_words} words.")
filenames = ['paper_1.txt', 'paper_2.txt', 'paper_3.txt']
for filename in filenames:
count_words(filename)

静默失败
- Python提供了
pass语句,可用于让Python在代码块中什么也不做
pass语句还充当了占位符,提醒你在程序某个地方什么都没有做,并且以后也许要在这里做些什么
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 = ['paper_1.txt', 'paper_2.txt', 'paper_3.txt']
for filename in filenames:
count_words(filename)

决定报告哪些错误
- 如果用户知道要分析哪些文件,他们可能希望在有文件却没有分析时出现一条消息来告知原因
- 如果用户只想看到结果,并不知道要分析哪些文件,可能就无须在有些文件不存在时告知他们
- Python的错误处理结构让你能够细致地控制与用户分享错误信息的程度,要分享多少信息由你决定
4. 存储数据
模块json让你能够将简单的Python数据结构转储到文件中,并在程序再次运行时加载该文件中的数据,或在Python程序之间分享数据
- JSON数据格式 并非Python专用,这使得以JSON格式存储的数据可以与其他编程语言互通分享
- JSON格式最初是为JavaScript开发的,但随后成了一种常见格式,被包含Python在内的众多语言采用
使用json.dump()和json.load()
函数json.dump()接受两个实参:要存储的数据,以及可用于存储数据的文件对象
函数json.load()加载存储在文件中的信息
import json
numbers = [2, 3, 5, 7, 11, 13]
filename = 'numbers.json'
with open(filename, 'w') as f:
json.dump(numbers, f)
filename = 'numbers.json'
with open(filename) as f:
numbers = json.load(f)
print(numbers)

保存和读取用户生成的数据
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 get_store_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_store_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()
文章中的所有代码经测试均可成功编译运行,可直接复制。具有显然结果或简单结论的代码不展示运行结果。如有问题欢迎随时交流~