Python文件读写方法详解

1. 引言

文件操作是编程中最常见的任务之一,Python提供了强大而灵活的文件处理能力。无论是读取配置文件、处理日志数据、保存用户输入,还是进行数据分析,都离不开文件操作。本教程将全面介绍Python文件读写的方法,包括打开文件、读取内容、写入数据、关闭文件等基本操作,以及文件指针管理、二进制模式处理、异常处理等高级技巧。通过详细的代码示例和逐步解释,帮助您掌握Python文件操作的核心知识。

2. 打开文件

在Python中,使用内置的open()函数打开文件。这是所有文件操作的第一步,也是最关键的一步,因为它决定了文件的访问方式和权限。

2.1. 基本语法

file_object = open(file_path, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

2.2. 参数详解

file_path:文件路径(必需参数)

可以是绝对路径(如'C:/data/file.txt')或相对路径(如'data/file.txt'

在Windows系统中,反斜杠\需要转义('C:\\data\\file.txt')或使用原始字符串(r'C:\data\file.txt'

mode:文件打开模式(可选,默认为'r'

'r':只读模式(默认)

'w':写入模式(会覆盖已有内容)

'a':追加模式(在文件末尾添加内容)

'x':独占创建模式(文件不存在时创建,存在则报错)

'b':二进制模式(与其他模式组合使用)

't':文本模式(默认)

'+':读写模式(与其他模式组合使用)

encoding:文件编码(可选,文本模式有效)

常见编码:'utf-8'(推荐)、'gbk''ascii''latin-1'

如果不指定,使用系统默认编码

errors:编码错误处理策略(可选)

'strict':默认,遇到编码错误抛出异常

'ignore':忽略编码错误

'replace':用替换字符替代无法解码的内容

'backslashreplace':用Python反斜杠转义序列替换

newline:换行符处理(可选)

None:所有平台的换行符都转换为\n

'':不转换换行符

'\n''\r''\r\n':指定换行符

2.3. 示例代码

# 以只读模式打开UTF-8编码的文本文件
file1 = open('example.txt', 'r', encoding='utf-8')

# 以写入模式打开二进制文件
file2 = open('data.bin', 'wb')

# 以追加读写模式打开文件,指定编码错误处理
file3 = open('log.txt', 'a+', encoding='gbk', errors='replace')

2.4. 注意事项

打开文件后必须关闭文件,否则可能导致资源泄露

使用with语句可以自动管理文件关闭(推荐)

打开不存在的文件进行读操作会引发FileNotFoundError

打开已有文件进行写操作会覆盖原内容(使用'w'模式时)

3. 读取文件内容

打开文件后,可以使用多种方法读取文件内容,根据需求选择合适的方法。

3.1. read()方法

读取文件全部内容到字符串中。

file = open('example.txt', 'r', encoding='utf-8')
content = file.read()  # 读取全部内容
file.close()
print(content)

参数说明:

size:可选参数,指定读取的字节数(文本模式)或字符数

如果省略size或指定为负数,读取整个文件

file = open('example.txt', 'r', encoding='utf-8')
first_10_chars = file.read(10)  # 读取前10个字符
file.close()
print(first_10_chars)

3.2. readline()方法

读取文件中的一行内容。

file = open('example.txt', 'r', encoding='utf-8')
line1 = file.readline()  # 读取第一行
line2 = file.readline()  # 读取第二行
file.close()
print("第一行:", line1)
print("第二行:", line2)

特点:

保留行尾的换行符(\n

文件末尾返回空字符串(''

3.3. readlines()方法

读取文件所有行到列表中。

file = open('example.txt', 'r', encoding='utf-8')
lines = file.readlines()  # 读取所有行到列表
file.close()
for i, line in enumerate(lines, 1):
    print(f"行 {i}: {line.strip()}")  # 去除行尾空白

参数说明:

hint:可选参数,指定读取的行数(近似值)

如果文件很大,使用hint可以控制内存使用

3.4. 迭代文件对象

直接遍历文件对象逐行读取,推荐用于大文件处理。

file = open('example.txt', 'r', encoding='utf-8')
for line in file:  # 逐行迭代
    print(line.strip())
file.close()

3.5. 二进制文件读取

使用'rb'模式读取二进制文件。

file = open('data.bin', 'rb')
data = file.read()  # 读取为字节串
file.close()
print("数据类型:", type(data))
print("数据内容:", data)

3.6. 完整示例:逐行读取并处理

# 打开文件
with open('example.txt', 'r', encoding='utf-8') as file:
    line_number = 0
    for line in file:
        line_number += 1
        # 去除行首尾空白
        stripped_line = line.strip()
        # 跳过空行
        if not stripped_line:
            continue
        # 处理行内容
        if 'error' in stripped_line.lower():
            print(f"错误行 (行 {line_number}): {stripped_line}")
        else:
            print(f"正常行 (行 {line_number}): {stripped_line}")

4. 写入文件内容

Python提供了多种方法向文件写入内容,包括写入字符串、写入列表等。

4.1. write()方法

向文件写入字符串内容。

file = open('output.txt', 'w', encoding='utf-8')
file.write("第一行内容\n")  # 写入字符串
file.write("第二行内容\n")
file.close()

注意:

write()方法不会自动添加换行符,需要手动添加\n

写入二进制数据需要使用'wb'模式

4.2. writelines()方法

向文件写入字符串列表。

lines = ["第一行\n", "第二行\n", "第三行\n"]
file = open('output.txt', 'w', encoding='utf-8')
file.writelines(lines)  # 写入多行
file.close()

注意:

列表中的每个元素必须包含换行符(如果需要换行)

不会自动添加换行符

4.3. 二进制文件写入

data = b'\x48\x65\x6c\x6c\x6f'  # 字节串(Hello的ASCII编码)
file = open('binary.bin', 'wb')
file.write(data)
file.close()

4.4. 追加内容

使用'a'模式在文件末尾追加内容。

file = open('output.txt', 'a', encoding='utf-8')
file.write("这是追加的内容\n")
file.close()

4.5. 完整示例:写入多行数据

# 准备数据
users = [
    {"name": "Alice", "age": 30, "email": "alice@example.com"},
    {"name": "Bob", "age": 25, "email": "bob@example.com"},
    {"name": "Charlie", "age": 35, "email": "charlie@example.com"}
]

# 写入文件
with open('users.txt', 'w', encoding='utf-8') as file:
    file.write("Name\tAge\tEmail\n")  # 写入标题行
    for user in users:
        # 格式化数据并写入
        line = f"{user['name']}\t{user['age']}\t{user['email']}\n"
        file.write(line)

5. 文件指针操作

文件指针表示当前读写的位置,Python提供了操作文件指针的方法。

5.1. tell()方法

获取当前文件指针的位置(字节偏移量)。

file = open('example.txt', 'r', encoding='utf-8')
print("当前位置:", file.tell())  # 初始位置为0
file.read(5)  # 读取5个字符
print("读取5个字符后位置:", file.tell())
file.close()

5.2. seek()方法

移动文件指针到指定位置。

file = open('example.txt', 'r', encoding='utf-8')
file.read(5)  # 读取前5个字符
file.seek(0)  # 移动回文件开头
content = file.read()  # 重新读取全部内容
file.close()
print(content)

参数说明:

offset:偏移量(字节数)

whence:参考位置(可选,默认为0)

0:文件开头(默认)

1:当前位置

2:文件末尾

示例:

file = open('example.txt', 'r', encoding='utf-8')
# 移动到文件末尾前10个字节
file.seek(-10, 2)
last_10_bytes = file.read()
print("最后10个字节:", last_10_bytes)
file.close()

5.3. 注意事项

文本模式下,seek()只能移动到read()tell()返回的位置

二进制模式下,可以自由移动到任何位置

追加模式('a')下,文件指针始终在文件末尾,seek()不影响写入位置

5.4. 完整示例:随机访问文件

with open('example.txt', 'r+', encoding='utf-8') as file:
    # 读取前10个字符
    first_part = file.read(10)
    print("前10个字符:", first_part)

    # 获取当前位置
    current_pos = file.tell()
    print("当前位置:", current_pos)

    # 移动到文件开头
    file.seek(0)

    # 读取第一行
    first_line = file.readline()
    print("第一行:", first_line.strip())

    # 移动回之前的位置
    file.seek(current_pos)

    # 继续读取
    rest_content = file.read()
    print("剩余内容:", rest_content[:50] + "...")

6. 关闭文件

文件操作完成后,必须关闭文件以释放系统资源。

6.1. close()方法

显式关闭文件。

file = open('example.txt', 'r', encoding='utf-8')
content = file.read()
file.close()  # 关闭文件

6.2. 使用with语句(推荐)

自动管理文件的打开和关闭,即使发生异常也能正确关闭。

with open('example.txt', 'r', encoding='utf-8') as file:
    content = file.read()
# 文件在这里自动关闭
print(content)

6.3. 为什么必须关闭文件

释放系统资源(文件描述符)

确保所有缓冲数据写入磁盘

避免其他进程无法访问该文件

防止数据损坏或丢失

6.4. 检查文件是否关闭

file = open('example.txt', 'r', encoding='utf-8')
print("文件是否关闭:", file.closed)  # False
file.close()
print("文件是否关闭:", file.closed)  # True

7. 文件操作模式详解

Python提供了多种文件操作模式,不同模式组合可以实现不同的访问需求。

7.1. 文本模式与二进制模式

文本模式(默认)

't':文本模式(默认)

自动编码/解码(如UTF-8)

自动转换换行符(\n\r\n等)

二进制模式

'b':二进制模式

不进行编码/解码

不转换换行符

适用于非文本文件(图片、音频、视频等)

7.2. 基本访问模式

| 模式 | 描述 | 文件存在时 | 文件不存在时 | 指针位置 |

|------|------|-----------|------------|----------|

| 'r' | 只读 | 成功打开 | 报错 | 文件开头 |

| 'w' | 只写 | 截断文件(清空) | 创建新文件 | 文件开头 |

| 'a' | 只追加 | 在末尾追加 | 创建新文件 | 文件末尾 |

| 'x' | 独占创建 | 报错 | 创建新文件 | 文件开头 |

7.3. 组合模式

读写模式

'r+':读写(文件必须存在)

'w+':读写(文件存在则清空)

'a+':读写(文件不存在则创建,追加写入)

二进制组合

'rb':二进制只读

'wb':二进制只写

'ab':二进制追加

'rb+':二进制读写

'wb+':二进制读写(清空)

'ab+':二进制读写(追加)

7.4. 模式示例

# 只读模式
with open('data.txt', 'r') as f:
    data = f.read()

# 只写模式(覆盖)
with open('output.txt', 'w') as f:
    f.write("新内容")

# 追加模式
with open('log.txt', 'a') as f:
    f.write("新日志条目\n")

# 读写模式
with open('data.txt', 'r+') as f:
    content = f.read()
    f.seek(0)
    f.write("修改后的内容")

# 二进制读写
with open('image.png', 'rb') as f:
    image_data = f.read()

# 二进制追加
with open('data.bin', 'ab') as f:
    f.write(b'\x00\x01\x02')

7.5. 模式选择指南

只需要读取已有文件:'r''rb'

需要创建新文件或覆盖已有文件:'w''wb'

需要在文件末尾添加内容:'a''ab'

需要同时读取和写入:'r+''w+''a+'

处理文本文件:使用文本模式(默认或显式指定't'

处理非文本文件:必须使用二进制模式('b'

8. 二进制文件处理

二进制文件处理是文件操作中的重要部分,适用于处理图像、音频、视频、可执行文件等非文本数据。

8.1. 读取二进制文件

# 读取图片文件
with open('image.jpg', 'rb') as img_file:
    image_data = img_file.read()
    print("文件大小:", len(image_data), "字节")
    # 显示前20个字节的十六进制表示
    print("前20个字节:", image_data[:20].hex())

8.2. 写入二进制文件

# 创建简单的二进制数据
data = bytes([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A])  # PNG文件头

# 写入二进制文件
with open('test.png', 'wb') as f:
    f.write(data)
    print("二进制数据已写入")

8.3. 二进制文件修改

# 修改二进制文件的特定字节
with open('data.bin', 'r+b') as f:
    # 读取前10个字节
    first_bytes = f.read(10)
    print("原始前10字节:", first_bytes)

    # 修改第5个字节(索引4)
    f.seek(4)
    f.write(bytes([0xFF]))  # 写入新字节

    # 读取修改后的内容
    f.seek(0)
    modified_bytes = f.read(10)
    print("修改后前10字节:", modified_bytes)

8.4. 结构化二进制数据处理

使用struct模块处理结构化二进制数据:

import struct

# 创建二进制数据(整数、浮点数、字符串)
packer = struct.Struct('i f 10s')  # 整型、浮点型、10字节字符串
data = packer.pack(42, 3.14, b'Hello')  # 打包数据

# 写入文件
with open('struct.bin', 'wb') as f:
    f.write(data)

# 读取文件并解包
with open('struct.bin', 'rb') as f:
    packed_data = f.read(packer.size)
    unpacked_data = packer.unpack(packed_data)
    print("解包后的数据:", unpacked_data)
    # (42, 3.140000104904175, b'Hello')

8.5. 完整示例:复制二进制文件

def copy_binary_file(src_path, dst_path, chunk_size=8192):
    """
    复制二进制文件(大文件分块处理)
    :param src_path: 源文件路径
    :param dst_path: 目标文件路径
    :param chunk_size: 每次读取的块大小(字节)
    """
    try:
        with open(src_path, 'rb') as src_file:
            with open(dst_path, 'wb') as dst_file:
                while True:
                    chunk = src_file.read(chunk_size)
                    if not chunk:
                        break
                    dst_file.write(chunk)
        print(f"文件 {src_path} 已成功复制到 {dst_path}")
    except IOError as e:
        print(f"复制文件时出错: {e}")

# 使用示例
copy_binary_file('source.jpg', 'destination.jpg')

9. 异常处理

文件操作中可能会遇到各种异常情况,良好的异常处理能增强程序的健壮性。

9.1. 常见文件异常

FileNotFoundError:文件不存在

尝试打开不存在的文件进行读取时抛出

PermissionError:权限错误

无足够权限访问文件或目录时抛出

IsADirectoryError:是目录而非文件

尝试打开目录而不是文件时抛出

FileExistsError:文件已存在

使用独占创建模式('x')打开已存在文件时抛出

NotADirectoryError:不是目录

尝试在非目录上执行目录操作时抛出

UnicodeDecodeError:Unicode解码错误

文本模式下编码不匹配时抛出

9.2. 基本异常处理

try:
    file = open('nonexistent.txt', 'r', encoding='utf-8')
    content = file.read()
    file.close()
except FileNotFoundError:
    print("错误:文件不存在!")
except PermissionError:
    print("错误:没有权限访问文件!")
except Exception as e:
    print(f"发生未知错误: {e}")
else:
    print("文件读取成功")
    print(content)

9.3. 使用with语句的异常处理

try:
    with open('example.txt', 'r', encoding='utf-8') as file:
        content = file.read()
except UnicodeDecodeError:
    print("错误:文件编码不正确,请尝试其他编码")
except IOError as e:
    print(f"文件I/O错误: {e}")
else:
    print(content)

9.4. 检查文件是否存在

在打开文件前检查文件是否存在:

import os

file_path = 'example.txt'
if os.path.exists(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
        content = file.read()
else:
    print(f"文件 {file_path} 不存在")

9.5. 创建目录(如果不存在)

import os

dir_path = 'data/output'
file_path = os.path.join(dir_path, 'result.txt')

try:
    # 创建目录(如果不存在)
    os.makedirs(dir_path, exist_ok=True)

    # 写入文件
    with open(file_path, 'w', encoding='utf-8') as file:
        file.write("数据处理结果")
        print(f"结果已保存到 {file_path}")
except Exception as e:
    print(f"操作失败: {e}")

9.6. 完整异常处理示例

import os

def safe_read_file(file_path, encoding='utf-8'):
    """
    安全读取文件(包含完整异常处理)
    :param file_path: 文件路径
    :param encoding: 文件编码
    :return: 文件内容(字符串)
    """
    try:
        # 检查文件是否存在
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"文件 {file_path} 不存在")

        # 检查是否是文件而非目录
        if not os.path.isfile(file_path):
            raise IsADirectoryError(f"路径 {file_path} 是目录而非文件")

        # 检查读取权限
        if not os.access(file_path, os.R_OK):
            raise PermissionError(f"没有权限读取文件 {file_path}")

        # 尝试打开并读取文件
        with open(file_path, 'r', encoding=encoding) as file:
            content = file.read()
            return content

    except FileNotFoundError as e:
        print(f"错误: {e}")
    except IsADirectoryError as e:
        print(f"错误: {e}")
    except PermissionError as e:
        print(f"错误: {e}")
    except UnicodeDecodeError:
        print(f"错误: 文件 {file_path} 编码不是 {encoding}")
    except IOError as e:
        print(f"文件I/O错误: {e}")
    except Exception as e:
        print(f"发生未知错误: {e}")

    return None  # 发生错误时返回None

# 使用示例
content = safe_read_file('example.txt')
if content is not None:
    print("文件内容:")
    print(content)

10. 实际应用示例

10.1. 示例1:读取CSV文件并处理

# 读取CSV文件并计算平均值
def analyze_csv(file_path):
    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            # 跳过标题行
            next(file)

            total = 0
            count = 0

            for line in file:
                # 去除空白并分割
                parts = line.strip().split(',')
                if len(parts) >= 2:
                    try:
                        value = float(parts[1])
                        total += value
                        count += 1
                    except ValueError:
                        continue  # 跳过无效数据

            if count > 0:
                average = total / count
                print(f"平均值: {average:.2f} (基于 {count} 个数据点)")
            else:
                print("没有有效数据")

    except FileNotFoundError:
        print(f"错误: 文件 {file_path} 不存在")
    except Exception as e:
        print(f"处理文件时出错: {e}")

# 使用示例
analyze_csv('data.csv')

10.2. 示例2:日志文件分析

# 分析日志文件中的错误
def analyze_log(file_path):
    error_count = 0
    warning_count = 0

    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            for line_num, line in enumerate(file, 1):
                line = line.strip()
                if "ERROR" in line:
                    error_count += 1
                    print(f"行 {line_num}: {line}")
                elif "WARNING" in line:
                    warning_count += 1

        print(f"\n统计结果:")
        print(f"错误数: {error_count}")
        print(f"警告数: {warning_count}")

    except FileNotFoundError:
        print(f"错误: 日志文件 {file_path} 不存在")
    except Exception as e:
        print(f"分析日志时出错: {e}")

# 使用示例
analyze_log('application.log')

10.3. 示例3:批量处理文件

import os

# 批量处理目录中的所有文本文件
def batch_process_text_files(directory, search_word, replacement):
    """
    批量替换目录中所有文本文件中的指定单词
    :param directory: 目录路径
    :param search_word: 要搜索的单词
    :param replacement: 替换后的单词
    """
    # 遍历目录
    for root, dirs, files in os.walk(directory):
        for file in files:
            if file.endswith('.txt'):
                file_path = os.path.join(root, file)
                try:
                    with open(file_path, 'r', encoding='utf-8') as f:
                        content = f.read()

                    # 替换单词
                    new_content = content.replace(search_word, replacement)

                    # 写回文件
                    with open(file_path, 'w', encoding='utf-8') as f:
                        f.write(new_content)

                    print(f"已处理文件: {file_path}")

                except Exception as e:
                    print(f"处理文件 {file_path} 时出错: {e}")

# 使用示例
batch_process_text_files('data', 'error', 'issue')

10.4. 示例4:文件合并与分割

# 合并多个文件为一个文件
def merge_files(output_path, input_paths):
    """
    合并多个文件为一个文件
    :param output_path: 输出文件路径
    :param input_paths: 输入文件路径列表
    """
    try:
        with open(output_path, 'wb') as out_file:
            for path in input_paths:
                try:
                    with open(path, 'rb') as in_file:
                        out_file.write(in_file.read())
                        print(f"已合并: {path}")
                except Exception as e:
                    print(f"读取文件 {path} 时出错: {e}")

        print(f"所有文件已合并到 {output_path}")

    except Exception as e:
        print(f"合并文件时出错: {e}")

# 使用示例
merge_files('combined.bin', ['file1.bin', 'file2.bin', 'file3.bin'])

11. 总结

本教程详细介绍了Python文件读写的方法,涵盖了从基础到高级的各个方面。我们学习了如何使用open()函数打开文件,探索了各种文件操作模式,掌握了读取和写入文本及二进制文件的不同方法。通过文件指针操作,我们实现了随机访问文件内容。我们还强调了正确关闭文件的重要性,并推荐使用with语句自动管理资源。

异常处理部分帮助您构建更健壮的文件操作程序,能够优雅地处理各种错误情况。二进制文件处理和结构化数据操作(如使用struct模块)扩展了文件处理的能力,使其能够处理更复杂的数据类型。实际应用示例展示了如何将所学知识应用于解决实际问题,如日志分析、CSV处理、批量文件操作和文件合并等。

掌握Python文件读写技巧是编程的基本技能,也是数据处理、系统管理和软件开发的重要基础。通过本教程的学习,您应该能够自信地处理各种文件操作任务,并根据实际需求选择最合适的方法和模式。在实际开发中,请务必注意文件权限、编码问题和异常处理,以确保程序的稳定性和可靠性。

发表回复

后才能评论