Python零基础入门(六):文件操作
前言
前五篇我们完成了环境搭建与数据类型、运算符与条件判断、面向对象编程基础、模块与包导入和异常处理。你已经能让程序"算"、能"想"、能"组织"、能"容错"——但程序运行的结果总是转瞬即逝,关闭终端就没了。
如何让数据持久化?如何读取配置文件?如何生成报告?如何处理日志?
这就是本篇要解决的核心问题:文件操作。文件操作让你的程序能与外部世界交换数据——读取文本、保存结果、处理日志、导出报表。
一、文件操作基础
Python 内置了强大的文件操作功能,无需额外安装模块即可完成绝大多数文件读写任务。
1.1 打开文件:open() 函数
# 基本语法file = open("example.txt", "r", encoding="utf-8")# 参数说明:# - 第一个参数:文件路径(相对路径或绝对路径)# - 第二个参数:打开模式(见下表)# - encoding:编码格式(处理文本文件时推荐指定)
打开模式一览:
| | | |
|---|
'r' | | | |
'w' | | | 清空后写入 |
'a' | | | |
'x' | | | |
'b' | | | |
't' | | | |
'+' | | | |
常见组合:
open("file.txt", "r") # 只读文本open("file.txt", "w") # 写入文本(清空原内容)open("file.txt", "a") # 追加文本open("file.bin", "rb") # 只读二进制open("file.bin", "wb") # 写入二进制open("file.txt", "r+") # 读写文本
1.2 读取文件内容
# 方法一:read() —— 读取全部内容file = open("poem.txt", "r", encoding="utf-8")content = file.read() # 返回整个文件内容的字符串print(content)file.close() # 记得关闭文件!# 方法二:readline() —— 逐行读取file = open("poem.txt", "r", encoding="utf-8")line1 = file.readline() # 读取第一行(包含换行符 \n)line2 = file.readline() # 读取第二行print(line1, line2)file.close()# 方法三:readlines() —— 读取所有行到列表file = open("poem.txt", "r", encoding="utf-8")lines = file.readlines() # 返回列表,每个元素是一行for line in lines: print(line.strip()) # strip() 去除换行符file.close()# 方法四:迭代读取(推荐,内存友好)file = open("poem.txt", "r", encoding="utf-8")for line in file: # 文件对象本身可迭代 print(line.strip())file.close()
注意:readline() 和 readlines() 读取的行末尾包含换行符 \n,通常需要用 strip() 或 rstrip() 去除。
1.3 写入文件
# 写入字符串file = open("output.txt", "w", encoding="utf-8")file.write("第一行内容\n") # write() 不会自动添加换行符file.write("第二行内容\n")file.close()# 写入多行(writelines)lines = ["苹果\n", "香蕉\n", "橘子\n"]file = open("fruits.txt", "w", encoding="utf-8")file.writelines(lines) # 不会自动添加换行符,需要自己处理file.close()# 追加模式from datetime import datetimefile = open("log.txt", "a", encoding="utf-8")file.write(f"[{datetime.now()}] 用户登录\n")file.close()
1.4 文件指针操作
文件有一个"指针",记录当前读写位置:
file = open("example.txt", "r", encoding="utf-8")# tell() —— 获取当前位置print(file.tell()) # 0(开头)# read(n) —— 读取 n 个字符data = file.read(5)print(file.tell()) # 5(移动了 5 个字符)# seek(offset, whence) —— 移动指针# whence: 0=开头(默认),1=当前位置,2=末尾file.seek(0) # 回到开头print(file.tell()) # 0file.close()
注意:seek() 在文本模式下,whence 参数只能是 0(只有二进制模式支持 1 和 2)。
二、上下文管理器:with 语句
手动调用 file.close() 容易遗忘,更糟糕的是如果读写过程中发生异常,文件可能不会被正确关闭。with 语句(上下文管理器)完美解决了这个问题:
# 传统写法(有风险)file = open("data.txt", "r")content = file.read()# 如果这里发生异常,file.close() 不会执行!file.close()# with 写法(安全)with open("data.txt", "r", encoding="utf-8") as file: content = file.read()# 退出 with 块后,文件自动关闭(即使发生异常)
2.1 with 语句的优势
- 1. 自动关闭文件:离开 with 块后自动调用
close()
2.2 推荐用法
始终使用 with 语句操作文件:
# 读取with open("input.txt", "r", encoding="utf-8") as f: for line in f: print(line.strip())# 写入with open("output.txt", "w", encoding="utf-8") as f: f.write("Hello, Python!\n") f.write("文件操作很简单\n")# 复制文件with open("source.txt", "r", encoding="utf-8") as src: content = src.read()with open("dest.txt", "w", encoding="utf-8") as dst: dst.write(content)
2.3 同时打开多个文件
Python 3.10+ 支持在同一个 with 语句中打开多个文件:
# 复制文件(同时打开源和目标)with open("source.txt", "r", encoding="utf-8") as src, \ open("dest.txt", "w", encoding="utf-8") as dst: for line in src: dst.write(line)
三、文件和目录操作:os 模块
Python 的 os 模块提供了丰富的文件和目录操作功能。
3.1 路径操作
import os# 获取当前工作目录cwd = os.getcwd()print(cwd)# 路径拼接(推荐,跨平台兼容)path = os.path.join("data", "files", "test.txt")print(path) # data/files/test.txt(Linux/Mac) # data\files\test.txt(Windows)# 获取文件名和目录print(os.path.basename("/home/user/test.txt")) # test.txtprint(os.path.dirname("/home/user/test.txt")) # /home/user# 分离扩展名name, ext = os.path.splitext("report.pdf")print(name) # reportprint(ext) # .pdf# 判断路径是否存在print(os.path.exists("/tmp/test.txt")) # True 或 False# 判断是文件还是目录print(os.path.isfile("/tmp/test.txt")) # Trueprint(os.path.isdir("/tmp")) # True# 获取文件大小(字节)size = os.path.getsize("test.txt")print(f"文件大小:{size} 字节")# 获取绝对路径print(os.path.abspath(".")) # 当前目录的绝对路径
3.2 目录操作
import os# 创建目录os.mkdir("new_folder") # 创建单级目录os.makedirs("a/b/c", exist_ok=True) # 创建多级目录(exist_ok=True 避免已存在时报错)# 列出目录内容files = os.listdir(".") # 列出当前目录所有文件和文件夹print(files)# 遍历目录树for root, dirs, files in os.walk("my_project"): print(f"当前目录:{root}") print(f" 子目录:{dirs}") print(f" 文件:{files}")# 删除目录os.rmdir("empty_folder") # 删除空目录import shutilshutil.rmtree("folder_to_delete") # 删除非空目录(谨慎使用!)
3.3 文件操作
import osimport shutil# 重命名/移动文件os.rename("old.txt", "new.txt")os.rename("file.txt", "archive/file.txt") # 移动# 复制文件shutil.copy("source.txt", "dest.txt") # 复制文件shutil.copy2("source.txt", "dest.txt") # 复制文件(保留元数据)shutil.copytree("src_folder", "dest_folder") # 复制整个目录# 删除文件os.remove("file.txt")os.unlink("file.txt") # 和 remove() 等价
四、实用文件处理技巧
4.1 统计文件信息(类似 wc 命令)
def count_file(filename): """统计文件的行数、单词数和字符数""" lines = 0 words = 0 chars = 0 with open(filename, "r", encoding="utf-8") as f: for line in f: lines += 1 words += len(line.split()) chars += len(line) return lines, words, chars# 使用lines, words, chars = count_file("example.txt")print(f"行数:{lines},单词数:{words},字符数:{chars}")
4.2 安全写入(避免覆盖已有文件)
import osdef safe_write(filename, content): """安全写入文件,如果文件已存在则添加数字后缀""" if not os.path.exists(filename): with open(filename, "w", encoding="utf-8") as f: f.write(content) return filename # 文件已存在,生成新文件名 name, ext = os.path.splitext(filename) counter = 1 while os.path.exists(f"{name}_{counter}{ext}"): counter += 1 new_filename = f"{name}_{counter}{ext}" with open(new_filename, "w", encoding="utf-8") as f: f.write(content) return new_filename# 使用result = safe_write("report.txt", "这是报告内容")print(f"文件已保存:{result}")
4.3 读取配置文件
def read_config(filename): """读取 key=value 格式的配置文件""" config = {} try: with open(filename, "r", encoding="utf-8") as f: for line_num, line in enumerate(f, 1): line = line.strip() # 跳过空行和注释 if not line or line.startswith("#"): continue # 解析 key=value if "=" not in line: print(f"警告:第 {line_num} 行格式错误,已跳过") continue key, value = line.split("=", 1) config[key.strip()] = value.strip() except FileNotFoundError: print(f"配置文件不存在:{filename}") except Exception as e: print(f"读取配置文件失败:{e}") return config# 使用config = read_config("app.conf")print(config)
4.4 批量重命名文件
import osdef batch_rename(folder, prefix="file", start=1): """批量重命名文件夹中的文件""" files = sorted(os.listdir(folder)) renamed = [] for i, filename in enumerate(files, start): # 获取扩展名 _, ext = os.path.splitext(filename) # 新文件名 new_name = f"{prefix}_{i:03d}{ext}" # 完整路径 old_path = os.path.join(folder, filename) new_path = os.path.join(folder, new_name) # 重命名 os.rename(old_path, new_path) renamed.append((filename, new_name)) print(f"重命名:{filename} → {new_name}") return renamed# 使用# batch_rename("photos", prefix="photo", start=1)
4.5 文件备份工具
import osimport shutilfrom datetime import datetimedef backup_files(source_dir, backup_dir, extensions=None): """ 备份指定目录下的文件 Args: source_dir: 源目录 backup_dir: 备份目录 extensions: 要备份的文件扩展名列表,如 [".txt", ".py"],None 表示全部 """ # 创建备份目录 os.makedirs(backup_dir, exist_ok=True) # 记录备份信息 backup_log = [] timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") # 遍历源目录 for root, dirs, files in os.walk(source_dir): for filename in files: # 检查扩展名 if extensions: _, ext = os.path.splitext(filename) if ext.lower() not in extensions: continue # 源文件路径 src_path = os.path.join(root, filename) # 保持目录结构 rel_path = os.path.relpath(src_path, source_dir) dst_path = os.path.join(backup_dir, rel_path) # 创建目标目录 os.makedirs(os.path.dirname(dst_path), exist_ok=True) # 复制文件 shutil.copy2(src_path, dst_path) backup_log.append(rel_path) print(f"备份:{rel_path}") # 写入备份日志 log_path = os.path.join(backup_dir, "backup_log.txt") with open(log_path, "w", encoding="utf-8") as f: f.write(f"备份时间:{timestamp}\n") f.write(f"源目录:{source_dir}\n") f.write(f"备份目录:{backup_dir}\n") f.write(f"文件数量:{len(backup_log)}\n") f.write("\n备份文件列表:\n") for file in backup_log: f.write(f" - {file}\n") print(f"\n备份完成!共 {len(backup_log)} 个文件") print(f"日志已保存:{log_path}")# 使用# backup_files("project", "backup", extensions=[".txt", ".py", ".json"])
五、综合实战:日志文件处理器
让我们结合文件操作和异常处理,实现一个实用的日志处理器:
import osfrom datetime import datetimeclass Logger: """简单的日志记录器""" def __init__(self, log_file="app.log"): self.log_file = log_file # 确保日志目录存在 log_dir = os.path.dirname(log_file) if log_dir and not os.path.exists(log_dir): os.makedirs(log_dir, exist_ok=True) def _write_log(self, level, message): """写入日志(内部方法)""" timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") log_entry = f"[{timestamp}] [{level}] {message}\n" try: with open(self.log_file, "a", encoding="utf-8") as f: f.write(log_entry) except PermissionError: print(f"警告:没有权限写入日志文件 {self.log_file}") except Exception as e: print(f"警告:写入日志失败 - {e}") def info(self, message): """记录信息级别日志""" self._write_log("INFO", message) def error(self, message): """记录错误级别日志""" self._write_log("ERROR", message) def warning(self, message): """记录警告级别日志""" self._write_log("WARNING", message) def read_logs(self, n=10): """读取最后 n 条日志""" try: with open(self.log_file, "r", encoding="utf-8") as f: lines = f.readlines() return lines[-n:] if len(lines) >= n else lines except FileNotFoundError: return [] except Exception as e: print(f"读取日志失败:{e}") return [] def clear_logs(self): """清空日志文件""" try: with open(self.log_file, "w", encoding="utf-8") as f: f.write("") return True except Exception as e: print(f"清空日志失败:{e}") return False# 使用示例if __name__ == "__main__": logger = Logger("logs/application.log") logger.info("应用程序启动") logger.info("用户登录:张三") logger.warning("磁盘空间不足") logger.error("数据库连接失败") print("\n最近的日志:") for log in logger.read_logs(5): print(log.strip())
这个例子展示了:
- 4. 上下文管理器:使用
with 语句安全操作文件
六、动手练习
- 1. 写一个程序,读取一个文本文件,统计其中的行数、单词数和字符数(类似
wc 命令)。 - 2. 创建一个函数
safe_write(filename, content),写入文件时如果文件已存在,自动在文件名后添加数字后缀(如 file_1.txt、file_2.txt)。 - 3. 实现一个配置文件读取器,支持读取简单的
key=value 格式的配置文件,处理文件不存在、格式错误等异常。 - 4. 写一个文件备份工具,给定源目录,将所有
.txt 文件复制到备份目录,并在备份目录中创建一个 backup_log.txt 记录备份时间、文件列表等信息。 - 5. 实现一个简单的"记事本"程序,支持以下功能:
七、小结
本篇我们系统学习了 Python 的文件操作:
核心语法
- • 读写方法:
read()、readline()、readlines()、write()、writelines()
os 模块
- • 路径操作:
join()、basename()、dirname()、splitext()、exists() - • 目录操作:
mkdir()、makedirs()、listdir()、walk() - • shutil 模块:
copy()、copy2()、copytree()、rmtree()
最佳实践
- • 始终使用 with 语句:自动关闭文件,异常安全
掌握文件操作,你的程序就能读写数据、处理配置、生成报告、记录日志。这是编写实用程序的基础技能。
至此,Python 零基础入门系列的六篇文章全部完成。你已经掌握了 Python 编程的核心基础知识:
下一步,建议你多动手实践,尝试完成每篇的练习题,或者找一些小项目练手。编程是一项实践技能,只有不断练习才能真正掌握。
祝你学习愉快,Python 之路越走越远!
本文首发于微信公众号「羽您码上聊」,欢迎关注获取更多技术内容。