> 技术文档 > 7.文件操作:让程序读写文件 [特殊字符]

7.文件操作:让程序读写文件 [特殊字符]


文件操作:让程序读写文件 📁

🎯 前言:程序也需要记事本

想象一下,如果你的大脑无法记住任何信息,每次重启(睡一觉)后就忘记了昨天学的Python,那该多可怕!程序也是如此,如果不能读写文件,那就是个\"金鱼记忆\"的程序——运行时热热闹闹,一关闭就什么都不记得了。

今天我们就来学习如何让程序拥有\"记忆力\",学会读写文件。这就像给程序配备了一个超级记事本,不仅能记录信息,还能跨时空传递数据!

📚 目录

  • 为什么需要文件操作
  • 文件的打开与关闭
  • 读取文件的各种姿势
  • 写入文件的艺术
  • 文件路径的奥秘
  • CSV文件处理
  • JSON文件操作
  • 文件操作的最佳实践
  • 实战项目:个人日记管理系统
  • 常见错误与解决方案

🧠 为什么需要文件操作?

生活中的文件操作

就像我们需要:

  • 📝 写日记记录生活点滴
  • 📊 保存Excel表格数据
  • 📷 存储照片和视频
  • 📄 备份重要文档

程序也需要:

  • 💾 保存用户数据
  • 📈 记录运行日志
  • ⚙️ 读取配置文件
  • 🔄 导入导出数据

一个简单的例子

# 没有文件操作的程序name = input(\"请输入你的名字:\")print(f\"你好,{name}!\")# 程序结束后,name就消失了... 👻# 有文件操作的程序name = input(\"请输入你的名字:\")with open(\"user_info.txt\", \"w\") as file: file.write(name)print(f\"你好,{name}!你的名字已经被我记住了!\")# 下次运行时还能读取到这个名字 🎉

📖 文件的打开与关闭

基本语法:open()函数

# 基本语法file = open(\"文件名\", \"模式\")# 使用文件file.close() # 别忘了关闭!

文件模式大全

模式 说明 比喻 \'r\' 只读模式 📖 只能看书,不能写字 \'w\' 写入模式 ✏️ 重新写作,之前的内容会被清空 \'a\' 追加模式 📝 在文章末尾继续写 \'r+\' 读写模式 🖊️ 既能读又能写 \'x\' 独占创建 🆕 只能创建新文件,文件存在就报错

编码方式

# 指定编码(重要!)file = open(\"中文文件.txt\", \"r\", encoding=\"utf-8\")

with语句:自动管家

# 传统方式(容易忘记关闭)file = open(\"test.txt\", \"r\")content = file.read()file.close() # 经常忘记这一行!# with语句(推荐)with open(\"test.txt\", \"r\") as file: content = file.read()# 文件会自动关闭,就像有个贴心的管家 🤵

📚 读取文件的各种姿势

1. 一口气读完:read()

# 创建一个测试文件with open(\"story.txt\", \"w\", encoding=\"utf-8\") as file: file.write(\"从前有座山\\n山里有座庙\\n庙里有个老和尚\\n在给小和尚讲故事\")# 一次性读取所有内容with open(\"story.txt\", \"r\", encoding=\"utf-8\") as file: content = file.read() print(content) print(f\"文件大小:{len(content)} 个字符\")

2. 一行一行读:readline()

# 逐行读取with open(\"story.txt\", \"r\", encoding=\"utf-8\") as file: line1 = file.readline() line2 = file.readline() print(f\"第一行:{line1.strip()}\") print(f\"第二行:{line2.strip()}\")

3. 读取所有行:readlines()

# 读取所有行到列表中with open(\"story.txt\", \"r\", encoding=\"utf-8\") as file: lines = file.readlines() for i, line in enumerate(lines, 1): print(f\"第{i}行:{line.strip()}\")

4. 优雅遍历:直接迭代

# 最优雅的方式with open(\"story.txt\", \"r\", encoding=\"utf-8\") as file: for line_num, line in enumerate(file, 1): print(f\"第{line_num}行:{line.strip()}\")

