技术栈
python文件IO学习
布值倒区什么name
2026-08-08 12:48
Python 文件 IO
from email.encoders import encode_noop
from fileinput import close
文件 IO 核心概念
文件 IO 就是磁盘文件的读取、写入、追加、修改、遍历、删除操作。
什么是文件 IO
IO = Input (读文件) / Output (写文件)
计算机文件分两类:
文本文件:txt、csv、py、json,字符编码存储(utf-8/gbk),肉眼可读
二进制文件:图片、视频、exe、zip、4G 模组日志 bin,直接字节存储,不能直接用文本打开乱码
Python 文件操作核心入口:内置函数 open(),分为两大读写模式:
文本模式(Text Mode):读写字符串,自动处理换行符编码,.txt/.csv/.log使用;
二进制模式(Binary Mode):读写原始字节bytes,无编码转换,图片、视频、exe、固件、Base64 都用二进制读写。
文件操作标准流程:
打开文件 → 读写操作 → 关闭文件
重点:文件必须关闭!否则会出现文件句柄泄漏、内容写不全、文件被锁定无法删除。
open () 函数完整参数语法
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
file:文件路径(相对路径 / 绝对路径)
相对路径:"test.txt"(和 py 脚本同目录)
绝对路径:"C:/log/device.log"(Windows 斜杠建议用/或\\,避免转义)
mode:打开模式
encoding:文本模式指定编码,Windows 默认 gbk,Linux/mac 默认 utf-8,中文文件强制写 encoding="utf-8"
errors:编码错误处理
errors="ignore":忽略乱码字符(读取破损日志用)
errors="strict":编码错误直接抛异常(默认)
newline:控制换行符转换(文本模式专用,Windows 换行\r\n,Linux\n)
buffering:缓冲区设置,大文件读写调整缓冲区提升性能
mode 打开模式
模式 含义 文件不存在 文件存在 适用场景
r 只读(文本) 报错 FileNotFoundError 从头读取 读取配置、日志
w 只写(文本) 创建新文件 清空全部内容再写入 覆盖写入日志
a 追加写(文本) 创建新文件 在文件末尾新增内容 滚动日志写入
r+ 读写 报错 可读可写,覆盖前部内容 修改文件中部
w+ 读写 创建 直接清空 先清空再读写
a+ 追加读写 创建 只能在末尾写,可读 日志边读边追加
rb 只读二进制 报错 读取字节 图片、视频、固件
wb 覆盖写二进制 创建 清空覆盖 保存二进制数据
ab 追加二进制 创建 尾部追加字节 二进制日志续存
后缀说明:
带b = 二进制模式,不能指定 encoding,读写都是bytes
不带b = 文本模式,必须指定encoding防止乱码
encoding 编码(文本模式专用)
常用:
encoding="utf-8"(通用,Linux、Python 默认)
encoding="gbk"(Windows 记事本默认编码)
不指定编码:Windows 默认 gbk,Linux 默认 utf-8,极易中文乱码。
errors:编码容错
errors="strict" 默认,编码错误直接抛异常
errors="ignore" 忽略无法解码的字节(日志兼容常用)
errors="replace" 乱码字符替换为�
newline:换行符适配
Windows 换行\r\n,Linux\n,设置newline=''可以保留原始换行符,解析日志必备
1. 文件操作核心流程
Python 文件 IO 本质是通过操作系统提供的文件描述符(File Descriptor,FD)与磁盘交互,
拿到f = open(...)后可用方法:
读取类
f.read(n):读取 n 个字符 (文本)/n 字节 (二进制),不传 n 读取整个文件
f.readline():读取一行(包含换行符)
f.readlines():读取所有行,返回列表
"行1\\n","行2\\n"
写入类
f.write("字符串"):写入文本,返回写入字符数
f.write(b'\x00\x01'):二进制写入 bytes
f.writelines(
"第一行","第二行"
):批量写入序列,不会自动加换行
指针 & 刷新
f.tell():获取当前文件指针位置(字节偏移)
f.seek(offset, whence):移动文件指针
whence=0:从文件开头偏移
whence=1:从当前位置偏移(二进制模式才支持)
whence=2:从文件末尾偏移
f.flush():强制把缓冲区数据写入磁盘(不关闭文件也落地)
收尾
f.close():关闭文件,释放句柄
核心流程分为3步,每一步都有对应的底层机制,且涉及操作系统内核与用户态的交互:
1.打开文件:调用操作系统 open() 系统调用,内核为文件分配文件描述符(非负整数,如0代表标准输入、1代表标准输出、2代表标准错误)、文件对象(包含读写指针、权限、状态、缓冲区等信息),并将文件描述符返回给用户态。Python 中通过 open() 函数封装此过程,返回的文件对象(如 _io.TextIOWrapper)包含 FD、模式、缓冲区、编码等属性,可通过 fileno() 方法获取文件描述符。
示例:
with open("test.txt", "r") as f:
print(f"文件描述符:{f.fileno()}") # 输出:3(默认前3个FD被标准占用)
2.读写操作:通过文件对象调用 read()/write() 等方法,本质是调用操作系统 read()/write() 系统调用,
数据会先经过内核缓冲区(Page Cache),再同步到磁盘(默认非实时,需手动 flush 或关闭文件触发同步)。
内核缓冲区的作用是减少磁盘 I/O 次数,提升效率,例如多次小写入会被合并为一次磁盘写入。
示例:
with open("test.txt", "w") as f:
f.write("hello") # 数据先写入内核缓冲区,未立即到磁盘
f.flush() # 强制刷新缓冲区,数据同步到磁盘
关闭文件:调用操作系统 close() 系统调用,释放文件描述符、内核资源,清空缓冲区数据并同步到磁盘,
Python 中 close() 方法会触发此操作,with 语句会自动执行 close(),避免资源泄漏。
若未关闭文件,会导致 FD 耗尽(默认单进程可打开文件数约1024,可通过 ulimit -n 查看),
后续文件操作报错。示例:# 错误写法(未关闭文件,资源泄漏)
f = open("test.txt", "w")
f.write("test")
3.# 正确写法(with 自动关闭)
with open("test.txt", "w") as f:
f.write("test")
文件描述符是有限资源(默认单进程可打开文件数约1024),未关闭文件会导致 FD(文件描述符 File Descriptor,FD)耗尽,后
续文件操作报错。可通过以下代码查看当前进程已打开的文件数:
import os
import psutil
pid = os.getpid() # 获取当前进程ID
process = psutil.Process(pid)
print(f"当前进程已打开文件数:{len(process.open_files())}")
2. 缓冲区机制(核心影响读写效率与数据安全)
缓冲区是内存中的临时存储区域,用于减少系统调用次数,提升 IO 效率,分为3种类型,对应 open() 的 buffering 参数,
其底层逻辑是通过内存缓冲降低磁盘访问频率,不同缓冲类型适用于不同场景:
•全缓冲(buffering=-1,默认):缓冲区填满后才触发系统调用,文本模式下缓冲区大小为4096字节(4KB),二进制模式为系统默认(通常4096或8192字节)。
适用于大文件批量读写,效率最高,因为减少了系统调用次数。
示例:# 全缓冲写入,缓冲区填满后才同步到磁盘
with open("big_file.txt", "w", buffering=-1) as f:
for i in range(1000):
f.write(f"line {i}\n") # 多次写入,缓冲区填满后一次性同步
•行缓冲(buffering=1):文本模式下,遇到换行符 \n 即触发缓冲区刷新,同步到磁盘。适用于日志文件实时写入(如程序运行日志,需实时查看最新内容),
确保每一行日志都能及时落地。
示例:# 行缓冲,每写入一行就同步到磁盘
with open("app.log", "a", buffering=1) as f:
f.write("log line 1\n") # 立即同步到磁盘
f.write("log line 2\n") # 立即同步到磁盘
•无缓冲(buffering=0):每次读写操作直接触发系统调用,不经过缓冲区,数据实时同步到磁盘。适用于需要实时同步的关键数据(如配置文件修改、关键日志记录),
但效率最低,不适合大文件,因为每次操作都要访问磁盘。
示例:# 无缓冲,每次写入都直接同步到磁盘
with open("critical_data.txt", "w", buffering=0) as f:
f.write(b"critical data") # 注意:无缓冲时写入需为bytes
实操技巧:手动刷新缓冲区可调用 f.flush() 方法,强制将缓冲区数据同步到磁盘,无需等待缓冲区填满或关闭文件。此外,
可通过 f.buffer 属性访问底层缓冲区对象,查看缓冲区状态(如是否已满)。
示例:with open("test.txt", "w") as f:
f.write("test data")
print(f"缓冲区是否已满:{f.buffer.full()}") # 查看缓冲区状态
f.flush() # 强制刷新
3. 编码与换行符底层处理
3.1 编码转换底层逻辑
文本模式下,Python 会自动对读写数据进行编码/解码:读取时,将磁盘中的字节(bytes)按指定编码解码为字符串(str);
写入时,将字符串按指定编码编码为字节写入磁盘。二进制模式下无编码转换,直接读写原始字节。其底层逻辑是通过 codec 模块实现编码/解码,
不同编码对应不同的字节映射规则(如 UTF-8 用1-4字节表示一个字符,GBK 用2字节表示一个字符)。
示例:# 编码转换示例
text = "中文测试"
utf8_bytes = text.encode("utf-8") # 编码为UTF-8字节:b"\xe4\xb8\xad\xe6\x96\x87\xe6\xb5\x8b\xe8\xaf\x95"
gbk_bytes = text.encode("gbk") # 编码为GBK字节:b"\xd6\xd0\xce\xc4\xb2\xe2\xca\xd4"
print(utf8_bytes)
print(gbk_bytes)
# 解码示例
decoded_text = utf8_bytes.decode("utf-8") # 解码为字符串:"中文测试"
print(decoded_text)
见编码问题:Windows 系统默认编码为 GBK,Linux/macOS 为 UTF-8,若文件编码与代码中指定的 encoding 不一致,
会出现 UnicodeDecodeError 乱码,需精准匹配编码或使用 errors="ignore"/"replace" 处理。
若不确定文件编码,可使用 chardet 库检测(pip install chardet)。示例(检测编码):import chardet
with open("unknown_encoding.txt", "rb") as f:
raw_data = f.read()
result = chardet.detect(raw_data)
print(f"文件编码:{result
'encoding'
},置信度:{result
'confidence'
}")
# 根据检测结果读取文件
with open("unknown_encoding.txt", "r", encoding=result
'encoding'
) as f:
content = f.read()
换行符转换机制
不同系统换行符不同:Windows 为 \r\n(CRLF,占2字节),Linux/macOS 为 \n(LF,占1字节),
Python 文本模式下会自动处理换行符:读取时将系统换行符转换为 \n,写入时将 \n 转换为系统对应换行符。
可通过 newline 参数控制,其底层逻辑是在读写时对换行符进行映射转换,避免跨平台文件共享时出现换行符混乱。
示例(跨平台换行符处理):
•newline=None(默认):自动适配系统换行符,读写时自动转换。适用于本地文件操作,无需关心跨平台问题。
示例:# Windows 系统写入,换行符会自动转为 \r\n
with open("test.txt", "w", newline=None) as f:
f.write("line1\nline2")
# 读取时,\r\n 会自动转为 \n
with open("test.txt", "r", newline=None) as f:
print(f.read()) # 输出:line1\nline2
•newline='':不进行换行符转换,读取时保留原始换行符,写入时直接写入 \n。适用于需要保留原始换行符的场景(如跨平台文件传输)。
示例:# 写入时不转换换行符,直接写入 \n
with open("test.txt", "w", newline='') as f:
f.write("line1\nline2")
# 读取时保留原始换行符(Windows 下仍为 \n)
with open("test.txt", "r", newline='') as f:
print(f.read()) # 输出:line1\nline2
•newline='\n'/'\r\n':强制指定换行符,读写时统一使用该换行符,跨平台文件共享时常用,确保文件在不同系统中换行符一致。
示例:# 强制使用 \n 作为换行符,跨平台统一
with open("cross_platform.txt", "w", newline='\n') as f:
f.write("line1\nline2")
# 读取时也强制按 \n 解析
with open("cross_platform.txt", "r", newline='\n') as f:
print(f.read()) # 输出:line1\nline2
基础读写实战(不推荐裸 open,仅演示原理)
案例 1:文本只读 r
读取日志文件
f = open("test.txt",mode="r",encoding="utf-8",errors="ignore")
content = f.readline()
print(content)
f.close()
案例 2:覆盖写入 w
f = open("test.txt","w",encoding="utf-8",errors="ignore")
f.write("test\n")
f.write("test1")
f.close()
案例 3:追加写入 a
f = open("test.txt","a",encoding="utf-8",errors="ignore")
f.write("\ntest3\n")
f.write("test4")
f.close()
案例 4:二进制读写(图片 / 固件 / 模组 bin 日志)
读取图片 rb:Read Binary 二进制只读
f = open("123.png","rb")
img_bytes = f.read()
print(img_bytes)
f.close()
复制图片 wb:Write Binary 二进制覆盖写入 新建 / 覆盖文件写入二进制流,文件不存在则创建,存在直接清空重写。
ab = 二进制追加写入
f_out = open("123_copy.png","wb")
f_out.write(img_bytes)
f_out.close()
案例 5:按行读取大文件(避免一次性加载内存溢出)
f = open("test.txt", "r", encoding="utf-8")
for line in f: # 迭代器,一行一行加载,内存友好
if "test" in line:
print("存在test:", line.strip())
f.close()
with 上下文管理器 替代手动 close
为什么要用 with?
自动关闭文件:即使代码报错、异常崩溃,也会执行 close (),杜绝句柄泄漏
代码简洁,不用手动写f.close()
基础语法
with open("test.txt", "r", encoding="utf-8") as f:
print(f.read())
代码离开with代码块,文件自动关闭
with 追加写入
def with_add_text(text:str):
with open("test.txt", "a", encoding="utf-8") as f:
f.write(f"{text}\n")
with_add_text("123")
with_add_text("456")
同时打开两个文件(读取 + 写入)
with open("test.txt", "r", encoding="utf-8") as f, open("test1.txt", "a", encoding="utf-8") as g:
for line in f:
g.write(f"{line}")
文件指针 seek /tell 进阶(修改文件、截断)
文件是线性数据流,读写共用一个指针,写会覆盖当前指针位置内容。
w+模式:打开文件清空原有内容,可读可写;w 只能写,调用f.read()直接报错
当你传入 encoding 参数时,Python 类型检查器(Pyright / Pylance / VSCode 类型提示)强制要求:
文件打开模式必须是文本模式,不能只写w+(类型识别歧义)。
w+ 同时兼容文本 / 二进制,类型推断模糊,加上encoding参数直接触发类型警告:
Unexpected type(s): (str, Literal
"w+"
, None)
w+ 简写,类型工具分不清是文本wt+还是二进制wb+,可以不屑encoding
with open("test.txt", "w+") as f:
f.write("hello world")
print(f.tell())
f.seek(0) # 指针跳回文件开头
print(f.read())
f.seek(2)
print(f.read(3))
截断文件(清空文件后半段)
with open("test.txt", "r+", encoding="utf-8") as f:
f.seek(2)
print(f.read()) # f.read() 读完所有内容,文件指针跑到文件最后,truncate() 直接把文件截到末尾,等于没截断效果。
f.truncate() # 从指针位置截断,后面内容全部删除
# print(f.read())
需要先截断,再读,不能先read
with open("test.txt", "r+", encoding="utf-8") as f:
f.seek(2)
f.truncate() # 直接在位置2截断,文件只剩 AB
f.seek(0)
print(f.read()) # AB
:truncate 带参数用法(固定截断长度)
f.truncate(size):直接把文件强制裁剪为 size 字节,无视当前指针
with open("test.txt", "r+", encoding="utf-8") as f:
f.truncate(3) # 文件只保留前3字节
路径处理:os.path vs pathlib(推荐 pathlib,Python3.4+)
手动拼接路径极易出错,跨 Windows/Linux 兼容用路径库。
from pathlib import Path
# 定义文件路径
log_path = Path("./modem/modem.log")
# 父目录
print(log_path.parent)
# 判断文件是否存在
print(log_path.exists())
# 创建父目录(不存在则创建)
log_path.parent.mkdir(parents=True, exist_ok=True)
# 直接打开文件,无需open
with log_path.open("a", encoding="utf-8") as f:
f.write("日志内容\n")
# 获取绝对路径
print(log_path.absolute())
# 文件名、后缀
print(log_path.name, log_path.suffix)
os.path(传统写法,老项目兼容)
import os
file_path = os.path.join("modem", "modem.log") # 自动适配斜杠
print(os.path.exists(file_path))
print(os.path.dirname(file_path)) # 目录
os.makedirs(os.path.dirname(file_path), exist_ok=True)
文件 IO 异常处理
文件操作高频异常:
FileNotFoundError:文件不存在(r 模式)
PermissionError:权限不足(Linux /dev/radio_cmd 典型报错)
UnicodeDecodeError:文本编码不匹配乱码报错
OSError:硬件设备文件异常
带异常捕获的写入函数
from pathlib import Path
def safe_write_log(file_path: Path, msg: str):
try:
# 确保文件夹存在
file_path.parent.mkdir(parents=True, exist_ok=True)
with file_path.open("a", encoding="utf-8", errors="ignore") as f:
f.write(f"{msg}\n")
f.flush() # 强制落盘,防止断电丢失日志
print(f"写入成功")
except PermissionError:
print(f"错误:文件{file_path}权限不足,无法写入")
except OSError as e:
print(f"系统文件异常:{e}")
except Exception as e:
print(f"未知异常:{type(e).
name
}: {e}")
# 测试写入模组日志
log_file = Path("./modem.log")
print("日志完整路径:", log_file.resolve())
safe_write_log(log_file, "
MODEM
modem_current_state:1")
safe_write_log(log_file, "
MODEM
open interface /dev/radio_cmd failed.")
from pathlib import Path
def parse_modem_log(log_file: Path):
error_state = {1: "模组初始化失败", 19: "拨号PDP激活失败"}
result = \[\]
with log_file.open("r", encoding="utf-8", errors="ignore") as f:
for line in f:
line = line.strip()
if "modem_current_state:" in line:
state_code = int(line.split(":")
-1
)
desc = error_state.get(state_code, "未知状态")
result.append((line, state_code, desc))
return result
# 使用
logs = parse_modem_log(Path("modem.log"))
for item in logs:
print(f"原始日志:{item
0
} | 状态码:{item
1
} | 说明:{item
2
}")
二进制文件批量复制
from pathlib import Path
def copy_bin_file(src: Path, dst: Path):
if not src.exists():
print("源文件不存在")
return
# 二进制块读写,防止大文件内存爆满
chunk_size = 4096
with src.open("rb") as fr, dst.open("wb") as fw:
while chunk := fr.read(chunk_size):
fw.write(chunk)
copy_bin_file(Path("firmware.bin"), Path("firmware_backup.bin"))
清空文件内容
方式1
with open("clear.log", "w", encoding="utf-8") as f:
pass
# 方式2 truncate
with open("clear.log", "r+", encoding="utf-8") as f:
f.truncate(0)
python
上一篇:
COVE:记忆-参数双通道协调自进化
下一篇:
【RHCA+】for循环
相关推荐
丁引
1 小时前
《数据清洗的艺术:如何用20行核心逻辑优雅地删除无标签图片》
前端
·
数据库
·
python
梦想的旅途2
1 小时前
企业微信API二次开发:外部群模块功能清单与全场景对接
java
·
python
·
企业微信
056646
1 小时前
Python高级——迭代器
开发语言
·
python
·
学习
老白干
2 小时前
基于 Spring AOP 的操作日志记录:以 DeptController 增删接口为例
java
·
python
·
spring
John jj
2 小时前
拆解 Telegram 群组频道收录市场:三类方案,一个可运行的评分模型,目前TG中文人工评分加模型评分机制——LetsTG收录“快速”、“无门槛”
大数据
·
后端
·
python
·
深度学习
·
搜索引擎
·
django
·
全文检索
aqi00
2 小时前
15天学会AI应用开发(十九)使用LangGraph实现持久记忆功能
人工智能
·
python
·
大模型
·
ai编程
·
ai应用
月光船幽幽
2 小时前
KDTree查表实现观测到动作的统计映射
python
2401_86853478
2 小时前
论信息系统安全保障规划与设计
python
·
django
热门推荐
01
如何新建文件夹? 电脑新建文件夹的4种方法
02
GitHub 镜像站点
03
国内可直接用、免费额度/永久免费的大模型API清单(含 SiliconFlow、火山、阿里、智谱、百度、Kimi、DeepSeek、DMXAPI 等)
04
AI 编程 IDE 全景解析 2026:Agent 全面接管开发链路
05
2026 国产 AI 大模型横评:DeepSeek、通义千问、Kimi、文心一言、星火、豆包谁更能打?
06
Agnes AI 免费 API 接入指南:文本、生图、生视频,一套接口全免费
07
微信历史版本含下载地址( Windows PC | 安卓 | MAC )及设置微信不更新
08
历年考研数学一、数学二、数学三真题试卷及答案PDF
09
2026年7月AI圈大地震:GPT-5.6被政府限制、Claude入驻Slack、Anthropic自研芯片
10
00 Debian字符界面如何支持中文