七:文件读写——处理文本、JSON 和 CSV

一、回顾与本篇目标

上一篇我们学了模块和包,你的代码可以拆分到多个文件里了。上一篇我们学的模块和包,主要是为了把代码组织好——函数放到不同的 .py 文件里,通过 import 引入使用。这些都发生在程序运行期间,数据存在变量里。程序运行期间,数据存在变量和容器里。一旦程序退出,所有数据都没了。

数据需要持久化。持久化的一种方式是用数据库,另一种更简单的方式是把数据写到硬盘上的文件里。Python 对文件读写的支持非常简洁,几行代码就能完成。如果你之前跟着《后端零基础入门》学过 Node.js 的 fs 模块,这一篇的很多概念你会觉得似曾相识——读写文件、JSON 序列化、CSV 处理,核心思想都一样,只是语法不同。

在很多实际场景里,文件读写比数据库更实用:处理日志文件、读取配置文件、导出 CSV 报表、把爬虫抓到的数据存成 JSON——这些都是在写文件。

本篇的目标:

  1. 学会用 open() 打开和关闭文件
  2. 掌握读文件的三种方式:read()readline()readlines()
  3. 学会写文件和追加文件
  4. 理解 with 语句的作用——自动关闭文件
  5. 学会 JSON 的序列化与反序列化
  6. 学会 CSV 的读写

二、打开和关闭文件:open() 和 close()

Python 用内置函数 open() 来打开文件。它返回一个文件对象,通过这个对象你可以读写文件的内容。

基本语法

文件对象 = open('文件路径', '模式', encoding='编码')
  • 文件路径:文件在硬盘上的位置。可以是相对路径('data.txt')或绝对路径('C:/Users/xxx/data.txt')。
  • 模式:告诉 Python 你要做什么——读、写、追加等。
  • 编码:处理中文时一般指定 encoding='utf-8',否则在 Windows 上可能出现乱码。

文件打开模式

模式 含义 文件不存在时 文件存在时
'r' 只读(默认) 报错 从开头读取
'w' 写入(覆盖) 创建新文件 清空原内容
'a' 追加 创建新文件 在末尾追加
'r+' 读写 报错 从开头读写
'rb' 二进制读 报错 从开头读取字节
'wb' 二进制写 创建新文件 清空原内容

最常用的三个模式'r' 读、'w' 写、'a' 追加。'w' 模式需要特别小心——它会清空文件原有内容。如果只是想添加内容,用 'a'

关闭文件:close()

文件用完后必须关闭。如果不关,可能会导致:写入的内容没有真正保存到硬盘、占用系统文件句柄(打开太多文件会报错)。

# 打开文件
file = open('data.txt', 'r', encoding='utf-8')

# 读取内容(后面会讲)
content = file.read()
print(content)

# 关闭文件
file.close()

但每次都手动 close() 很麻烦,而且如果在 open()close() 之间程序抛出了异常,close() 可能根本不会被执行。下一节会介绍更好的写法。

三、读取文件的三种方式

假设我们有一个文件 data.txt,内容如下:

第一行:你好,世界
第二行:Python 文件读写
第三行:坚持学习

方式一:read()——一次性读取整个文件

file = open('data.txt', 'r', encoding='utf-8')
content = file.read()       # 把整个文件内容读成一个字符串
file.close()

print(content)
# 输出:
# 第一行:你好,世界
# 第二行:Python 文件读写
# 第三行:坚持学习

read() 可以传入一个数字参数,表示读取多少个字符:

file = open('data.txt', 'r', encoding='utf-8')
partial = file.read(10)     # 只读前 10 个字符
print(partial)              # '第一行:你好,世界\n第'
file.close()

方式二:readline()——一次读取一行

file = open('data.txt', 'r', encoding='utf-8')
line1 = file.readline()     # 读取第一行(包括行尾的换行符)
line2 = file.readline()     # 读取第二行
file.close()

print(repr(line1))  # '第一行:你好,世界\n'
print(repr(line2))  # '第二行:Python 文件读写\n'

readline() 返回的字符串末尾包含 \n(换行符)。如果你不想要换行符,可以用 strip() 去掉。

方式三:readlines()——一次读取所有行,返回列表

file = open('data.txt', 'r', encoding='utf-8')
lines = file.readlines()    # 返回列表,每个元素是一行
file.close()

print(lines)
# ['第一行:你好,世界\n', '第二行:Python 文件读写\n', '第三行:坚持学习']

