【Python系列课程】Python文件操作:从路径处理到with语句
阅读时长20分钟 | 关键词Python文件操作、open()、读写模式、with语句、os模块、路径处理引言和计算机关打交道的唯一方式前面讲的都是计算——加减乘除、列表操作、函数调用。但程序真正强大之处在于能和文件系统打交道读取配置文件.ini、.json、.yaml处理用户上传的文件.csv、.xlsx、图片生成报表输出到本地.txt、.pdf、.docx日志系统.log文件的读写Python 的文件操作 API 设计得非常优雅学完这篇你基本上能应付 90% 的文件处理任务。一、路径操作文件在哪在操作文件之前你得先搞清楚文件在哪——这就是路径做的事。1.1 绝对路径 vs 相对路径类型说明示例Windows示例macOS/Linux绝对路径从根目录开始的完整路径D:\PythonFiles\p01.py/home/user/code/main.py相对路径相对于当前工作目录的路径.\data\input.txt./data/input.txt获取当前工作目录importos cwdos.getcwd()print(cwd)# 输出当前工作目录相对路径的特殊符号.→ 当前目录..→ 上一级目录os.getcwd()# D:\PythonFilesos.path.join(.,data,input.txt)# .\data\input.txtos.path.join(..,main.py)# ..\main.py1.2os.path常用方法速查importos pathrD:\PythonFiles\data\input.txtos.path.basename(path)# input.txt → 最后一级文件名os.path.dirname(path)# D:\PythonFiles\data → 目录部分os.path.split(path)# (D:\...\data, input.txt)os.path.splitext(path)# (D:\...\input, .txt) → 拆分扩展名os.path.exists(path)# False不存在os.path.isfile(path)# Falseos.path.isdir(path)# False[图1os.path各方法解析效果对比图]建议配图一张表中列出同一个路径字符串分别用basename/initname/split/splitext处理后得到的返回值直观展示每个方法切出来的部分。1.3os.path.join()智能拼接路径跨平台必备永远不要用字符串拼接路径不同操作系统的路径分隔符不同Windows 用\macOS/Linux 用/。os.path.join()会根据当前操作系统自动选择正确的分隔符。importos# ❌ 错误写法不跨平台pathdata\\input.txt# 在 Mac 上会出错# ✅ 正确写法pathos.path.join(data,input.txt)print(path)# Windows → data\input.txt# macOS/Linux → data/input.txt1.4 创建目录os.makedirs()importos# 创建单层目录os.mkdir(new_folder)# 递归创建多层目录自动创建中间目录os.makedirs(./dir1/dir2/dir3,exist_okTrue)# exist_okTrue目录已存在时不报错二、open()打开文件的万能钥匙2.1 基本语法fileopen(file,moder,encodingNone)参数说明file文件路径绝对或相对mode打开模式默认r只读encoding文本文件编码建议显式指定utf-8返回一个文件对象迭代器对象可以用for循环逐行读取。2.2 文件打开模式详解模式说明文件不存在时文件已存在时r只读报错FileNotFoundError正常打开指针在开头w只写覆盖创建新文件清空原内容再写入a只写追加创建新文件指针在末尾追加写入x独占创建创建新文件报错FileExistsErrorb二进制模式—需配合r/w/a使用读写模式—需配合r/w/a使用组合模式示例open(file.txt,rb)# 二进制只读如读取图片open(file.txt,wb)# 二进制只写open(file.txt,r)# 读写指针在开头open(file.txt,w)# 读写清空后写入open(file.txt,a)# 读写指针在末尾2.3 文本文件 vs 二进制文件类型编码示例文件打开模式文本文件需要指定encoding.txt、.py、.json、.mdr/w/a默认二进制文件无编码直接读写字节.png、.mp3、.exe、.ziprb/wb/ab三、文件对象的常用方法3.1 读取内容withopen(./exam.txt,r,encodingutf-8)asf:# 方式1next() 读取一行文件是迭代器print(next(f))# 方式2for 循环逐行读取最常用内存友好forlineinf:print(line,end)# end 去掉多余的换行# 方式3read(size) 读取指定字符数f.seek(0)# 指针回到开头contentf.read(5)# 读取前5个字符print(content)# 方式4read() 读取全部f.seek(0)all_contentf.read()print(all_content)# 方式5readlines() 读取全部行到列表f.seek(0)linesf.readlines()print(lines)# [line1\n, line2\n, ...][图2文件指针移动示意图]建议配图一个横向的文本流标注指针位置seek展示 read(5) 从指针处读取5个字符后指针移动到第6个字符位置。3.2 写入内容withopen(./exam.txt,w,encodingutf-8)asf:# write(s)写入字符串返回写入的字符数numf.write(Hello World\n)print(f写入了{num}个字符)# writelines(lines)写入字符串列表不会自动加换行lines[Line 1\n,Line 2\n,Line 3\n]f.writelines(lines)⚠️ 注意writelines()不会自动添加换行符需要自己加\n。3.3 移动文件指针seek(offset)withopen(./exam.txt,r,encodingutf-8)asf:f.write(Hello)f.seek(0)# 指针回到开头print(f.read())# 从头开始读取3.4 刷新缓冲区flush()写入文件时Python 会先把内容放到缓冲区等缓冲区满了或文件关闭时才真正写入磁盘。flush()可以强制立即写入。importtime fopen(./test.txt,a,encodingutf-8)f.write(123456789\n)f.flush()# 立即写入磁盘不等文件关闭time.sleep(5)# 这5秒内文件里已经有内容了f.close()四、with语句最优雅的文件操作方式4.1 为什么要用with传统写法需要手动关闭文件如果中间发生异常close()可能执行不到# ❌ 不推荐的写法fopen(./test.txt,w,encodingutf-8)f.write(hello world)# 如果这里发生异常close() 不会执行 → 文件损坏风险f.close()with语句的好处无论是否发生异常退出with块时自动执行close()。# ✅ 推荐写法withopen(./test.txt,w,encodingutf-8)asf:f.write(hello world)# 退出 with 块时自动调用 f.close()等价于fopen(./test.txt,w,encodingutf-8)try:f.write(hello world)finally:f.close()# 无论如何都会执行4.2withtry/except/finally组合try:withopen(./test.txt,r,encodingutf-8)asf:contentf.read()print(content)exceptFileNotFoundError:print(文件不存在)exceptUnicodeDecodeError:print(文件编码不是 UTF-8)finally:print(文件操作结束自动关闭)五、实战几个常用文件操作场景5.1 复制文件defcopy_file(src,dst):复制文件支持文本和二进制withopen(src,rb)asfsrc:withopen(dst,wb)asfdst:whileTrue:chunkfsrc.read(4096)# 每次读 4KBifnotchunk:breakfdst.write(chunk)print(f已复制{src}→{dst})copy_file(./source.jpg,./backup/copy.jpg)5.2 逐行处理日志文件内存友好# 处理大文件时绝对不能用 read() 一次性读入内存# 用 for 循环逐行读取每次只占用一行的内存error_count0withopen(./app.log,r,encodingutf-8)asf:forlineinf:# 逐行迭代内存占用极低ifERRORinline:error_count1print(f发现错误{line.strip()})print(f共发现{error_count}处错误)5.3 CSV 文件读写数据分析必备importcsv# 读取 CSVwithopen(./data.csv,r,encodingutf-8)asf:readercsv.reader(f)headernext(reader)# 跳过表头print(f表头{header})forrowinreader:print(row)# 写入 CSVwithopen(./output.csv,w,newline,encodingutf-8)asf:writercsv.writer(f)writer.writerow([姓名,年龄,城市])# 写表头writer.writerows([[小明,25,北京],[小红,23,上海],])六、动手练习练习 1统计文件行数defcount_lines(filepath):统计文件行数withopen(filepath,r,encodingutf-8)asf:returnsum(1for_inf)# 逐行迭代计数print(count_lines(./exam.txt))练习 2查找包含关键词的行defgrep(filepath,keyword):查找包含关键词的所有行withopen(filepath,r,encodingutf-8)asf:fori,lineinenumerate(f,1):ifkeywordinline:print(f第{i}行{line.strip()})grep(./app.log,ERROR)练习 3批量重命名文件importosdefbatch_rename(folder,old_ext,new_ext):批量修改文件扩展名forfilenameinos.listdir(folder):iffilename.endswith(old_ext):old_pathos.path.join(folder,filename)new_filenamefilename.replace(old_ext,new_ext)new_pathos.path.join(folder,new_filename)os.rename(old_path,new_path)print(f{filename}→{new_filename})batch_rename(./images,.jpeg,.jpg)小结知识点一句话总结路径操作用os.path.join()拼接路径跨平台open()打开文件返回文件对象指定mode和encoding读写模式r只读 /w覆盖 /a追加 /b二进制读取方法read()全读 /readline()读一行 /for line in f逐行推荐写入方法write()写字符串 /writelines()写列表with语句自动关闭文件异常处理更安全必须用大文件处理用for line in f逐行读取不要用read()[图3文件操作流程全景图]建议配图一张完整的流程图从open()打开文件 → 选择读写模式 → 调用 read/write 方法 → 关闭文件close 或 with 自动关闭并在底部标注文本模式 vs 二进制模式的区别。本文是「Python从入门到数据分析」系列的第 13 篇共 24 篇。关注我不错过后续更新。