python基础-文件和错误处理
文件读取
读取整个文件
with open('demo_2.txt') as file_object:
content = file_object.read()
print(content)
open(‘要打开的文件路径’):如果向代码中那样传入名称,则会基于py脚本目录进行文件的查找,文件打开成功返回一个文件对象file_object;
with是在不需要访问文件后将其关闭。因为正常情况下调用了open就必须调用close。比如
filePath = 'demo_2.txt'
file = open(filePath, 'r') // 只读
filew = open('demo_w.txt', 'w') // 写入
words = file.read()
filew.write(words); // 将内容写入
print(words);
// 关闭两个文件
file.close()
filew.close()
但是这样做有个问题,当程序出现Bug,导致close未执行的时候,文件将不会关闭,而过早的调用close则会导致使用文件的时候它已经关闭了(无法访问)。而我们使用with,就可以让PY自动确定,在需要的时候使用它,并让PY在合适的时机自动关闭文件。
with
语句是一种上下文管理器,它提供了一种非常方便的方式来管理资源,如文件操作、网络连接、数据库连接等。
with主要作用和使用场景体现在以下几个方面:
1. 确保资源的正确释放
在使用一些资源时,例如文件、网络连接或数据库连接,我们需要在使用完后正确地释放这些资源,以避免资源泄漏。传统的方式是使用try...finally
语句,但使用with
语句可以让代码更加简洁和安全。
# 传统的文件操作方式
try:
file = open('test.txt', 'r')
content = file.read()
print(content)
finally:
file.close()
# 使用 with 语句的文件操作方式
with open('test.txt', 'r') as file:
content = file.read()
print(content)
传统方式使用try...finally
确保文件在操作完成后关闭,而使用with
语句则更简洁,with
语句会自动处理文件的关闭操作,即使在操作过程中出现异常,也能保证文件被正确关闭。
2. 自动管理上下文
with
语句的执行过程涉及到上下文管理器协议,即对象需要实现__enter__()
和__exit__()
方法。当执行with
语句时,会调用对象的__enter__()
方法,该方法的返回值会赋值给as
后面的变量;当with
语句块执行完毕或出现异常时,会调用对象的__exit__()
方法,进行资源的清理工作。
class MyContextManager:
def __enter__(self):
print("进入上下文管理器")
return self
def __exit__(self, exc_type, exc_value, traceback):
print("退出上下文管理器")
if exc_type is not None:
print(f"发生异常: {exc_type}, {exc_value}")
return True
with MyContextManager() as my_obj:
print("在上下文管理器内部执行操作")
# 可以在这里模拟异常
# raise ValueError("这是一个测试异常")
在上述示例中,MyContextManager
类实现了上下文管理器协议,当执行with
语句时,会依次调用__enter__()
和__exit__()
方法。
3. 简化代码结构
使用with
语句可以避免大量的try...finally
嵌套,使代码更加清晰和易读,特别是在处理多个资源时,优势更加明显。比如多个资源管理:
# 同时打开两个文件进行操作
with open('file1.txt', 'r') as file1, open('file2.txt', 'w') as file2:
content = file1.read()
file2.write(content)
使用with
语句同时管理两个文件的打开和关闭操作,代码简洁明了。
read
read()是读取文件中的全部内容,并将内容保存为字符串返回。但是read到达文件末尾时会返回一个空字符串,会导致显示出来就是一个空行,如果要删除末尾空行,就要使用rstrip()
print( content.rstrip() )
逐行读取
要以每次一行的方式检查文件,可以对文件对象使用for循环:
with open('demo_2.txt') as file_object:
for line in file_object:
if 'language' in line:
print(line.rstrip())
加上rstrip()方法目的就是 因为逐行输出的时候,每行的末尾都会有个换行符,要消除这些空白行,就要使用rstrip()方法。
创建一个包含文件各行内容的列表
使用with+open的时候,返回的文件对象只能在with代码块中可用,如果要在with之外使用文件内容,可以将逐行内容添加到一个列表中。
with open('demo_2.txt') as file_object:
lines = file_object.readlines()
for line in lines:
print(line)
readlines从文件中读取每一行,并将其存储在一个列表中,并返回这个列表。这样在with代码块外部,依然可以使用这个列表
读取文本文件的时候,会将所有文件内容都转化为字符串,如果内容是数字,那么就需要根据情况使用Int()转化为整数或者float()转化为浮点数。
文件写入
文件写入时,需要传入另一个参数,告诉PY你要写入打开的文件。
with open('write_demo.txt', 'w') as file_object:
file_object.write('hello world!\n')
open('文件路径', ‘打开模式’):打开模式有3种:
'r': 默认,读取;'w': 写入;‘a’:附加模式;‘r+':让你能读取和写入文件的模式。
如果要写入的文件不存在,那么PY会自动创建它,而以'w'写入模式打开的文件,PY会在返回文件对象前清空该文件。
如果要将数值存储到文本文件种,必须使用str()转换为字符串形式。
写入多行
write()不会在写入的文本末尾加换行符,所以在使用.write的时候,需要主动加入换行符
with open('write_demo.txt', 'w') as file_object:
file_object.write('hello world!\n')
file_object.write('i miss you!\n')
附加内容
如果要给文件添加内容,而不是覆盖原有内容,就可以使用附加模式:'a',以附加模式打开文件时,PY不会再返回文件对象前清空文件,而你写入到文件的行都将添加到文件末尾。另外与w一样,文件不存在则新建一个文件。
with open('write_demo.txt', 'a') as file_object:
file_object.write('and i wanna to see you')
异常
PY使用异常这个特殊对象来管理程序执行期间发生的错误,当遇到错误,PY会创建一个异常对象,并显示一个traceback。
异常使用try-except代码块进行处理。
比如,实现一个最简单的traceback:
print( 5 / 0)
报错:
ZeroDivisionError Traceback (most recent call last)
Cell In[5], line 1
----> 1 print( 5 / 0)
ZeroDivisionError: division by zero
ZeroDivisionError就是一个异常对象,当PY无法按照你的要求做时,就会创建这种对象。
try:
print(5/0)
except ZeroDivisionError:
print('你不能除以0')
如果try-except后面还有其他代码,则程序会接着运行。
while True:
first_num = input('\n请输入第一个数字:')
if first_num == 'q':
break
second_num = input('请输入第二个数字:')
try:
answer = int(first_num) / int(second_num)
except ZeroDivisionError:
print('除数不能为0') // 5/0走这里
else:
print(answer) // 6/2 走这里
将可能引发错误的代码放在try-except中,而else则代表try代码块成功执行之后的部分放在else中。
except ZeroDivisionError告诉PY如果出现ZeroDivisionError错误该怎么办。
处理FileNotFoundError
使用文件常遇到的错误是找不到文件。
with open('nopath.txt', 'r') as file_object:
content = file_object.read()
// 错误
FileNotFoundError Traceback (most recent call last)
Cell In[7], line 1
----> 1 with open('nopath.txt', 'r') as file_object:
2 content = file_object.read()
所以要优化报错:
try:
with open('nopath.txt', 'r') as file_object:
content = file_object.read()
except FileNotFoundError:
print('文件不存在')
这里要注意:try-except要写在with去读文件的外面。
try:
with open('demo_2.txt') as file_object:
contents = file_object.read()
except FileNotFoundError:
print('文件不存在')
else:
words = contents.split()
print( len(words) )
上面例子实现了一个查看文件中有多少个单词。
split() 以空格分隔字符串,并将拆分的(单词)部分存为一个列表。
len(列表) 则返回一个列表的长度。
遇到问题 一声不吭
并非每个捕获异常都需要告诉用户,有时需要像什么都没有发生一样继续运行。这时候就要使用PY的pass语句,让它告诉py什么都不要做。
try:
with open('nopath.txt') as file_object:
contents = file_object.read()
except FileNotFoundError:
pass
else:
words = contents.split()
print( len(words) )
使用pass当遇到异常的时候,什么都不会发生,也不会有任何的输出。pass语句还充当了占位符。
存储数据
json.dump
json.dump:用于存储数据,接受两个参数,json.dump(‘要存储的数据’, ‘用于存储的文件对象)
import json
nums = list(range(1,6)) // 创建一个1-6的列表
filename = 'numbers.json'
with open(filename, 'w') as file_object:
json.dump(nums, file_object)
使用json.dump以及json.load需要先引入json模块
range(1, 6)
函数会生成一个从 1 开始(包含 1)到 6 结束(不包含 6)的整数序列,即1, 2, 3, 4, 5
。list()
函数将这个range
对象转换为一个列表,所以arr
最终是[1, 2, 3, 4, 5]
。
注意:json.dump写入文件要对文件使用'w'写入模式。
json.load
filename = 'numbers.json'
with open(filename) as file_object:
nums = json.load(file_object)
print(nums)
使用json.load可以将文件内容加载到内存中。
综合案例
filename = 'username.json'
try:
# 先从文件中读取 如果文件不存在那么就让用户输入并创建
with open(filename) as file_read:
username = json.load(file_read)
except FileNotFoundError:
# 如果用户名文件不存在 那么询问
username = input('你叫什么名字\n')
with open(filename, 'w') as file_write:
json.dump(username, file_write)
print('我们记住你的名字了:' + username)
else:
print('欢迎回来' + username)
上面例子是执行脚本,当脚本不存在到时候 进入except,引导用户输入,并保存用户名。如果文件存在则进入else,输出用户名。
这里在敲代码时候遇到几个问题:
1、脚本文件名不可以是json.py
AttributeError: partially initialized module 'json' has no attribute 'dump' (most likely due to a circular import) 可以看出,问题出在循环导入上。当你把 Python 文件命名为 json.py 时,Python 在尝试导入 json 模块时,会优先导入你当前的 json.py 文件,而不是 Python 标准库中的 json 模块,这样就导致无法使用标准库 json 模块里的 dump 和 load 等方法。
所以不要起与py模块同名的脚本文件!
2、当无意中创建了空的username.json文件时,执行json.load会报错:
JSONDecodeError: Expecting value: line 1 column 1 (char 0)
通常表示json.load()
函数试图解析一个空的 JSON 文件或者文件内容不是有效的 JSON 格式。如果username.json
文件为空,json.load()
就会抛出JSONDecodeError
异常,因为它期望文件中包含有效的 JSON 数据。
所以,保险起见,可以这么修改:
try:
with open(filename, 'r') as file_object:
# 先读取文件内容
content = file_object.read()
# 检查文件内容是否为空
if content:
username = json.loads(content)
else:
# 文件为空,执行与文件不存在时相同的操作
username = input('你叫什么名字\n')
with open(filename, 'w') as file:
json.dump(username, file)
print('我们记住你的名字了:' + username)
先执行一次file_object.read()判断json中有没有内容,有内容再执行json.load