Skip to content

Python 文件 I/O

本章将介绍 Python 中基本的输入/输出 (I/O) 操作,重点是与控制台(标准输入/输出)和文件进行交互。

print() 函数是将输出内容显示到标准输出(通常是控制台)的主要方式。你可以传递零个或多个对象进行打印,对象之间用逗号分隔。

# 简单打印
print("Hello, Python!")
# 打印多个项(默认用空格分隔)
name = "Alice"
age = 30
print("Name:", name, "Age:", age)
# 使用 f-strings 进行格式化输出(推荐)
print(f"Name: {name}, Age: {age}")
# 控制分隔符和结束符
print("Item 1", "Item 2", sep=" | ") # 自定义分隔符
print("First line", end=" - ") # 自定义结束符(默认为 '\n',即换行符)
print("Second line")

运行以上代码,会产生以下结果:

Hello, Python!
Name: Alice Age: 30
Name: Alice, Age: 30
Item 1 | Item 2
First line - Second line

Python 提供了内置的 input() 函数来从标准输入(通常是键盘)读取一行文本。

input([prompt]) 函数会显示可选的 prompt 字符串,等待用户输入文本并按回车键,然后将输入的文本以字符串的形式返回。

#!/usr/bin/env python3
user_name = input("Enter your name: ") # 显示提示信息并等待用户输入
print(f"Hello, {user_name}!")
# input() 的返回值总是字符串,如果需要,请进行转换
age_str = input("Enter your age: ")
try:
age_int = int(age_str) # 尝试将字符串转换为整数
print(f"You will be {age_int + 1} next year.")
except ValueError: # 如果转换失败(输入的不是有效数字)
print("Invalid age entered. Please enter a number.")

运行此脚本可能看起来像这样:

Enter your name: Bob
Hello, Bob!
Enter your age: 25
You will be 26 next year.

注意:Python 2 中的 raw_input() 函数在 Python 3 中已被 input() 函数取代。旧的 Python 2 中的 input() (它会评估输入表达式) 已被移除。

Python 提供了内置函数和方法来进行文件操作。

在读写文件之前,必须使用 open() 函数打开文件。强烈推荐的方式是使用 with 语句,因为它能确保文件在使用后自动关闭,即使发生错误也不例外。

with open(file_path, mode='r', encoding=None) as file_object:
# 在此处使用 file_object 进行文件操作
pass # 退出 'with' 代码块时,文件会自动关闭

参数详情:

  • file_path: 包含文件路径的字符串。
  • mode: 决定文件打开的方式(例如,‘r’ 用于读取,‘w’ 用于写入,‘a’ 用于追加,‘b’ 用于二进制,’+’ 用于更新)。默认为 'r'(读取文本)。
  • encoding: 指定文本编码(例如,'utf-8','ascii')。对于文本文件至关重要,确保在不同系统上正确解释字符。对于文本文件,始终指定编码。'utf-8' 是一种常见且安全的选择。
  • 还存在缓冲选项,但通常不需要更改。

文件模式详情:

模式描述
r读取(默认)。如果文件未找到则报错。
rb以二进制模式读取。
r+读写。指针位于开头。如果文件未找到则报错。
rb+以二进制模式读写。指针位于开头。
w写入。创建文件或截断(清空)现有文件。
wb以二进制模式写入。创建或截断。
w+写入并读取。创建或截断。
wb+以二进制模式写入并读取。创建或截断。
a追加。如果文件未找到则创建。指针位于末尾。
ab以二进制模式追加。如果文件未找到则创建。指针位于末尾。
a+追加并读取。如果文件未找到则创建。写入时指针位于末尾。
ab+以二进制模式追加并读取。如果文件未找到则创建。写入时指针位于末尾。
x独占创建。如果文件已存在则报错。
xb以二进制模式独占创建。
x+独占创建,并读写。
xb+以二进制模式独占创建,并读写。

open() 函数返回的文件对象具有几个有用的属性:

属性描述
file.closed一个布尔值,指示文件是否已关闭。
file.mode打开文件时使用的模式。
file.name文件名。
file.encoding文件使用的编码(用于文本模式)。
#!/usr/bin/env python3
file_path = "sample.txt"
# 以写入模式打开文件(创建或覆盖)
try:
with open(file_path, "w", encoding="utf-8") as f:
print(f"Name of the file: {f.name}") # 打印文件名属性
print(f"Closed or not : {f.closed}") # 打印 closed 属性,在 with 块内为 False
print(f"Opening mode : {f.mode}") # 打印 mode 属性
print(f"Encoding : {f.encoding}") # 打印 encoding 属性
f.write("This is a sample line.\n") # 写入一行文本
# 退出 'with' 代码块后
print(f"\nFile closed now? {f.closed}") # 注意:f 变量仍然存在,但文件已关闭
except IOError as e: # 捕获 IOError 异常
print(f"Error opening or writing file: {e}")

