文件的读写
磁盘上读写文件的功能都是由操作系统提供的,操作系统不允许普通的程序直接操作磁盘。
请求操作系统打开一个文件对象(通常称为文件描述符),通过操作系统提供的接口从这个文件对象中读取数据(读文件),把数据写入这个文件对象(写文件)。
- 打开文件:
open(),获取一个文件对象。 - 读取文件内容:通过
read()、readline()、readlines()等来获取文件中的内容。 - 处理文件内容:对读取到的内容进行进一步的处理(例如解析、分析、修改等)。
- 关闭文件:
close()方法关闭文件,以释放系统资源。
open() 打开文件
如果文件不存在,open()函数就会抛出一个IOError的错误
打开文件的模式:
'r':只读模式(默认),文件必须存在。'w':写模式,文件不存在时会创建,文件存在时会覆盖内容。'a':追加模式,文件不存在时会创建,文件存在时会在末尾追加内容。'b':二进制模式,适用于处理非文本文件(如图片、音频等)。'r+':读写模式,文件必须存在。
# 文件路径:文件的路径,可以是绝对路径或相对路径。
# 模式:文件打开的模式(如读模式、写模式等)。
file = open('test.txt', 'r')
# content = file.read() # 读取整个文件
# content = file.read(10) # 读取10个字节
content = file.readline() # 读取一行数据
print(content)
file.close()
读取所有的行
file = open("test.txt", "r")
# 返回文件的所有行,并将读取到的行放到一个对象中
lines = file.readlines()
for line in lines:
print(line)
file.close()
with 自动管理文件的打开和关闭
文件在退出 with 语句块时自动关闭,可以避免忘记关闭文件,它会在代码块执行完毕后自动关闭文件,哪怕发生异常。当然也可以使用 try ... finally。
with open("test.txt", "r") as file:
content = file.read()
print(content)
逐行读取大文件
大文件读取调用 read() 会读取文件全部内容到内存中,可能导致内存不足。此时,可以反复调用read(size)方法,可以使用 for 循环逐行读取文件,避免一次性读取全部内容。
- 小文件,
read()一次性读取; - 不确定文件大小,反复调用
read(size)比较保险; - 配置文件,调用
readlines()最方便
with open("test.txt", "r") as file:
for line in file:
# strip() 去除每行末尾的换行符
print(line.strip())
读取二进制文件
python 默认读取到的文本文件都是UTF-8,要读取二进制文件,比如图片、视频等,用'rb'模式打开文件
f = open('test.png', 'rb')
print(f.read())
读取文件指定字符编码
open()函数传入参数
# `encoding` 指定读取文件使用的字符编码
f = open('test_gbk.txt', 'r', encoding='gbk')
print(f.read())
当文本文件中夹杂了一些非法编码的字符,可能会遇到UnicodeDecodeError。open()可接收一个errors参数,表示发生编码错误后的处理方式。最简单的处理方式是直接忽略
f = open('test_gbk.txt', 'r', encoding='gbk', errors='ignore')
写文件
调用open()函数时,传入标识符'w'或者'wb'表示写文本文件或写二进制文件:
写文件时,操作系统会先将数据是放到内存缓存起来,空闲的时候再慢慢写入。只有调用 close() 时,未写入的数据才会被保证写入磁盘。如果忘记调用 close(),可能导致数据丢失。
with open('test.txt', 'w') as f:
f.write('王林 李慕婉!')
以追加的方式写入
with open('test.txt', 'a') as f:
f.write('王林 李慕婉!')
StringIO 和 BytesIO
StringIO 和 BytesIO 可在内存中操作 str 和 bytes
StringIO
写 StringIO
from io import StringIO
f = StringIO()
f.write('王林')
f.write(" ")
f.write("李慕婉")
print(f.getvalue())
读 StringIO
# 可以初始化 `StringIO`
f = StringIO("hello\n王林\t李慕婉")
print(f.read())
BytesIO
要操作二进制数据,就需要使用BytesIO
from io import BytesIO
bytes_io = BytesIO()
bytes_io.write("修道,修仙,修真。神通,道法,仙法".encode('utf-8'))
print(bytes_io.getvalue())
os 模块操作文件和目录
操作系统提供了 dir、cd、cp、ls 等命令来操作文件和目录,而在Python中内置的os模块就可以直接调用操作系统提供的接口函数。
import os
import shutil
# posix:Linux、Unix或Mac OS X
# nt:Windows系统
print(os.name)
# 要获取详细的系统信息
print(os.uname())
# posix.uname_result(sysname='Darwin', nodename='cqqMacBook-Pro.local', release='23.4.0', version='Darwin Kernel Version 23.4.0: Fri Mar 15 00:11:05 PDT 2024; root:xnu-10063.101.17~1/RELEASE_X86_64', machine='x86_64')
# 获取系统的环境变量(操作系统中定义的环境变量,都保存在os.environ)
print(os.environ)
# 获取当前目录的绝对路径
print(os.path.abspath('.'))
# 在当前目录下,创建一个新目录
a = os.path.join('.', 'test')
os.mkdir(a)
# 删除一个目录
os.rmdir(a)
# os.path.join() 拼接的路径可正确处理不同操作系统的路径分隔符
# os.path.split() 拆分路径可正确处理不同操作系统的分割符
# os.path.split:后部分是文件或者最后一级目录
print(os.path.split('/path/to/test.txt')) # ('/path/to', 'test.txt')
# os.path.splitext:后部分是文件的扩展名
print(os.path.splitext('/path/to/test.txt')) # ('/path/to/test', '.txt')
# 文件重命名
os.rename('test.txt', 'new.txt')
# 删除文件
os.remove('new.txt')
# 复制文件
# shutil模块提供了copyfile(),shutil模块中有很多实用函数,可以看做是os模块的补充
shutil.copyfile('test.txt', 'new.txt')
# 过滤目录中的文件
files = [x for x in os.listdir(".") if os.path.isfile(x)]
print(files)
# 过滤说有的 .py 文件
py_files = [x for x in os.listdir(".") if os.path.isfile(x) and os.path.splitext(x)[1]=='.py']
print(py_files)
序列化、反序列化
Python的序列化过程称为 pickling 序列化后的内容可写入磁盘,或者在网络上传输。把变量内容从序列化的对象重新读到内存里称之为反序列化 unpickling
Python 的 pickle模块可实现序列化
import pickle
list0 = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
# pickle.dumps()将对象序列化成一个bytes
print(pickle.dumps(list0))
# 将对象序列化后写入一个file-like Object
with open('dump.txt', 'wb') as f:
pickle.dump(list0, f)
# pickle.loads()方法反序列化出对象
# pickle.load()方法从一个file-like Object中直接反序列化出对象
with open('dump.txt', 'rb') as f:
list1 = pickle.load(f)
print(list1)
JSON 的序列化与反序列化
Python内置的json模块提供了对象和JSON的转换
import json
dict0 = dict(id=123, name='司徒南', email='1@1.com')
# 反序列化为 json
# ensure_ascii 保留中文字符
user_json = json.dumps(dict0, ensure_ascii=False)
print(user_json) # {"id": 123, "name": "司徒南", "email": "1@1.com"}
# 序列化为对象
user = json.loads(user_json)
print(user) # {'id': 123, 'name': '司徒南', 'email': '1@1.com'}
序列化对象,序列化对象需要自定义序列化和反序列化的函数
class User:
def __init__(self, id_no, name, email):
self.id_no = id_no
self.name = name
self.email = email
# 自定义序列化函数
def user_serializer(user):
return {
'id': user.id_no,
'name': user.name,
'email': user.email
}
user = User(12, '李慕婉', '2@1.com')
# 序列化为json
# ensure_ascii=False 可以保留中文字符
user_obj_json = json.dumps(user, ensure_ascii=False, default=user_serializer)
print(user_obj_json)
# 自定义反序列化函数
def user_decoder(dct):
if 'id' in dct and 'name' in dct and 'email' in dct:
return User(dct['id'], dct['name'], dct['email'])
return dct
# 将json load为对象
user_obj = json.loads(user_obj_json, object_hook=user_decoder)
print("Deserialized:", user_obj.id_no, user_obj.name, user_obj.email)