Skip to content

Python 3 - 文件 I/O

本章涵盖了在 Python 3 中读写文件的基本函数和技术。

print() 函数是将输出显示到标准输出流(通常是你的终端/控制台)的主要方式。

#!/usr/bin/env python3
print("Hello, world!")
name = "Alice"
age = 30
print("Name:", name, "Age:", age) # 默认情况下多个参数用空格分隔
print(f"Name: {name}, Age: {age}") # 使用 f-string 进行格式化输出
print("No newline at the end ->", end="") # 使用 'end' 参数改变结束符
print("<- See?")
print("Item1", "Item2", sep=" | ") # 使用 'sep' 参数改变分隔符

输出:

Hello, world!
Name: Alice Age: 30
Name: Alice, Age: 30
No newline at the end -><- See?
Item1 | Item2

input() 函数从标准输入(键盘)读取用户输入的一行文本,并将其作为字符串返回。

注意:Python 2 有 raw_input()(返回字符串)和 input()(尝试将输入评估为 Python 代码)。在 Python 3 中,input() 的行为类似于 Python 2 的 raw_input(),总是返回一个字符串。如果你需要其他类型(例如数字),必须明确进行类型转换。

#!/usr/bin/env python3
user_name = input("Enter your name: ")
print(f"Hello, {user_name}!")
age_str = input("Enter your age: ")
# 输入始终是字符串,如果需要其他类型必须进行转换
try:
age_num = int(age_str)
print(f"Next year, you will be {age_num + 1}.")
except ValueError:
print("Invalid age entered. Please enter a number.")

Python 提供了强大的工具用于与文件系统上的文件进行交互。

打开文件:open() 函数和 with 语句(推荐)

Section titled “打开文件:open() 函数和 with 语句(推荐)”

在读写文件之前,你必须使用内置的 open() 函数打开文件。它会返回一个文件对象。

最佳实践: 在操作文件时使用 with 语句。它能确保即使在处理过程中发生错误,文件也会自动关闭。

with open(file_path, mode='r', encoding=None) as file_variable:
# 操作文件对象 (file_variable)
pass
# 文件在此处自动关闭

open() 的参数:

  • file_path: 一个字符串,包含文件的路径(例如,'my_data.txt','../config/settings.ini')。
  • mode: 一个字符串,指示应如何打开文件(读取、写入、追加等)。默认为 'r'(文本读取)。参见下表。
  • encoding: 指定用于解码/编码文件的编码方式(例如,'utf-8','latin-1')。对于文本模式至关重要。如果为 None,则使用系统默认编码,但通常推荐明确设置 'utf-8' 以保证可移植性。
模式描述
'r'读取(默认)。文本模式。如果文件不存在则报错。
'w'写入。文本模式。如果文件不存在则创建,如果存在则截断(清空)。
'a'追加。文本模式。如果文件不存在则创建。写入内容会添加到文件末尾。
'rb'二进制读取。用于非文本文件(图片、可执行文件)。
'wb'二进制写入。如果文件存在则截断。
'ab'二进制追加。
'r+'读写。文本模式。不截断。如果文件不存在则报错。
'w+'写读。文本模式。如果文件存在则截断。
'a+'追加并读。文本模式。写入到文件末尾。
'x'独占创建。文本模式。如果文件已存在则失败。
'xb', 'x+' 等独占创建的二进制或读写变体。

注意:在模式后追加 b(例如,'rb','wb')表示二进制模式。如果未指定 b,则默认为文本模式(t)。

文件打开后,文件对象具有有用的属性:

属性描述
file.closed布尔值:如果文件已关闭则为 True,否则为 False。
file.mode字符串:打开文件时使用的模式(例如,'r','wb')。
file.name字符串:文件的名称/路径。
file.encoding字符串:使用的编码(在文本模式下)。
#!/usr/bin/env python3
try:
with open("sample.txt", "w", encoding="utf-8") as f:
print(f"File Name: {f.name}")
print(f"Opening Mode: {f.mode}")
print(f"Encoding: {f.encoding}")
print(f"Is Closed? {f.closed}")
f.write("Test line\n")
print(f"After with block, Is Closed? {f.closed}")
except IOError as e:
print(f"Error opening/writing file: {e}")

使用 write() 方法将字符串(在文本模式下)或字节串(在二进制模式下)写入文件。它返回写入的字符数/字节数。在文本模式下,如果需要换行,请记得手动包含换行符 (\n)。

writelines() 方法将一个序列(例如,一个列表)的字符串写入文件。它不会在字符串之间添加换行符。

#!/usr/bin/env python3
lines_to_write = [
"First line.\n",
"Second line.\n",
"Third line without newline."
]
try:
with open("output.txt", "w", encoding="utf-8") as f:
num_chars = f.write("This is a single line written with write().\n")
print(f"Wrote {num_chars} characters.")
f.writelines(lines_to_write)
print("Wrote multiple lines with writelines().")
except IOError as e:
print(f"Error writing to file: {e}")

从文件读取 (read, readline, readlines, 迭代)

Section titled “从文件读取 (read, readline, readlines, 迭代)”

有几种读取方法:

  • read([size]): 从文件中读取 size 个字符/字节。如果省略 size 或 size 为负数,则读取整个文件内容。
  • readline(): 从文件中读取一行,包括末尾的换行符 (\n)。在文件末尾返回空字符串。
  • readlines(): 读取文件中所有剩余的行,并将其作为字符串列表返回。
  • 迭代: Pythonic 的逐行读取文件的方式是直接迭代文件对象。