运行此代码会产生类似于以下结果:

Name of the file: sample.txt
Closed or not : False
Opening mode : w
Encoding : utf-8
File closed now? True

close() 方法(以及为何 with 更好)

Section titled “close() 方法(以及为何 with 更好)”

文件对象有一个 close() 方法。如果你不使用 with 语句打开文件,则必须显式调用 close() 来确保数据写入磁盘并释放资源。然而,忘记关闭或在关闭前遇到错误可能导致问题。

手动关闭(不推荐):

# 手动打开/关闭 - 尽可能避免使用
f = None # 初始化为 None
try:
f = open("old_style.txt", "w", encoding="utf-8") # 打开文件
print(f"File name: {f.name}")
f.write("Manual close example.\n") # 写入数据
finally: # 无论是否发生异常,都会执行 finally 块
if f: # 检查文件对象是否存在
f.close() # 必须确保调用 close() 方法
print("File closed manually.")

使用 with 语句是现代 Python 中标准且最安全的做法。

文件对象提供了用于读取和写入数据的方法。

将字符串(在文本模式下)或字节(在二进制模式下)写入文件。它不会自动添加换行符 (\n)。返回写入的字符或字节数。

file_object.write(data)
#!/usr/bin/env python3
file_path = "output.txt"
try:
with open(file_path, "w", encoding="utf-8") as f: # 以写入模式打开文件
num_chars = f.write("Python is a great language.\n") # 写入第一行并获取写入字符数
print(f"Wrote {num_chars} characters.") # 打印写入字符数
f.write("Yes, it's great!!\n") # 写入第二行
print(f"Successfully wrote to {file_path}") # 打印成功信息
except IOError as e: # 捕获 IOError 异常
print(f"Error writing file: {e}")
# output.txt 文件的内容将是:
# Python is a great language.
# Yes, it's great!!

从文件中读取数据。在文本模式下读取字符;在二进制模式下读取字节。

file_object.read(size=-1)

这里,size 是可选的要读取的字符或字节数。如果省略或为负数,则读取直到文件末尾 (EOF)。

示例(使用上面创建的 output.txt)

Section titled “示例(使用上面创建的 output.txt)”
#!/usr/bin/env python3
file_path = "output.txt"
try:
with open(file_path, "r", encoding="utf-8") as f: # 以读取模式打开文件
# 读取前 10 个字符
first_part = f.read(10)
print(f"First 10 chars: '{first_part}'")
# 读取文件的其余部分
rest_of_file = f.read() # read() 再次调用会从当前位置开始读取
print(f"Rest of file: '{rest_of_file}'")
print("\nReading entire file at once:")
with open(file_path, "r", encoding="utf-8") as f: # 再次打开文件以从头开始读取
all_content = f.read() # 读取整个文件内容
print(f"'{all_content}'")
except FileNotFoundError: # 捕获文件未找到异常
print(f"Error: File '{file_path}' not found.")
except IOError as e: # 捕获 IOError 异常
print(f"Error reading file: {e}")

运行此代码会产生以下结果:

First 10 chars: 'Python is '
Rest of file: 'a great language.\nYes, it's great!!\n'
Reading entire file at once:
'Python is a great language.\nYes, it's great!!\n'
  • readline(): 读取一行,包括末尾的 \n(或直到 EOF)。
  • readlines(): 将所有行读取到一个字符串列表中,每行包含其末尾的 \n。
print("\nReading with readline():")
try:
with open(file_path, "r", encoding="utf-8") as f:
line1 = f.readline() # 读取第一行
line2 = f.readline() # 读取第二行
print(f"Line 1: {line1.strip()}") # .strip() 移除行首/行尾的空白字符
print(f"Line 2: {line2.strip()}")
except FileNotFoundError:
print(f"Error: File '{file_path}' not found.")

tell(): 返回文件指针的当前位置(从文件开头算起的字节偏移量)。