三种方式的适用场景

  • 文件很小:用 read() 一次性读取,简单省事。
  • 需要逐行处理:用 for 循环直接遍历文件对象(最推荐的方式,见下一节)。
  • 需要随机访问某一行:用 readlines() 拿到所有行的列表,然后按索引访问。

最好的读文件方式:用 for 循环遍历文件对象

Python 的文件对象本身是可迭代的——你可以直接对它做 for 循环,一行一行地读,不需要先把整个文件加载到内存

file = open('data.txt', 'r', encoding='utf-8')
for line in file:
    line = line.strip()      # 去掉换行符和空白
    print(f'读到一行:{line}')
file.close()

这是处理大文件的最佳方式——每次只在内存中保存一行,不会撑爆内存。

四、with 语句:自动关闭文件的优雅写法

前面所有的例子都用了 open() + close()。但有一种更好的写法——with 语句。它会在代码块执行完毕后自动关闭文件,即使代码块内部抛出了异常也能保证关闭。

# 推荐写法:用 with,不需要手动 close()
with open('data.txt', 'r', encoding='utf-8') as file:
    content = file.read()
    print(content)
# 出了 with 块,文件自动关闭了

# 等价于之前的写法,但更安全

with 语句的本质 是 Python 的上下文管理器协议。你不需要理解底层原理,只需要记住:读写文件永远用 with 语句。后面的所有示例都会用这个写法。

五、写入文件

覆盖写入:’w’ 模式

# 写入新文件(如果文件不存在会自动创建)
with open('output.txt', 'w', encoding='utf-8') as file:
    file.write('这是第一行\n')
    file.write('这是第二行\n')
    file.write('这是第三行\n')

print('写入完成!')

执行后,output.txt 的内容就是你写入的三行。注意write() 不会自动加换行符,你需要手动加 \n。如果你再次用 'w' 模式打开这个文件并写入新内容,原来的内容会被清空

追加写入:’a’ 模式

# 在文件末尾追加内容,不覆盖原有内容
with open('output.txt', 'a', encoding='utf-8') as file:
    file.write('这是追加的一行\n')
    file.write('这也是追加的一行\n')

写入多行:writelines()

lines = ['第一行\n', '第二行\n', '第三行\n']
with open('output.txt', 'w', encoding='utf-8') as file:
    file.writelines(lines)

writelines() 接收一个字符串列表,把每个元素写入文件。和 write() 一样,它不会自动加换行符,你需要自己确保每个字符串末尾有 \n

六、JSON 文件的读写

JSON 是一种通用的数据交换格式。Python 的 json 模块提供了四个核心函数:

函数 方向 作用
json.dumps(obj) Python → JSON 字符串 把 Python 对象转成 JSON 字符串
json.loads(str) JSON 字符串 → Python 把 JSON 字符串转成 Python 对象
json.dump(obj, file) Python → JSON 文件 把 Python 对象写入文件
json.load(file) JSON 文件 → Python 从文件读取并转成 Python 对象

记忆技巧:带 s 的是处理字符串(string),不带 s 的是处理文件。这跟 JavaScript 的 JSON.parse()JSON.stringify() 类似。

把 Python 对象写入 JSON 文件

import json

# Python 数据
users = [
    {'name': '张三', 'age': 28, 'city': '上海'},
    {'name': '李四', 'age': 22, 'city': '北京'},
    {'name': '王五', 'age': 25, 'city': '广州'}
]

# 写入 JSON 文件
with open('users.json', 'w', encoding='utf-8') as file:
    json.dump(users, file, ensure_ascii=False, indent=2)

print('JSON 文件写入完成')

参数解释:

  • ensure_ascii=False:不把中文转成 \uXXXX 的形式,直接保留中文字符。不加这个参数的话,中文会变成乱码似的转义序列。
  • indent=2:缩进 2 个空格,让 JSON 文件格式化好读。不加的话所有内容会挤在一行。

生成的 users.json 文件:

[
  {
    "name": "张三",
    "age": 28,
    "city": "上海"
  },
  {
    "name": "李四",
    "age": 22,
    "city": "北京"
  },
  {
    "name": "王五",
    "age": 25,
    "city": "广州"
  }
]

从 JSON 文件读取数据

import json

with open('users.json', 'r', encoding='utf-8') as file:
    users = json.load(file)

# users 现在是一个 Python 列表,包含三个字典
for user in users:
    print(f'{user["name"]},{user["age"]}岁,来自{user["city"]}')

处理字符串格式的 JSON

import json

# JSON 字符串
json_str = '{"name": "张三", "age": 28}'

# 解析成 Python 对象
data = json.loads(json_str)
print(data['name'])  # 张三

