第8章:IO 操作
作为 Java 开发者,你已经熟悉了 Java 的 IO 操作。Python 的 IO 操作与 Java 有很多相似之处,但也有一些重要的区别。本章将详细对比两种语言的 IO 操作。
8.1 文件读写
Java 文件读写
// 读取文件
try (BufferedReader reader = new BufferedReader(new FileReader("example.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
// 写入文件
try (BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {
writer.write("Hello, World!");
writer.newLine();
writer.write("Welcome to Java IO.");
} catch (IOException e) {
e.printStackTrace();
}
Python 文件读写
# 读取文件
with open("example.txt", "r", encoding="utf-8") as file:
for line in file:
print(line.strip())
# 写入文件
with open("output.txt", "w", encoding="utf-8") as file:
file.write("Hello, World!\n")
file.write("Welcome to Python IO.\n")
对比:
| 特性 | Java | Python |
|---|---|---|
| 打开文件 | new FileReader() 或 new FileWriter() | open() 函数 |
| 资源管理 | try-with-resources | with 语句 |
| 读取方式 | readLine() 或 read() | 直接迭代文件对象或 read() |
| 写入方式 | write() | write() |
| 异常处理 | 必须捕获 IOException | 可以捕获 IOError 或 FileNotFoundError |
| 编码处理 | 需要显式指定 | 可以在 open() 中指定 encoding 参数 |
Python 文件操作示例:
# 文件模式
# r: 只读(默认)
# w: 写入(覆盖)
# a: 追加
# x: 创建并写入
# b: 二进制模式
# t: 文本模式(默认)
# 读取整个文件
with open("example.txt", "r") as file:
content = file.read()
print(content)
# 读取指定字符数
with open("example.txt", "r") as file:
content = file.read(10) # 读取前10个字符
print(content)
# 读取所有行
with open("example.txt", "r") as file:
lines = file.readlines()
for line in lines:
print(line.strip())
# 二进制文件
with open("image.jpg", "rb") as file:
data = file.read()
print(f"File size: {len(data)} bytes")
# 追加写入
with open("output.txt", "a") as file:
file.write("This is an appended line.\n")
# 创建新文件
with open("new_file.txt", "x") as file:
file.write("This is a new file.\n")
8.2 标准输入输出
Java 标准输入输出
// 标准输出
System.out.println("Hello, World!");
System.out.print("Enter your name: ");
// 标准输入
Scanner scanner = new Scanner(System.in);
String name = scanner.nextLine();
int age = scanner.nextInt();
System.out.println("Hello, " + name + ", you are " + age + " years old.");
Python 标准输入输出
# 标准输出
print("Hello, World!")
print("Enter your name: ", end="") # 不换行
# 标准输入
name = input("Enter your name: ")
age = int(input("Enter your age: "))
print(f"Hello, {name}, you are {age} years old.")
对比:
| 操作 | Java | Python |
|---|---|---|
| 标准输出 | System.out.println() | print() |
| 不换行输出 | System.out.print() | print(..., end="") |
| 标准输入 | Scanner 类 | input() 函数 |
| 输入转换 | 需要手动转换 | 使用类型函数转换 |
8.3 序列化与反序列化
Java 序列化
// 序列化
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.ser"))) {
Person person = new Person("Alice", 25);
oos.writeObject(person);
} catch (IOException e) {
e.printStackTrace();
}
// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.ser"))) {
Person person = (Person) ois.readObject();
System.out.println(person.getName() + ", " + person.getAge());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
Python 序列化
import pickle
# 序列化
data = {"name": "Alice", "age": 25, "city": "New York"}
with open("data.pkl", "wb") as file:
pickle.dump(data, file)
# 反序列化
with open("data.pkl", "rb") as file:
loaded_data = pickle.load(file)
print(loaded_data)
# 使用 json 模块
import json
# 序列化
data = {"name": "Alice", "age": 25, "city": "New York"}
with open("data.json", "w") as file:
json.dump(data, file, indent=2)
# 反序列化
with open("data.json", "r") as file:
loaded_data = json.load(file)
print(loaded_data)
对比:
| 特性 | Java | Python |
|---|---|---|
| 序列化机制 | ObjectOutputStream | pickle 模块 |
| 反序列化机制 | ObjectInputStream | pickle 模块 |
| 跨语言支持 | 仅 Java | json 模块支持跨语言 |
| 类要求 | 类必须实现 Serializable 接口 | 几乎所有对象都可以序列化 |
| 安全性 | 可能存在安全问题 | pickle 可能存在安全问题,json 更安全 |
8.4 目录操作
Java 目录操作
// 创建目录
File dir = new File("new_dir");
if (!dir.exists()) {
dir.mkdir();
}
// 列出目录内容
File[] files = dir.listFiles();
for (File file : files) {
if (file.isFile()) {
System.out.println("File: " + file.getName());
} else if (file.isDirectory()) {
System.out.println("Directory: " + file.getName());
}
}
// 删除目录
if (dir.exists()) {
dir.delete();
}
Python 目录操作
import os
# 创建目录
os.makedirs("new_dir", exist_ok=True) # exist_ok=True 避免目录已存在时出错
# 列出目录内容
for item in os.listdir("new_dir"):
item_path = os.path.join("new_dir", item)
if os.path.isfile(item_path):
print(f"File: {item}")
elif os.path.isdir(item_path):
print(f"Directory: {item}")
# 删除目录
import shutil
shutil.rmtree("new_dir", ignore_errors=True)
# 路径操作
print(os.path.abspath(".")) # 当前目录绝对路径
print(os.path.join("dir1", "dir2", "file.txt")) # 拼接路径
print(os.path.exists("example.txt")) # 检查文件是否存在
print(os.path.isfile("example.txt")) # 检查是否是文件
print(os.path.isdir("new_dir")) # 检查是否是目录
对比:
| 操作 | Java | Python |
|---|---|---|
| 创建目录 | mkdir() | os.makedirs() |
| 列出目录 | listFiles() | os.listdir() |
| 删除目录 | delete() | shutil.rmtree() |
| 路径操作 | File 类方法 | os.path 模块函数 |
| 检查文件存在 | exists() | os.path.exists() |
8.5 二进制文件操作
Java 二进制文件操作
try (FileInputStream fis = new FileInputStream("image.jpg")) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
// 处理数据
}
} catch (IOException e) {
e.printStackTrace();
}
try (FileOutputStream fos = new FileOutputStream("copy.jpg")) {
byte[] data = new byte[1024];
// 写入数据
fos.write(data);
} catch (IOException e) {
e.printStackTrace();
}
Python 二进制文件操作
# 读取二进制文件
with open("image.jpg", "rb") as file:
data = file.read()
print(f"File size: {len(data)} bytes")
# 写入二进制文件
with open("copy.jpg", "wb") as file:
file.write(data)
# 分块读取大文件
with open("large_file.bin", "rb") as file:
while True:
chunk = file.read(1024) # 每次读取 1024 字节
if not chunk:
break
# 处理 chunk
对比:
| 操作 | Java | Python |
|---|---|---|
| 打开模式 | FileInputStream/FileOutputStream | open("file", "rb")/open("file", "wb") |
| 读取方式 | 使用字节数组缓冲区 | 直接读取或分块读取 |
| 写入方式 | 写入字节数组 | 写入字节对象 |
8.6 临时文件操作
Java 临时文件操作
try {
File tempFile = File.createTempFile("prefix", ".txt");
tempFile.deleteOnExit(); // JVM 退出时删除
try (FileWriter writer = new FileWriter(tempFile)) {
writer.write("Temporary data");
}
// 使用临时文件
System.out.println("Temp file: " + tempFile.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
Python 临时文件操作
import tempfile
# 创建临时文件
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as temp:
temp.write("Temporary data\n")
temp_path = temp.name
print(f"Temp file: {temp_path}")
# 使用临时文件
with open(temp_path, 'r') as file:
content = file.read()
print(content)
# 手动删除
import os
os.unlink(temp_path)
# 临时目录
with tempfile.TemporaryDirectory() as temp_dir:
print(f"Temp directory: {temp_dir}")
# 在临时目录中创建文件
file_path = os.path.join(temp_dir, "test.txt")
with open(file_path, 'w') as file:
file.write("Test data")
# 临时目录自动删除
对比:
| 操作 | Java | Python |
|---|---|---|
| 临时文件 | File.createTempFile() | tempfile.NamedTemporaryFile() |
| 临时目录 | 需要手动创建 | tempfile.TemporaryDirectory() |
| 自动删除 | deleteOnExit() | with 语句自动删除 |
8.7 IO 异常处理
Java IO 异常处理
try {
// IO 操作
} catch (FileNotFoundException e) {
System.err.println("File not found: " + e.getMessage());
} catch (IOException e) {
System.err.println("IO error: " + e.getMessage());
} finally {
// 清理资源
}
Python IO 异常处理
try:
# IO 操作
with open("nonexistent.txt", "r") as file:
content = file.read()
except FileNotFoundError:
print("File not found")
except IOError as e:
print(f"IO error: {e}")
except Exception as e:
print(f"Error: {e}")
对比:
| 异常类型 | Java | Python |
|---|---|---|
| 文件未找到 | FileNotFoundException | FileNotFoundError |
| IO 错误 | IOException | IOError |
| 异常处理 | 必须捕获检查型异常 | 可以捕获运行时异常 |
8.8 练习
- 文件读写练习:编写程序,读取一个文本文件,统计其中单词的出现次数
- 序列化练习:使用
pickle模块序列化一个对象并保存到文件,然后读取并反序列化 - 目录操作练习:编写程序,列出指定目录下的所有文件和子目录
- 二进制文件练习:编写程序,复制一个图片文件
- 临时文件练习:使用
tempfile模块创建临时文件并写入数据 - 综合练习:编写程序,读取一个 CSV 文件,处理数据后写入新的 CSV 文件
8.9 小结
- Python 的文件操作比 Java 更简洁,使用
with语句自动管理资源 - Python 的
open()函数支持多种模式,包括文本和二进制模式 - Python 的
input()函数比 Java 的Scanner更简单 - Python 的序列化支持
pickle(Python 专用)和json(跨语言) - Python 的目录操作使用
os和shutil模块,比 Java 的File类更灵活 - Python 的临时文件操作使用
tempfile模块,比 Java 更方便 - Python 的异常处理更简洁,不需要捕获检查型异常
通过本章的学习,你已经了解了 Java 和 Python 在 IO 操作上的主要区别。接下来,我们将学习 Python 的集合与数据结构。