Skip to content

Python 文件读写:与操作系统的"文件对话"

引言:从"自己搬"到"请搬家公司"

想象你要搬家:

  • 自己搬:一件一件扛,累个半死,还可能摔坏东西;
  • 请搬家公司(open() + with:打电话预约,工人上门打包、搬运、送到新家——你只需说"搬什么、搬到哪",全程不用动手。

Python 的文件读写就是"请搬家公司"——open() 打电话预约,with 确保工人干完活收拾干净,你只需处理"内容"。


一、文件读写的本质:请求操作系统

1.1 为什么不能直接操作磁盘?

现代操作系统不允许普通程序直接碰磁盘——就像你不能直接进银行金库,必须通过柜台办理。

你的程序 → open() → 操作系统 → 磁盘

         文件对象(文件描述符)

         read() / write()

生活化理解open() 是"取号排队",read()/write() 是"柜台办理业务",close() 是"办完走人"。

1.2 文件对象(file object)

open() 返回的文件对象是操作系统给你的"办事窗口"——所有读写都通过它。


二、读文件:从磁盘到内存

2.1 基本流程

python
# 1. 打开文件(取号)
f = open('/path/to/file.txt', 'r')   # 'r' = read

# 2. 读取内容(办理业务)
content = f.read()                   # 全部读到内存

# 3. 关闭文件(走人)
f.close()

文件不存在时

python
f = open('/path/to/notfound.txt', 'r')
# FileNotFoundError: [Errno 2] No such file or directory

2.2 必须关闭文件

python
f = open('/path/to/file.txt', 'r')
content = f.read()
# 如果这里出错,f.close() 不会执行!
f.close()

问题:文件对象占用操作系统资源,且系统同时打开的文件数有限——不关门,别人进不来

2.3 try...finally 保底

python
f = None
try:
    f = open('/path/to/file.txt', 'r')
    print(f.read())
finally:
    if f:
        f.close()   # 无论如何都关门

2.4 with 语句:自动关门(推荐)

python
with open('/path/to/file.txt', 'r') as f:
    print(f.read())
# 出 with 块自动 close,不用管

生活化理解with 是"智能门禁"——你办完业务出门,门自动关,不用记得锁门。


三、读取策略:根据文件大小选方法

方法适用场景类比
read()小文件,一次读完一杯水一口喝干
read(size)大文件,分块读大桶水分多次倒
readline()逐行读一行一行看
readlines()配置文件,全部行整本通讯录一次拿

3.1 大文件分块读

python
with open('/path/to/bigfile.txt', 'r') as f:
    while True:
        chunk = f.read(1024)   # 每次读 1KB
        if not chunk:
            break
        process(chunk)         # 处理这一块

生活化理解:10G 文件像一大桶水,read() 一口喝会呛死,read(1024) 分多次倒着喝。

3.2 逐行读(配置文件最方便)

python
with open('/path/to/config.txt', 'r') as f:
    for line in f.readlines():
        print(line.strip())   # strip() 去掉行尾的 '\n'

四、file-like Object:只要有 read() 就是"文件"

python
# 文件
with open('test.txt', 'r') as f:
    data = f.read()

# 内存中的 StringIO(也是 file-like)
from io import StringIO
f = StringIO('Hello, world!')
data = f.read()

核心思想鸭子类型——不需要继承特定类,只要有 read() 方法,就可以当文件用。

适用场景:网络流、内存缓冲、自定义数据源——接口统一,来源不限


五、二进制文件:图片、视频怎么读?

python
with open('/path/to/test.jpg', 'rb') as f:   # 'rb' = read binary
    data = f.read()
    print(data[:20])   # b'\xff\xd8\xff\xe1\x00\x18Exif\x00\x00...'

区别

  • 'r':读文本,返回 str
  • 'rb':读二进制,返回 bytes(十六进制字节)。

六、字符编码:读懂"外语"文件

6.1 指定编码

python
# 读取 GBK 编码的文件
with open('/path/to/gbk.txt', 'r', encoding='gbk') as f:
    print(f.read())   # '测试'

6.2 忽略编码错误

python
with open('/path/to/gbk.txt', 'r', encoding='gbk', errors='ignore') as f:
    print(f.read())

场景:文件里夹杂非法编码字符,忽略错误保证能读出来。

生活化理解errors='ignore' 是"不懂的外语单词跳过,只翻译看得懂的"。


七、写文件:从内存到磁盘

7.1 基本写法

python
with open('/path/to/test.txt', 'w') as f:   # 'w' = write
    f.write('Hello, world!')

注意'w' 模式会覆盖已有文件!

7.2 追加模式

python
with open('/path/to/test.txt', 'a') as f:   # 'a' = append
    f.write('\n追加一行')

7.3 为什么必须 close()

python
f = open('/path/to/test.txt', 'w')
f.write('Hello')
# 如果程序崩溃,数据可能还在内存缓存里,没写到磁盘!
f.close()   # 只有 close() 才保证全部写入磁盘

生活化理解write() 是"把包裹放传送带上",close() 是"按发货按钮"——不按按钮,包裹还在仓库,没发出去。

7.4 写入指定编码

python
with open('/path/to/gbk.txt', 'w', encoding='gbk') as f:
    f.write('测试')

八、知识链条:文件读写完整流程

确定操作:读 or 写?

选择模式:'r'/'w'/'a'/'rb'/'wb'

with open(...) as f:  ← 自动管理资源

读:read() / read(size) / readline() / readlines()
写:write() / writelines()

(可选)指定 encoding / errors

出 with 块 → 自动 close() → 资源释放

九、常见误区与避坑指南

9.1 误区一:忘记关闭文件

python
f = open('test.txt', 'w')
f.write('hello')
# 忘记 close,数据可能丢失!

修正:永远用 with

9.2 误区二:用 'w' 模式想追加

python
with open('log.txt', 'w') as f:
    f.write('第一行')

with open('log.txt', 'w') as f:
    f.write('第二行')   # ❌ 第一行被覆盖了!

修正:追加用 'a'

python
with open('log.txt', 'a') as f:
    f.write('第二行')

9.3 误区三:文本文件用二进制模式读

python
with open('test.txt', 'rb') as f:
    content = f.read()
    print(content.upper())   # ❌ bytes 没有 upper()!

修正:文本用 'r',二进制用 'rb'

9.4 误区四:路径分隔符硬编码

python
f = open('C:\\Users\\name\\file.txt', 'r')   # Windows 专用
f = open('/home/user/file.txt', 'r')         # Linux/Mac 专用

修正:用 os.path.join()pathlib

python
import os
path = os.path.join('folder', 'file.txt')   # 自动适配系统

或:

python
from pathlib import Path
path = Path('folder') / 'file.txt'

十、实际应用案例

案例 1:日志文件分析器

python
import os
from datetime import datetime

def analyze_log(log_path):
    """分析日志文件,统计错误数量和类型"""
    if not os.path.exists(log_path):
        print(f'日志文件不存在: {log_path}')
        return

    error_count = 0
    error_types = {}

    with open(log_path, 'r', encoding='utf-8', errors='ignore') as f:
        for line_num, line in enumerate(f, 1):
            line = line.strip()
            if not line:
                continue

            if 'ERROR' in line:
                error_count += 1
                # 提取错误类型
                if 'Database' in line:
                    error_types['Database'] = error_types.get('Database', 0) + 1
                elif 'Network' in line:
                    error_types['Network'] = error_types.get('Network', 0) + 1
                elif 'Auth' in line:
                    error_types['Auth'] = error_types.get('Auth', 0) + 1
                else:
                    error_types['Other'] = error_types.get('Other', 0) + 1

    print(f'总错误数: {error_count}')
    print('错误分布:')
    for err_type, count in sorted(error_types.items(), key=lambda x: -x[1]):
        print(f'  {err_type}: {count} 次')

# 使用
analyze_log('/var/log/app.log')

生活化理解:日志分析器是"会计查账"——逐行看(for line in f),发现"ERROR"就记账,最后汇总出哪类问题最多。

案例 2:配置文件生成器

python
import json

def create_config(output_path, **settings):
    """生成 JSON 配置文件"""
    config = {
        'version': '1.0',
        'created_at': datetime.now().isoformat(),
        'settings': settings
    }

    with open(output_path, 'w', encoding='utf-8') as f:
        json.dump(config, f, indent=2, ensure_ascii=False)

    print(f'配置文件已保存: {output_path}')

def load_config(config_path):
    """读取配置文件"""
    with open(config_path, 'r', encoding='utf-8') as f:
        return json.load(f)

# 使用
create_config(
    'app_config.json',
    database={'host': 'localhost', 'port': 3306},
    cache={'enabled': True, 'ttl': 3600},
    features=['login', 'export', 'import']
)

config = load_config('app_config.json')
print(config['settings']['database']['host'])   # localhost

生活化理解:配置文件生成器是"自动填表机"——你告诉它参数,它按格式写好存起来,下次直接读。


十一、实战练习

练习:读取本地文本文件

请将本地一个文本文件读为 str 并打印出来:

python
fpath = '/etc/timezone'   # Linux/Mac
# fpath = 'C:\\Windows\\system.ini'   # Windows

# 你的代码
参考答案
python
fpath = '/etc/timezone'

with open(fpath, 'r') as f:
    s = f.read()
    print(s)

Windows 用户注意:路径用 \\r'C:\...' 原始字符串。


十二、小结

  1. 文件读写本质:通过 open() 请求操作系统,获得文件对象,用完必须 close()
  2. 永远用 with:自动管理资源,异常也安全;
  3. 读取策略:小文件 read(),大文件 read(size),配置 readlines()
  4. 模式选择'r' 读文本、'rb' 读二进制、'w' 覆盖写、'a' 追加写;
  5. 编码处理encoding='gbk' 读非 UTF-8,errors='ignore' 忽略非法字符;
  6. file-like Object:有 read() 就是文件,来源不限(磁盘、内存、网络);
  7. 核心原则with + 合适的模式 + 注意编码 = 安全的文件操作。

文件读写是程序的"对外窗口"——数据进来,结果出去,with 确保这扇窗"开得好,关得牢"。