# 反过来:Python 对象转 JSON 字符串
json_str = json.dumps(data, ensure_ascii=False)
print(json_str)      # {"name": "张三", "age": 28}

七、CSV 文件的读写

CSV(逗号分隔值)是一种表格数据格式,广泛用于 Excel 导出、数据分析等场景。Python 内置了 csv 模块。

写入 CSV 文件

import csv

# 数据:每行是一个列表
data = [
    ['姓名', '年龄', '城市'],      # 表头
    ['张三', 28, '上海'],
    ['李四', 22, '北京'],
    ['王五', 25, '广州']
]

with open('users.csv', 'w', encoding='utf-8', newline='') as file:
    writer = csv.writer(file)
    # writerows 一次性写入多行
    writer.writerows(data)

print('CSV 文件写入完成')

newline='' 这个参数在 Windows 上很重要。如果不加,写入的每一行之间会多出一个空行。

读取 CSV 文件

import csv

with open('users.csv', 'r', encoding='utf-8') as file:
    reader = csv.reader(file)
    for row in reader:
        print(f'姓名:{row[0]},年龄:{row[1]},城市:{row[2]}')

用字典形式读写 CSV

如果 CSV 有表头,用 DictReaderDictWriter 会更直观:

import csv

# 用字典形式写入
data = [
    {'姓名': '张三', '年龄': 28, '城市': '上海'},
    {'姓名': '李四', '年龄': 22, '城市': '北京'},
]

with open('users_dict.csv', 'w', encoding='utf-8', newline='') as file:
    fieldnames = ['姓名', '年龄', '城市']
    writer = csv.DictWriter(file, fieldnames=fieldnames)
    writer.writeheader()           # 写入表头
    writer.writerows(data)         # 写入数据行

# 用字典形式读取
with open('users_dict.csv', 'r', encoding='utf-8') as file:
    reader = csv.DictReader(file)
    for row in reader:
        # row 是一个字典,键是表头
        print(f"{row['姓名']},{row['年龄']}岁")

DictReader 把每一行数据解析成字典,键是表头列名。这样访问字段不用记列索引,代码可读性更好。

八、文件与目录操作:os 模块

除了读写文件内容,还需要对文件本身进行操作——检查是否存在、重命名、删除等。这些功能在 os 模块中。

import os

# 检查文件是否存在
print(os.path.exists('data.txt'))     # True 或 False

# 检查是否是文件/目录
print(os.path.isfile('data.txt'))     # True
print(os.path.isdir('my_folder'))     # True

# 获取文件大小(字节)
print(os.path.getsize('data.txt'))

# 重命名文件
os.rename('old_name.txt', 'new_name.txt')

# 删除文件
os.remove('temp.txt')

# 创建目录
os.makedirs('new_folder/sub_folder', exist_ok=True)
# exist_ok=True 表示如果目录已存在不报错

# 列出目录中的文件
files = os.listdir('.')     # '.' 表示当前目录
for f in files:
    print(f)

# 拼接路径(跨平台,推荐用这个而不是字符串拼接)
path = os.path.join('folder', 'subfolder', 'file.txt')
print(path)  # Windows: folder\subfolder\file.txt  Mac: folder/subfolder/file.txt

九、和 Node.js 文件读写的对比

如果你之前跟着后端入门系列学过 Node.js 的 fs 模块,这张对比表能帮你快速迁移:

操作 Node.js Python
读文件(同步) fs.readFileSync(path, 'utf-8') open(path, 'r').read()
读文件(异步) fs.readFile(path, callback) Python 一般是同步,异步需 aiofiles
写文件 fs.writeFileSync(path, data) open(path, 'w').write(data)
JSON 解析 JSON.parse(str) / JSON.stringify(obj) json.loads(str) / json.dumps(obj)
路径拼接 path.join('a', 'b') os.path.join('a', 'b')
检查文件存在 fs.existsSync(path) os.path.exists(path)

两者的差异主要在:Node.js 强调异步非阻塞,所以有大量回调或 Promise 风格的 API;Python 默认是同步的,代码更直观,但处理大文件或高并发时需要额外工具。

十、综合演示:一个简单的笔记管理器

下面这段代码综合运用了本篇学到的文件读写知识,做一个能用 JSON 文件持久化存储的笔记管理器:

import json
import os
from datetime import datetime

NOTES_FILE = 'notes.json'

def load_notes():
    """从 JSON 文件加载笔记列表"""
    if not os.path.exists(NOTES_FILE):
        return []
    with open(NOTES_FILE, 'r', encoding='utf-8') as file:
        return json.load(file)