#!/usr/bin/env python3
# 假设 output.txt 文件在前面的例子中已经创建
try:
# 方法 1: 读取整个文件
with open("output.txt", "r", encoding="utf-8") as f:
content = f.read()
print("--- Read All ---")
print(content)
# 方法 2: 使用迭代逐行读取 (推荐)
with open("output.txt", "r", encoding="utf-8") as f:
print("--- Read by Iteration ---")
for line in f:
print(line, end='') # print 会添加换行符,所以使用 end='' 防止双重换行
print("\n-------------------------")
# 方法 3: Readlines 读取到列表
with open("output.txt", "r", encoding="utf-8") as f:
lines_list = f.readlines()
print("--- Readlines List ---")
print(lines_list) # 列表中包含换行符
except FileNotFoundError:
print("Error: output.txt not found.")
except IOError as e:
print(f"Error reading file: {e}")

tell(): 返回文件指针(光标)的当前位置,以从文件开头算起的字节数(整数)表示。

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

  • 0(默认):从文件开头算起(os.SEEK_SET)。
  • 1:相对于当前位置算起(os.SEEK_CUR)。
  • 2:相对于文件末尾算起(os.SEEK_END)。

注意:在文本模式下进行 seek(除了 seek(0))可能不可靠,除非你 seek 到之前由 tell() 返回的位置。二进制模式提供更可预测的按字节偏移量进行 seek。

#!/usr/bin/env python3
try:
with open("output.txt", "r+", encoding="utf-8") as f: # r+ 模式允许读写
text = f.read(10)
print(f"Read first 10 chars: '{text}'")
current_pos = f.tell()
print(f"Current position: {current_pos}")
f.seek(0) # 返回文件开头
print(f"Position after seek(0): {f.tell()}")
f.seek(5, 1) # 从当前位置(即 0)向前移动 5 个字节
print(f"Position after seek(5, 1): {f.tell()}")
print(f"Reading from new pos: '{f.readline().strip()}'")
# 从开头位置覆盖写入
f.seek(0)
f.write("OVERWRITTEN!")
except FileNotFoundError:
print("Error: output.txt not found.")
except IOError as e:
print(f"Error seeking/reading file: {e}")

Python 提供了模块用于与文件系统交互,而不仅仅是读写文件内容。

  • os 模块: 传统的与操作系统交互的模块,包括文件操作。
  • pathlib 模块 (Python 3.4+): 一种现代化、面向对象的方式来处理文件系统路径和操作。通常因其清晰性和易用性而被优先使用。

我们将展示在适用的情况下使用这两个模块的例子。

#!/usr/bin/env python3
import os
from pathlib import Path
# 创建一个临时文件
with open("test_old.txt", "w") as f:
f.write("temporary file")
# --- 使用 os 模块 ---
try:
os.rename("test_old.txt", "test_new_os.txt")
print("Renamed using os")
os.remove("test_new_os.txt")
print("Removed using os")
except OSError as e:
print(f"OS Error: {e}")
# 为 pathlib 例子重新创建文件
with open("test_old.txt", "w") as f:
f.write("temporary file")
# --- 使用 pathlib 模块 ---
try:
old_path = Path("test_old.txt")
new_path_pl = Path("test_new_pathlib.txt")
old_path.rename(new_path_pl)
print("Renamed using pathlib")
new_path_pl.unlink() # pathlib 使用 unlink() 删除文件/符号链接
print("Removed using pathlib")
except OSError as e:
print(f"Pathlib Error: {e}")

创建、改变、获取当前目录以及删除目录。

#!/usr/bin/env python3
import os
from pathlib import Path
# --- 使用 os 模块 ---
new_dir_os = "my_temp_dir_os"
try:
print(f"Current directory (os): {os.getcwd()}")
os.mkdir(new_dir_os) # 创建目录
print(f"Created directory: {new_dir_os}")
os.chdir(new_dir_os) # 进入新目录
print(f"Current directory after chdir (os): {os.getcwd()}")
os.chdir("..") # 返回上一级目录
print(f"Current directory after chdir .. (os): {os.getcwd()}")
os.rmdir(new_dir_os) # 删除目录(必须为空)
print(f"Removed directory: {new_dir_os}")
except OSError as e:
print(f"OS Error: {e}")
# 如果中间出错,进行清理
if os.path.exists(new_dir_os):
if os.path.basename(os.getcwd()) == new_dir_os: os.chdir("..")
os.rmdir(new_dir_os)
print("\n---\n")
# --- 使用 pathlib 模块 ---
new_dir_pl = Path("my_temp_dir_pl")
try:
print(f"Current directory (pathlib): {Path.cwd()}")
new_dir_pl.mkdir() # 创建目录
print(f"Created directory: {new_dir_pl}")
# pathlib 没有直接的 chdir 方法,使用 os.chdir()
os.chdir(new_dir_pl)
print(f"Current directory after chdir (pathlib): {Path.cwd()}")
os.chdir("..")
print(f"Current directory after chdir .. (pathlib): {Path.cwd()}")
new_dir_pl.rmdir() # 删除目录(必须为空)
print(f"Removed directory: {new_dir_pl}")
except OSError as e:
print(f"Pathlib Error: {e}")
# 如果中间出错,进行清理
if new_dir_pl.exists():
if Path.cwd().name == new_dir_pl.name: os.chdir("..")
new_dir_pl.rmdir()

其他有用的函数/方法:

  • os.makedirs(path, exist_ok=False) / Path(path).mkdir(parents=True, exist_ok=False): 递归创建目录(包括中间目录)。
  • os.path.exists(path) / Path(path).exists(): 检查路径是否存在。
  • os.path.isfile(path) / Path(path).is_file(): 检查路径是否为常规文件。
  • os.path.isdir(path) / Path(path).is_dir(): 检查路径是否为目录。
  • os.listdir(path) / Path(path).iterdir(): 列出目录内容。