5. 读取指定字符数

with open(\"story.txt\", \"r\", encoding=\"utf-8\") as file: first_10_chars = file.read(10) print(f\"前10个字符:{first_10_chars}\")

✍️ 写入文件的艺术

1. 覆盖写入:write()

# 创建购物清单shopping_list = [\"苹果\", \"香蕉\", \"牛奶\", \"面包\"]with open(\"shopping_list.txt\", \"w\", encoding=\"utf-8\") as file: for item in shopping_list: file.write(f\"- {item}\\n\") print(\"购物清单已保存!\")

2. 追加写入:append模式

# 添加新商品到清单new_items = [\"鸡蛋\", \"酸奶\"]with open(\"shopping_list.txt\", \"a\", encoding=\"utf-8\") as file: file.write(\"\\n--- 新添加的商品 ---\\n\") for item in new_items: file.write(f\"- {item}\\n\") print(\"新商品已添加到购物清单!\")

3. 写入多行:writelines()

# 写入多行数据poems = [ \"床前明月光\\n\", \"疑是地上霜\\n\", \"举头望明月\\n\", \"低头思故乡\\n\"]with open(\"poem.txt\", \"w\", encoding=\"utf-8\") as file: file.writelines(poems)

4. 格式化写入

# 写入格式化数据students = [ {\"name\": \"小明\", \"age\": 20, \"score\": 95}, {\"name\": \"小红\", \"age\": 19, \"score\": 87}, {\"name\": \"小刚\", \"age\": 21, \"score\": 92}]with open(\"students.txt\", \"w\", encoding=\"utf-8\") as file: file.write(\"学生成绩单\\n\") file.write(\"=\" * 30 + \"\\n\") for student in students: file.write(f\"姓名:{student[\'name\']:4} 年龄:{student[\'age\']:2} 分数:{student[\'score\']:3}\\n\")

🗂️ 文件路径的奥秘

绝对路径 vs 相对路径

import os# 绝对路径(从根目录开始)abs_path = r\"C:\\Users\\YourName\\Documents\\data.txt\" # Windowsabs_path = \"/home/username/documents/data.txt\" # Linux/Mac# 相对路径(从当前目录开始)rel_path = \"data.txt\"  # 当前目录rel_path = \"folder/data.txt\" # 子目录rel_path = \"../data.txt\" # 上级目录# 获取当前工作目录current_dir = os.getcwd()print(f\"当前目录:{current_dir}\")

路径操作技巧

import os# 拼接路径(推荐方式)data_dir = \"data\"filename = \"test.txt\"full_path = os.path.join(data_dir, filename)print(f\"完整路径:{full_path}\")# 检查路径是否存在if os.path.exists(full_path): print(\"文件存在\")else: print(\"文件不存在\") # 创建目录if not os.path.exists(data_dir): os.makedirs(data_dir) print(f\"已创建目录:{data_dir}\")

使用pathlib(Python 3.4+推荐)

from pathlib import Path# 创建路径对象data_path = Path(\"data\")file_path = data_path / \"test.txt\"# 创建目录data_path.mkdir(exist_ok=True)# 写入文件file_path.write_text(\"Hello, pathlib!\", encoding=\"utf-8\")# 读取文件content = file_path.read_text(encoding=\"utf-8\")print(content)

📊 CSV文件处理

什么是CSV?

CSV(Comma-Separated Values)就像Excel的简化版,用逗号分隔数据:

姓名,年龄,城市张三,25,北京李四,30,上海王五,28,广州

手动处理CSV

# 创建CSV文件csv_data = [ [\"姓名\", \"年龄\", \"城市\", \"薪资\"], [\"张三\", \"25\", \"北京\", \"8000\"], [\"李四\", \"30\", \"上海\", \"12000\"], [\"王五\", \"28\", \"广州\", \"10000\"]]with open(\"employees.csv\", \"w\", encoding=\"utf-8\") as file: for row in csv_data: file.write(\",\".join(row) + \"\\n\")# 读取CSV文件with open(\"employees.csv\", \"r\", encoding=\"utf-8\") as file: for line_num, line in enumerate(file): data = line.strip().split(\",\") if line_num == 0: print(f\"表头:{data}\") else: print(f\"员工{line_num}:姓名={data[0]}, 年龄={data[1]}, 城市={data[2]}, 薪资={data[3]}\")

使用csv模块(推荐)

import csv# 写入CSVemployees = [ [\"姓名\", \"年龄\", \"城市\", \"薪资\"], [\"张三\", 25, \"北京\", 8000], [\"李四\", 30, \"上海\", 12000], [\"王五\", 28, \"广州\", 10000]]with open(\"employees_v2.csv\", \"w\", newline=\"\", encoding=\"utf-8\") as file: writer = csv.writer(file) writer.writerows(employees)# 读取CSVwith open(\"employees_v2.csv\", \"r\", encoding=\"utf-8\") as file: reader = csv.reader(file) for row_num, row in enumerate(reader): if row_num == 0: print(f\"表头:{row}\") else: print(f\"员工数据:{row}\")

字典方式处理CSV

import csv# 写入CSV(字典方式)fieldnames = [\"姓名\", \"年龄\", \"城市\", \"薪资\"]employees = [ {\"姓名\": \"张三\", \"年龄\": 25, \"城市\": \"北京\", \"薪资\": 8000}, {\"姓名\": \"李四\", \"年龄\": 30, \"城市\": \"上海\", \"薪资\": 12000}, {\"姓名\": \"王五\", \"年龄\": 28, \"城市\": \"广州\", \"薪资\": 10000}]with open(\"employees_dict.csv\", \"w\", newline=\"\", encoding=\"utf-8\") as file: writer = csv.DictWriter(file, fieldnames=fieldnames) writer.writeheader() # 写入表头 writer.writerows(employees)# 读取CSV(字典方式)with open(\"employees_dict.csv\", \"r\", encoding=\"utf-8\") as file: reader = csv.DictReader(file) for row in reader: print(f\"员工:{row[\'姓名\']}, 年龄:{row[\'年龄\']}, 薪资:{row[\'薪资\']}\")

🔧 JSON文件操作

什么是JSON?

JSON(JavaScript Object Notation)就像Python的字典,但是以文本形式保存:

{ \"name\": \"张三\", \"age\": 25, \"hobbies\": [\"读书\", \"游泳\", \"编程\"], \"address\": { \"city\": \"北京\", \"district\": \"朝阳区\" }}

JSON基本操作

import json# Python对象person = { \"name\": \"张三\", \"age\": 25, \"hobbies\": [\"读书\", \"游泳\", \"编程\"], \"address\": { \"city\": \"北京\", \"district\": \"朝阳区\" }, \"is_student\": False}# 写入JSON文件with open(\"person.json\", \"w\", encoding=\"utf-8\") as file: json.dump(person, file, ensure_ascii=False, indent=2)# 读取JSON文件with open(\"person.json\", \"r\", encoding=\"utf-8\") as file: loaded_person = json.load(file) print(f\"姓名:{loaded_person[\'name\']}\") print(f\"爱好:{\', \'.join(loaded_person[\'hobbies\'])}\") print(f\"地址:{loaded_person[\'address\'][\'city\']}{loaded_person[\'address\'][\'district\']}\")

JSON字符串操作

import json# 转换为JSON字符串json_string = json.dumps(person, ensure_ascii=False, indent=2)print(\"JSON字符串:\")print(json_string)# 从JSON字符串解析parsed_person = json.loads(json_string)print(f\"解析后的姓名:{parsed_person[\'name\']}\")

处理复杂JSON数据

import json# 学生管理系统数据school_data = { \"school_name\": \"阳光中学\", \"students\": [ { \"id\": 1, \"name\": \"小明\", \"grades\": { \"math\": 95, \"english\": 87, \"chinese\": 92 } }, { \"id\": 2, \"name\": \"小红\", \"grades\": { \"math\": 88, \"english\": 94, \"chinese\": 89 } } ]}# 保存到文件with open(\"school_data.json\", \"w\", encoding=\"utf-8\") as file: json.dump(school_data, file, ensure_ascii=False, indent=2)# 读取并分析数据with open(\"school_data.json\", \"r\", encoding=\"utf-8\") as file: data = json.load(file) print(f\"学校:{data[\'school_name\']}\") print(\"学生成绩:\") for student in data[\'students\']: name = student[\'name\'] grades = student[\'grades\'] avg_score = sum(grades.values()) / len(grades) print(f\" {name}: 平均分 {avg_score:.1f}\")

🎯 文件操作的最佳实践

1. 异常处理

def safe_read_file(filename): \"\"\"安全地读取文件\"\"\" try: with open(filename, \"r\", encoding=\"utf-8\") as file: return file.read() except FileNotFoundError: print(f\"文件 {filename} 不存在\") return None except PermissionError: print(f\"没有权限读取文件 {filename}\") return None except Exception as e: print(f\"读取文件时发生错误:{e}\") return None# 使用示例content = safe_read_file(\"nonexistent.txt\")if content: print(content)

2. 文件备份

import shutilfrom datetime import datetimedef backup_file(original_file): \"\"\"创建文件备份\"\"\" if not os.path.exists(original_file): print(f\"原文件 {original_file} 不存在\") return False # 生成备份文件名 timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S\") backup_name = f\"{original_file}.backup_{timestamp}\" try: shutil.copy2(original_file, backup_name) print(f\"备份已创建:{backup_name}\") return True except Exception as e: print(f\"备份失败:{e}\") return False# 使用示例backup_file(\"important_data.txt\")

3. 文件监控

import osfrom datetime import datetimedef get_file_info(filename): \"\"\"获取文件信息\"\"\" if not os.path.exists(filename): return None stat = os.stat(filename) return { \"size\": stat.st_size, \"modified\": datetime.fromtimestamp(stat.st_mtime), \"created\": datetime.fromtimestamp(stat.st_ctime) }# 使用示例info = get_file_info(\"test.txt\")if info: print(f\"文件大小:{info[\'size\']} 字节\") print(f\"修改时间:{info[\'modified\']}\")

🚀 实战项目:个人日记管理系统

让我们创建一个完整的个人日记管理系统:

import jsonimport osfrom datetime import datetimefrom pathlib import Pathclass DiaryManager: def __init__(self, diary_dir=\"diary\"): self.diary_dir = Path(diary_dir) self.diary_dir.mkdir(exist_ok=True) self.config_file = self.diary_dir / \"config.json\" self.load_config() def load_config(self): \"\"\"加载配置文件\"\"\" if self.config_file.exists(): with open(self.config_file, \"r\", encoding=\"utf-8\") as file: self.config = json.load(file) else: self.config = { \"author\": \"匿名用户\", \"created_date\": datetime.now().strftime(\"%Y-%m-%d\"), \"diary_count\": 0 } self.save_config() def save_config(self): \"\"\"保存配置文件\"\"\" with open(self.config_file, \"w\", encoding=\"utf-8\") as file: json.dump(self.config, file, ensure_ascii=False, indent=2) def write_diary(self, content, title=None): \"\"\"写日记\"\"\" date_str = datetime.now().strftime(\"%Y-%m-%d\") time_str = datetime.now().strftime(\"%H:%M:%S\") # 生成文件名 diary_file = self.diary_dir / f\"{date_str}.txt\" # 如果没有标题,自动生成 if not title: title = f\"{date_str} 的日记\" # 写入日记 with open(diary_file, \"a\", encoding=\"utf-8\") as file: file.write(f\"\\n{\'=\'*50}\\n\") file.write(f\"标题:{title}\\n\") file.write(f\"时间:{date_str} {time_str}\\n\") file.write(f\"作者:{self.config[\'author\']}\\n\") file.write(f\"{\'=\'*50}\\n\") file.write(content) file.write(f\"\\n{\'=\'*50}\\n\") # 更新配置 self.config[\"diary_count\"] += 1 self.save_config() print(f\"日记已保存到 {diary_file}\") def read_diary(self, date_str): \"\"\"读取指定日期的日记\"\"\" diary_file = self.diary_dir / f\"{date_str}.txt\" if not diary_file.exists(): print(f\"没有找到 {date_str} 的日记\") return None with open(diary_file, \"r\", encoding=\"utf-8\") as file: content = file.read() print(content) return content def list_diaries(self): \"\"\"列出所有日记\"\"\" diary_files = list(self.diary_dir.glob(\"*.txt\")) diary_files.sort() print(f\"\\n共有 {len(diary_files)} 篇日记:\") for diary_file in diary_files: date_str = diary_file.stem print(f\"- {date_str}\") return [f.stem for f in diary_files] def search_diary(self, keyword): \"\"\"搜索日记内容\"\"\" results = [] diary_files = list(self.diary_dir.glob(\"*.txt\")) for diary_file in diary_files: with open(diary_file, \"r\", encoding=\"utf-8\") as file: content = file.read() if keyword.lower() in content.lower():  results.append(diary_file.stem) if results: print(f\"\\n在以下日记中找到关键词 \'{keyword}\':\") for date_str in results: print(f\"- {date_str}\") else: print(f\"没有找到包含关键词 \'{keyword}\' 的日记\") return results def export_diary(self, output_file=\"all_diaries.txt\"): \"\"\"导出所有日记到一个文件\"\"\" diary_files = list(self.diary_dir.glob(\"*.txt\")) diary_files.sort() with open(output_file, \"w\", encoding=\"utf-8\") as output: output.write(f\"《{self.config[\'author\']}的日记集》\\n\") output.write(f\"导出时间:{datetime.now().strftime(\'%Y-%m-%d %H:%M:%S\')}\\n\") output.write(f\"共包含 {len(diary_files)} 篇日记\\n\") output.write(\"=\"*80 + \"\\n\\n\") for diary_file in diary_files: with open(diary_file, \"r\", encoding=\"utf-8\") as file:  content = file.read()  output.write(content)  output.write(\"\\n\\n\") print(f\"所有日记已导出到 {output_file}\")# 使用示例def main(): diary = DiaryManager() while True: print(\"\\n=== 个人日记管理系统 ===\") print(\"1. 写日记\") print(\"2. 读日记\") print(\"3. 列出所有日记\") print(\"4. 搜索日记\") print(\"5. 导出日记\") print(\"6. 退出\") choice = input(\"请选择操作(1-6):\") if choice == \"1\": title = input(\"请输入日记标题(回车跳过):\") print(\"请输入日记内容(输入 \'END\' 结束):\") content_lines = [] while True: line = input() if line.strip() == \"END\":  break content_lines.append(line) content = \"\\n\".join(content_lines) diary.write_diary(content, title if title else None) elif choice == \"2\": date_str = input(\"请输入日期(格式:YYYY-MM-DD):\") diary.read_diary(date_str) elif choice == \"3\": diary.list_diaries() elif choice == \"4\": keyword = input(\"请输入搜索关键词:\") diary.search_diary(keyword) elif choice == \"5\": output_file = input(\"请输入导出文件名(默认:all_diaries.txt):\") if not output_file: output_file = \"all_diaries.txt\" diary.export_diary(output_file) elif choice == \"6\": print(\"再见!\") break else: print(\"无效的选择,请重试\")if __name__ == \"__main__\": main()

🚨 常见错误与解决方案

1. 编码问题

# 错误示例try: with open(\"中文.txt\", \"r\") as file: # 没有指定编码 content = file.read()except UnicodeDecodeError as e: print(f\"编码错误:{e}\")# 正确做法with open(\"中文.txt\", \"r\", encoding=\"utf-8\") as file: content = file.read()

2. 文件路径问题

# 错误示例filename = \"data\\\\test.txt\" # 硬编码路径分隔符# 正确做法import osfilename = os.path.join(\"data\", \"test.txt\") # 跨平台兼容# 或使用pathlibfrom pathlib import Pathfilename = Path(\"data\") / \"test.txt\"

3. 忘记关闭文件

# 错误示例file = open(\"test.txt\", \"w\")file.write(\"Hello\")# 忘记关闭文件!# 正确做法with open(\"test.txt\", \"w\") as file: file.write(\"Hello\")# 自动关闭

4. 文件权限问题

def safe_write_file(filename, content): \"\"\"安全地写入文件\"\"\" try: with open(filename, \"w\", encoding=\"utf-8\") as file: file.write(content) return True except PermissionError: print(f\"没有权限写入文件 {filename}\") return False except Exception as e: print(f\"写入文件时发生错误:{e}\") return False

🔧 性能优化技巧

1. 大文件处理

def process_large_file(filename): \"\"\"处理大文件的高效方法\"\"\" with open(filename, \"r\", encoding=\"utf-8\") as file: for line_num, line in enumerate(file, 1): # 逐行处理,不会占用大量内存 process_line(line) # 每处理1000行显示进度 if line_num % 1000 == 0: print(f\"已处理 {line_num} 行\")def process_line(line): \"\"\"处理单行数据\"\"\" # 实际的处理逻辑 pass

2. 缓冲区优化

# 调整缓冲区大小with open(\"large_file.txt\", \"r\", buffering=8192) as file: content = file.read()

3. 批量操作

def batch_write_lines(filename, lines, batch_size=1000): \"\"\"批量写入行数据\"\"\" with open(filename, \"w\", encoding=\"utf-8\") as file: for i in range(0, len(lines), batch_size): batch = lines[i:i + batch_size] file.writelines(batch) file.flush() # 强制写入磁盘

📖 扩展阅读

相关模块推荐

  • pathlib:面向对象的文件路径处理
  • shutil:高级文件操作(复制、移动、删除)
  • tempfile:临时文件处理
  • pickle:Python对象序列化
  • configparser:配置文件处理

进阶主题

  • 文件锁定和并发访问
  • 内存映射文件处理
  • 压缩文件操作(zip、tar)
  • 二进制文件处理
  • 网络文件系统操作

🎬 下集预告

下一篇文章我们将学习异常处理:优雅地处理错误。想象一下,如果程序遇到错误就崩溃,那用户体验该多糟糕!我们将学习如何让程序像一个专业的服务员,即使遇到意外情况也能优雅地处理,给用户最好的体验。

我们将探讨:

  • 🚨 异常的本质和类型
  • 🛡️ try-except语句的各种用法
  • 🔄 自定义异常的创建
  • 📝 日志记录的最佳实践
  • 🎯 调试技巧和工具

📝 总结与思考题

核心要点总结

  1. 文件操作是程序的\"记忆力\":让程序能够持久化数据
  2. with语句是最佳实践:自动管理文件的打开和关闭
  3. 编码很重要:处理中文文件时要指定UTF-8编码
  4. 路径处理要跨平台:使用os.path.join()或pathlib
  5. 异常处理不能忘:文件操作容易出错,要做好异常处理

实践作业

  1. 基础练习:创建一个简单的记账本程序,能够记录收支并保存到文件
  2. 进阶练习:实现一个通讯录管理系统,支持增删改查和数据导出
  3. 挑战练习:编写一个日志分析工具,能够分析Web服务器日志文件

思考题

  1. 为什么推荐使用with语句而不是手动open/close?
  2. 处理大文件时,为什么要逐行读取而不是一次性读取?
  3. JSON和CSV各有什么优缺点?什么情况下选择哪种格式?
  4. 如何设计一个程序来监控文件的变化?

记住:文件操作就像给程序配备了一个超级大脑,不仅能思考,还能记忆! 🧠✨

掌握了文件操作,你就掌握了程序的\"记忆术\"。无论是保存用户数据、记录程序日志,还是处理各种格式的文件,你都能游刃有余。下一步,让我们学习如何让程序在面对错误时也能保持优雅!