def save_notes(notes):
    """把笔记列表保存到 JSON 文件"""
    with open(NOTES_FILE, 'w', encoding='utf-8') as file:
        json.dump(notes, file, ensure_ascii=False, indent=2)

def add_note(title, content):
    """添加一条新笔记"""
    notes = load_notes()
    note = {
        'id': len(notes) + 1,
        'title': title,
        'content': content,
        'created_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    }
    notes.append(note)
    save_notes(notes)
    print(f'笔记"{title}"添加成功')

def list_notes():
    """列出所有笔记"""
    notes = load_notes()
    if not notes:
        print('暂无笔记')
        return
    print(f'\n===== 我的笔记(共{len(notes)}条)=====')
    for note in notes:
        print(f'[{note["id"]}] {note["title"]} - {note["created_at"]}')

def view_note(note_id):
    """查看一条笔记的详细内容"""
    notes = load_notes()
    for note in notes:
        if note['id'] == note_id:
            print(f'\n===== {note["title"]} =====')
            print(f'时间:{note["created_at"]}')
            print(f'内容:{note["content"]}')
            return
    print(f'笔记 {note_id} 不存在')

def delete_note(note_id):
    """删除一条笔记"""
    notes = load_notes()
    new_notes = [note for note in notes if note['id'] != note_id]
    if len(new_notes) == len(notes):
        print(f'笔记 {note_id} 不存在')
        return
    save_notes(new_notes)
    print(f'笔记 {note_id} 已删除')

# 测试
if __name__ == '__main__':
    add_note('学习 Python', '今天学习了文件读写和 JSON 处理')
    add_note('购物清单', '苹果、香蕉、牛奶、面包')
    list_notes()
    view_note(1)
    delete_note(2)
    list_notes()

代码设计要点:

  • load_notes()save_notes() 封装了文件读写的细节,其他函数不需要关心数据是怎么存的。
  • 如果文件不存在(第一次运行),load_notes() 返回空列表,不会报错。
  • datetime.now().strftime() 生成格式化的时间字符串,记录笔记的创建时间。
  • 删除操作用了列表推导式过滤掉要删除的笔记,这是 Python 中处理列表的高频写法。

十一、本篇动手练习

练习 1:日志记录器

新建 practice7-1.py,写一个函数 log_message(message),每次调用时把当前时间和消息追加写入 log.txt 文件。格式:[2025-01-15 10:30:00] 用户登录成功

练习 2:学生成绩管理(JSON 版)

新建 practice7-2.py,用 JSON 文件存储学生成绩数据。实现添加学生成绩、查看所有成绩、按姓名查找成绩。每次操作后自动保存到 JSON 文件。

练习 3:CSV 数据转换

新建 practice7-3.py,读取上一篇练习中的 users.csv,把其中的数据读取出来,转成 JSON 格式写入 users.json

练习 4:文件搜索工具

新建 practice7-4.py,写一个函数 search_files(directory, keyword),搜索指定目录下所有 .txt 文件,找出包含关键词的行,输出文件名、行号和内容。

十二、本篇小结

这一篇你学会了 Python 中文件读写和数据处理的核心技能:

  • open()with 语句with 语句自动关闭文件,是读写文件的最佳实践。常用模式:'r' 读、'w' 覆盖写、'a' 追加。
  • 读文件的四种方式read() 一次全读、readline() 逐行读、readlines() 读成列表、for 循环直接遍历(最推荐,不占内存)。
  • 写文件write() 写字符串、writelines() 写字符串列表。换行符需要手动加 \n
  • JSON 处理json.dump() 写文件、json.load() 读文件、json.dumps() 转字符串、json.loads() 解析字符串。ensure_ascii=False 保留中文,indent=2 格式化输出。
  • CSV 处理csv.reader / csv.writer 处理列表格式,csv.DictReader / csv.DictWriter 处理字典格式。
  • 文件和目录操作os.path.exists() 检查是否存在、os.path.join() 拼接路径、os.listdir() 列出文件。

文件读写是 Python 日常开发中最常用的技能之一——日志记录、配置管理、数据导出、爬虫存储,处处都能用到。下一篇,我们正式进入 Python 最令人生畏但也最强大的领域——爬虫,用 requests 和 BeautifulSoup 抓取网页数据。

下一篇预告

下一篇——《爬虫入门——用 requests 抓取网页数据》:安装 requests 和 BeautifulSoup、发送 GET 和 POST 请求、解析 HTML 提取数据、处理翻页、设置请求头伪装浏览器。你会写出第一个能自动抓取网页内容的 Python 脚本。

Python 零基础入门,每周更新。

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容