seek(offset, whence=0): 移动文件指针。offset 是要移动的字节数。whence 确定参照点:

  • 0 (默认): 从文件开头算起的绝对位置。
  • 1: 从当前指针位置算起的相对位置。
  • 2: 从文件末尾算起的相对位置(偏移量 offset 通常为负数)。
#!/usr/bin/env python3
file_path = "output.txt"
try:
with open(file_path, "r+", encoding="utf-8") as f: # 以读写模式打开文件
# 读取前 10 个字符
str_data = f.read(10)
print(f"Read String is : '{str_data}'")
# 检查当前位置
position = f.tell() # 获取当前指针位置
print(f"Current file position : {position}")
# 将指针重新定位到开头
f.seek(0, 0) # 将指针移动到文件开头 (0 偏移量,从开头算起)
str_data = f.read(10) # 再次读取
print(f"Again read String is : '{str_data}'")
# 移动到末尾并写入
f.seek(0, 2) # 将指针移动到文件末尾 (0 偏移量,从末尾算起)
f.write("Appending using seek.\n") # 在末尾写入数据
except FileNotFoundError:
print(f"Error: File '{file_path}' not found.")
except IOError as e:
print(f"Error accessing file: {e}")

运行此代码会产生以下结果:

Read String is : 'Python is '
Current file position : 10
Again read String is : 'Python is '

os 和 pathlib 等模块提供了除读写文件内容之外的文件系统任务函数。

提供了一种平台无关的方式来与操作系统交互。

重命名文件:os.rename(current_name, new_name)

Section titled “重命名文件:os.rename(current_name, new_name)”
import os
# 先创建一个临时文件
with open("test1.txt", "w") as f: f.write("dummy")
try:
os.rename("test1.txt", "test2.txt") # 将 test1.txt 重命名为 test2.txt
print("Renamed test1.txt to test2.txt")
except OSError as e: # 捕获操作系统相关的错误
print(f"Error renaming file: {e}")

移除文件:os.remove(file_name) 或 os.unlink(file_name)

Section titled “移除文件:os.remove(file_name) 或 os.unlink(file_name)”
try:
os.remove("test2.txt") # 移除 test2.txt 文件
print("Removed test2.txt")
except OSError as e:
print(f"Error removing file: {e}")

提供了文件路径的面向对象接口,通常能使代码更具可读性。

from pathlib import Path
# 创建一个临时文件
dummy_file = Path("pathlib_test1.txt") # 创建一个 Path 对象
dummy_file.write_text("dummy content", encoding="utf-8") # 使用 Path 对象写入文本
new_file_path = Path("pathlib_test2.txt")
new_dir_path = Path("my_test_dir")
# 重命名
try:
dummy_file.rename(new_file_path) # 使用 Path 对象的 rename 方法
print(f"Renamed {dummy_file} to {new_file_path}")
except OSError as e:
print(f"Error renaming: {e}")
# 移除
try:
new_file_path.unlink() # 对于文件,使用 unlink() 方法
print(f"Removed {new_file_path}")
except OSError as e:
print(f"Error removing: {e}")
# 创建目录
try:
new_dir_path.mkdir(exist_ok=True) # 使用 mkdir() 创建目录;exist_ok=True 表示如果目录已存在则不报错
print(f"Created directory {new_dir_path}")
except OSError as e:
print(f"Error creating directory: {e}")
# 获取当前目录
print(f"Current directory: {Path.cwd()}") # 使用 Path.cwd() 获取当前工作目录
# 移除目录(必须是空的)
try:
new_dir_path.rmdir() # 对于目录,使用 rmdir() 方法
print(f"Removed directory {new_dir_path}")
except OSError as e:
print(f"Error removing directory (may not be empty): {e}") # 移除非空目录会报错

os 和 pathlib 都提供了管理目录的函数。

os.mkdir(path) 或 Path(path).mkdir()

os.chdir(path) (改变当前进程的当前工作目录)

os.getcwd() 或 Path.cwd()

os.rmdir(path) 或 Path(path).rmdir() (目录必须是空的)

要移除包含内容的目录,请使用 shutil.rmtree(path)(小心使用!)。

  • 内置函数 open(): 用于读写文件内容(结合 with 语句使用)。
  • os 模块: 传统的、基于函数的方法,用于操作系统交互(路径、进程)。os.path 子模块用于路径操作。
  • pathlib 模块: 现代的、面向对象的方法,用于路径操作和基本文件操作。通常更受青睐。
  • shutil 模块: 用于更高级的文件操作(复制、移动、移除目录树)。