文章目录
- [Python 文件操作详解与代码示例](#Python 文件操作详解与代码示例)
-
- 一、文件操作基础
-
- [1. 打开和关闭文件](#1. 打开和关闭文件)
- [2. 文件读取方法](#2. 文件读取方法)
- [3. 文件写入方法](#3. 文件写入方法)
- 二、高级文件操作
-
- [1. 文件路径操作](#1. 文件路径操作)
- [2. 上下文管理和文件操作](#2. 上下文管理和文件操作)
- [3. 文件编码和格式处理](#3. 文件编码和格式处理)
- 三、实际应用示例
-
- [1. 日志文件分析器](#1. 日志文件分析器)
- [2. 配置文件管理器](#2. 配置文件管理器)
- [3. 文件备份和同步工具](#3. 文件备份和同步工具)
- 四、最佳实践和性能优化
- 总结
-
- Python文件操作要点:
- [📝 核心操作与代码示例](#📝 核心操作与代码示例)
-
- [1. 基础文本文件读写](#1. 基础文本文件读写)
- [2. 二进制文件读写](#2. 二进制文件读写)
- [3. 文件指针与随机访问](#3. 文件指针与随机访问)
- [🚀 现代C++的增强特性](#🚀 现代C++的增强特性)
-
- [1. 使用 `std::filesystem` (C++17)](#1. 使用
std::filesystem(C++17)) - [2. 结合 `std::string_view` 和 `std::format` (C++17/20)](#2. 结合
std::string_view和std::format(C++17/20))
- [1. 使用 `std::filesystem` (C++17)](#1. 使用
- [💡 最佳实践与要点](#💡 最佳实践与要点)
Python 文件操作详解与代码示例
一、文件操作基础
1. 打开和关闭文件
python
# ==================== 基本的文件打开和关闭 ====================
def basic_file_operations():
print("=== 基本文件操作 ===")
# 1. 使用 open() 函数打开文件
# 模式说明:
# 'r' - 只读(默认)
# 'w' - 写入,会覆盖已有内容
# 'a' - 追加,在文件末尾添加内容
# 'x' - 创建新文件,如果文件已存在则失败
# 'b' - 二进制模式
# 't' - 文本模式(默认)
# '+' - 读写模式
# 方法1:传统的打开和关闭方式
print("方法1:传统方式")
file = None
try:
# 打开文件进行读取
file = open('test.txt', 'w', encoding='utf-8') # 写入模式
file.write("Hello, World!\n")
file.write("这是测试文件\n")
print("文件写入成功")
except IOError as e:
print(f"文件操作错误: {e}")
finally:
# 确保文件被关闭
if file and not file.closed:
file.close()
print("文件已关闭")
# 方法2:使用 with 语句(推荐)
# with 语句会自动管理文件资源,确保文件被正确关闭
print("\n方法2:使用 with 语句(推荐)")
try:
with open('test.txt', 'r', encoding='utf-8') as file:
content = file.read()
print(f"文件内容:\n{content}")
except FileNotFoundError:
print("文件不存在")
except IOError as e:
print(f"读取文件时出错: {e}")
# 2. 不同的打开模式
print("\n=== 不同的打开模式 ===")
# 写入模式(如果文件不存在则创建,存在则覆盖)
with open('example.txt', 'w', encoding='utf-8') as f:
f.write("第一行\n")
f.write("第二行\n")
# 追加模式
with open('example.txt', 'a', encoding='utf-8') as f:
f.write("追加的内容\n")
# 读取模式
with open('example.txt', 'r', encoding='utf-8') as f:
print(f"example.txt 内容:\n{f.read()}")
# 读写模式(r+)
with open('example.txt', 'r+', encoding='utf-8') as f:
content = f.read()
f.write("使用r+模式添加的内容\n")
print(f"r+模式读取的内容:\n{content}")
# 创建新文件模式(x)
try:
with open('new_file.txt', 'x', encoding='utf-8') as f:
f.write("这是一个新文件\n")
print("新文件创建成功")
except FileExistsError:
print("文件已存在,x模式会失败")
# 3. 检查文件状态
print("\n=== 文件状态检查 ===")
file_path = 'example.txt'
# 检查文件是否存在
import os
if os.path.exists(file_path):
print(f"文件 {file_path} 存在")
# 获取文件大小
file_size = os.path.getsize(file_path)
print(f"文件大小: {file_size} 字节")
# 获取文件修改时间
import time
mod_time = os.path.getmtime(file_path)
print(f"最后修改时间: {time.ctime(mod_time)}")
# 检查是否为文件
print(f"是否为文件: {os.path.isfile(file_path)}")
# 检查是否为目录
print(f"是否为目录: {os.path.isdir(file_path)}")
else:
print(f"文件 {file_path} 不存在")
# 4. 文件属性
print("\n=== 文件属性 ===")
with open('example.txt', 'r', encoding='utf-8') as f:
print(f"文件名称: {f.name}")
print(f"文件模式: {f.mode}")
print(f"文件编码: {f.encoding}")
print(f"文件是否已关闭: {f.closed}")
print(f"with语句块外文件是否已关闭: {f.closed}")
basic_file_operations()
2. 文件读取方法
python
# ==================== 文件读取方法 ====================
def file_reading_methods():
print("\n=== 文件读取方法 ===")
# 先创建一个测试文件
with open('data.txt', 'w', encoding='utf-8') as f:
for i in range(1, 11):
f.write(f"第{i}行: 这是测试数据 {i}\n")
print("测试文件 data.txt 已创建\n")
# 1. read() - 读取整个文件
print("1. read() - 读取整个文件:")
with open('data.txt', 'r', encoding='utf-8') as f:
content = f.read()
print(f"读取前5个字符: {content[:50]}...")
print(f"总字符数: {len(content)}")
# 2. read(size) - 读取指定大小的内容
print("\n2. read(size) - 读取指定大小:")
with open('data.txt', 'r', encoding='utf-8') as f:
# 读取前100个字符
chunk = f.read(100)
print(f"读取100个字符:\n{chunk}")
# 继续读取下一个100字符
chunk2 = f.read(100)
print(f"继续读取100个字符:\n{chunk2}")
# 检查文件指针位置
position = f.tell()
print(f"当前文件指针位置: {position}")
# 回到文件开头
f.seek(0)
print(f"回到开头后指针位置: {f.tell()}")
# 3. readline() - 读取一行
print("\n3. readline() - 逐行读取:")
with open('data.txt', 'r', encoding='utf-8') as f:
line1 = f.readline()
line2 = f.readline()
print(f"第一行: {line1.strip()}")
print(f"第二行: {line2.strip()}")
# 4. readlines() - 读取所有行到列表
print("\n4. readlines() - 读取所有行到列表:")
with open('data.txt', 'r', encoding='utf-8') as f:
lines = f.readlines()
print(f"总行数: {len(lines)}")
print(f"前3行:")
for i, line in enumerate(lines[:3], 1):
print(f" 行{i}: {line.strip()}")
# 5. 遍历文件对象(逐行读取,内存高效)
print("\n5. 遍历文件对象(内存高效):")
with open('data.txt', 'r', encoding='utf-8') as f:
line_count = 0
for line in f:
line_count += 1
if line_count <= 3:
print(f" 行{line_count}: {line.strip()}")
print(f" 总行数: {line_count}")
# 6. 使用 with 语句和异常处理
print("\n6. 文件读取的异常处理:")
def read_file_safely(filename):
"""安全读取文件的函数"""
try:
with open(filename, 'r', encoding='utf-8') as f:
return f.read()
except FileNotFoundError:
return f"错误: 文件 '{filename}' 不存在"
except PermissionError:
return f"错误: 没有权限读取文件 '{filename}'"
except UnicodeDecodeError:
return f"错误: 文件 '{filename}' 编码问题"
except Exception as e:
return f"错误: 读取文件时发生未知错误 - {e}"
# 测试各种情况
print(read_file_safely('data.txt'))
print(read_file_safely('nonexistent.txt'))
# 7. 大文件读取策略
print("\n7. 大文件读取策略:")
def process_large_file(filename, chunk_size=1024):
"""分块读取大文件"""
print(f"分块读取 {filename},每块 {chunk_size} 字节")
with open(filename, 'r', encoding='utf-8') as f:
chunk_count = 0
total_chars = 0
while True:
chunk = f.read(chunk_size)
if not chunk: # 读取完毕
break
chunk_count += 1
total_chars += len(chunk)
if chunk_count <= 2: # 只显示前两块
print(f" 第{chunk_count}块({len(chunk)}字符): {chunk[:50]}...")
print(f" 总共读取 {chunk_count} 块,{total_chars} 字符")
# 创建一个稍大的文件
with open('large_data.txt', 'w', encoding='utf-8') as f:
for i in range(100):
f.write(f"这是第{i+1}行数据,包含一些测试内容以便生成足够长的行。\n")
process_large_file('large_data.txt', 200)
# 8. 带缓冲区的读取
print("\n8. 带缓冲区的读取:")
# 创建二进制文件
with open('binary_data.bin', 'wb') as f:
f.write(b'Hello' * 1000) # 5000字节的二进制数据
# 使用缓冲区读取
buffer_size = 512
with open('binary_data.bin', 'rb') as f:
chunk = f.read(buffer_size)
print(f"读取 {len(chunk)} 字节: {chunk[:20]}...")
# 9. 读取特定位置的内容
print("\n9. 读取特定位置的内容:")
with open('data.txt', 'r', encoding='utf-8') as f:
# 跳转到第50个字节
f.seek(50)
content = f.read(50)
print(f"从位置50读取50字节:\n{content}")
# 获取当前位置
current = f.tell()
print(f"当前位置: {current}")
# 相对当前位置移动
f.seek(-20, 1) # 从当前位置向前移动20字节
print(f"向前移动20字节后位置: {f.tell()}")
# 从文件末尾移动
f.seek(-30, 2) # 从文件末尾向前移动30字节
print(f"从文件末尾向前30字节位置: {f.tell()}")
print(f"最后30字节内容: {f.read()}")
file_reading_methods()
3. 文件写入方法
python
# ==================== 文件写入方法 ====================
def file_writing_methods():
print("\n=== 文件写入方法 ===")
# 1. write() - 写入字符串
print("1. write() - 写入字符串:")
with open('write_test.txt', 'w', encoding='utf-8') as f:
f.write("这是第一行\n")
f.write("这是第二行\n")
f.write("数字: " + str(123) + "\n")
f.write(f"格式化字符串: {3.14:.2f}\n")
# 验证写入内容
with open('write_test.txt', 'r', encoding='utf-8') as f:
print(f"写入的内容:\n{f.read()}")
# 2. writelines() - 写入字符串列表
print("\n2. writelines() - 写入字符串列表:")
lines = [
"使用writelines写入\n",
"这是列表中的第一行\n",
"这是列表中的第二行\n",
"不需要手动添加换行符,但列表中的字符串可以包含\n"
]
with open('writelines_test.txt', 'w', encoding='utf-8') as f:
f.writelines(lines)
# 验证
with open('writelines_test.txt', 'r', encoding='utf-8') as f:
print(f"writelines写入的内容:\n{f.read()}")
# 3. 追加模式
print("\n3. 追加模式:")
with open('append_test.txt', 'w', encoding='utf-8') as f:
f.write("初始内容\n")
# 多次追加
for i in range(3):
with open('append_test.txt', 'a', encoding='utf-8') as f:
f.write(f"第{i+1}次追加的内容\n")
with open('append_test.txt', 'r', encoding='utf-8') as f:
print(f"追加后的内容:\n{f.read()}")
# 4. 读写模式(r+)
print("\n4. 读写模式(r+):")
with open('rplus_test.txt', 'w', encoding='utf-8') as f:
f.write("第一行\n第二行\n第三行\n")
with open('rplus_test.txt', 'r+', encoding='utf-8') as f:
# 读取内容
content = f.read()
print(f"原始内容:\n{content}")
# 移动到文件开头
f.seek(0)
# 写入新内容(会覆盖原有内容)
f.write("新第一行\n")
# 移动到文件末尾
f.seek(0, 2)
# 追加内容
f.write("追加的内容\n")
with open('rplus_test.txt', 'r', encoding='utf-8') as f:
print(f"修改后的内容:\n{f.read()}")
# 5. 二进制写入
print("\n5. 二进制写入:")
# 创建一些二进制数据
binary_data = bytes(range(256)) # 0-255的字节
with open('binary_write.bin', 'wb') as f:
f.write(binary_data)
# 读取验证
with open('binary_write.bin', 'rb') as f:
read_data = f.read(10) # 读取前10个字节
print(f"前10个字节: {read_data}")
print(f"文件大小: {len(binary_data)} 字节")
# 6. 结构化数据写入
print("\n6. 结构化数据写入:")
# 写入CSV格式
data = [
["姓名", "年龄", "城市"],
["张三", "25", "北京"],
["李四", "30", "上海"],
["王五", "28", "广州"]
]
with open('data.csv', 'w', encoding='utf-8') as f:
for row in data:
f.write(','.join(row) + '\n')
print("CSV文件已创建")
# 7. 使用缓冲写入
print("\n7. 使用缓冲写入:")
# 创建一个大量数据的文件
with open('buffered_write.txt', 'w', encoding='utf-8', buffering=1024*1024) as f: # 1MB缓冲
for i in range(10000):
f.write(f"这是第{i+1}行数据\n")
print("带缓冲的大文件写入完成")
# 8. 检查写入是否成功
print("\n8. 检查写入是否成功:")
def safe_write(filename, content, mode='w'):
"""安全写入文件,返回是否成功"""
try:
with open(filename, mode, encoding='utf-8') as f:
written = f.write(content)
print(f"成功写入 {written} 字符到 {filename}")
return True
except PermissionError:
print(f"错误: 没有权限写入文件 {filename}")
return False
except Exception as e:
print(f"错误: 写入文件时发生错误 - {e}")
return False
# 测试安全写入
success = safe_write('safe_test.txt', '测试内容')
if success:
with open('safe_test.txt', 'r', encoding='utf-8') as f:
print(f"验证内容: {f.read()}")
# 9. 文件锁(在并发环境中)
print("\n9. 文件锁示例:")
import os
def append_with_lock(filename, content):
"""使用文件锁进行追加写入"""
try:
# 打开文件并获取锁
with open(filename, 'a', encoding='utf-8') as f:
# 在Unix/Linux上可以使用fcntl,这里简单模拟
f.write(f"[进程{os.getpid()}] {content}\n")
f.flush() # 确保立即写入
os.fsync(f.fileno()) # 强制同步到磁盘
return True
except Exception as e:
print(f"写入失败: {e}")
return False
append_with_lock('locked_file.txt', '第一条消息')
append_with_lock('locked_file.txt', '第二条消息')
with open('locked_file.txt', 'r', encoding='utf-8') as f:
print(f"锁定文件内容:\n{f.read()}")
file_writing_methods()
二、高级文件操作
1. 文件路径操作
python
# ==================== 文件路径操作 ====================
def file_path_operations():
print("\n=== 文件路径操作 ===")
import os
from pathlib import Path # Python 3.4+ 推荐使用
# 1. 使用 os.path 模块
print("1. 使用 os.path 模块:")
# 当前工作目录
cwd = os.getcwd()
print(f"当前工作目录: {cwd}")
# 路径拼接
file_path = os.path.join(cwd, 'data', 'files', 'example.txt')
print(f"拼接后的路径: {file_path}")
# 路径分解
dir_name = os.path.dirname(file_path)
base_name = os.path.basename(file_path)
print(f"目录部分: {dir_name}")
print(f"文件名部分: {base_name}")
# 获取文件名和扩展名
file_name, file_ext = os.path.splitext(base_name)
print(f"文件名(不含扩展名): {file_name}")
print(f"文件扩展名: {file_ext}")
# 标准化路径
norm_path = os.path.normpath('/usr/local/../bin/./python')
print(f"标准化路径: {norm_path}")
# 绝对路径
abs_path = os.path.abspath('example.txt')
print(f"绝对路径: {abs_path}")
# 相对路径
rel_path = os.path.relpath('/usr/local/bin', '/usr')
print(f"相对路径: {rel_path}")
# 检查路径是否存在
exists = os.path.exists('example.txt')
print(f"文件是否存在: {exists}")
# 2. 使用 pathlib(现代方式)
print("\n2. 使用 pathlib(现代方式):")
# 创建Path对象
p = Path('example.txt')
print(f"Path对象: {p}")
print(f"绝对路径: {p.absolute()}")
print(f"父目录: {p.parent}")
print(f"文件名: {p.name}")
print(f"文件名(不含扩展名): {p.stem}")
print(f"扩展名: {p.suffix}")
# 路径操作
new_path = Path('data') / 'files' / 'example.txt'
print(f"新路径: {new_path}")
# 检查文件属性
print(f"是否存在: {p.exists()}")
print(f"是否是文件: {p.is_file()}")
print(f"是否是目录: {p.is_dir()}")
# 3. 遍历目录
print("\n3. 遍历目录:")
# 创建一些测试文件和目录
test_dir = Path('test_dir')
test_dir.mkdir(exist_ok=True)
# 创建子目录和文件
(test_dir / 'subdir1').mkdir(exist_ok=True)
(test_dir / 'subdir2').mkdir(exist_ok=True)
(test_dir / 'file1.txt').write_text('文件1内容')
(test_dir / 'file2.txt').write_text('文件2内容')
(test_dir / 'subdir1' / 'subfile.txt').write_text('子目录文件内容')
# 列出目录内容
print(f"{test_dir} 目录内容:")
for item in test_dir.iterdir():
if item.is_file():
print(f" 文件: {item.name} ({item.stat().st_size} 字节)")
elif item.is_dir():
print(f" 目录: {item.name}")
# 递归遍历
print(f"\n递归遍历 {test_dir}:")
for item in test_dir.rglob('*'):
rel_path = item.relative_to(test_dir)
print(f" {rel_path}")
# 4. 文件通配符匹配
print("\n4. 文件通配符匹配:")
# 创建一些.txt和.csv文件
for i in range(3):
(test_dir / f'document{i}.txt').write_text(f'文本文件{i}')
(test_dir / f'data{i}.csv').write_text(f'CSV文件{i}')
# 使用glob匹配
print("所有.txt文件:")
for txt_file in test_dir.glob('*.txt'):
print(f" {txt_file.name}")
print("\n所有.csv文件:")
for csv_file in test_dir.glob('*.csv'):
print(f" {csv_file.name}")
print("\n递归查找所有.txt文件:")
for txt_file in test_dir.rglob('*.txt'):
print(f" {txt_file.relative_to(test_dir)}")
# 5. 路径操作实际应用
print("\n5. 路径操作实际应用:")
def backup_file(file_path):
"""创建文件备份"""
path = Path(file_path)
if not path.exists():
print(f"文件 {file_path} 不存在")
return
# 生成备份文件名
timestamp = time.strftime("%Y%m%d_%H%M%S")
backup_name = f"{path.stem}_backup_{timestamp}{path.suffix}"
backup_path = path.parent / backup_name
try:
# 复制文件
import shutil
shutil.copy2(path, backup_path)
print(f"已创建备份: {backup_path}")
except Exception as e:
print(f"备份失败: {e}")
# 测试备份功能
backup_file('example.txt')
# 6. 临时文件和目录
print("\n6. 临时文件和目录:")
import tempfile
# 创建临时文件
with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as tmp:
tmp.write("临时文件内容\n")
tmp_name = tmp.name
print(f"临时文件: {tmp_name}")
# 读取临时文件
with open(tmp_name, 'r') as f:
print(f"临时文件内容: {f.read()}")
# 删除临时文件
os.unlink(tmp_name)
print("临时文件已删除")
# 创建临时目录
with tempfile.TemporaryDirectory() as tmpdir:
print(f"临时目录: {tmpdir}")
temp_file = Path(tmpdir) / 'test.txt'
temp_file.write_text('临时目录中的文件')
print(f"临时文件内容: {temp_file.read_text()}")
print("临时目录已自动删除")
# 清理测试目录
import shutil
shutil.rmtree(test_dir, ignore_errors=True)
file_path_operations()
2. 上下文管理和文件操作
python
# ==================== 上下文管理和文件操作 ====================
import time
def context_management():
print("\n=== 上下文管理和文件操作 ===")
# 1. 自定义上下文管理器类
print("1. 自定义上下文管理器类:")
class FileLogger:
"""自定义文件日志上下文管理器"""
def __init__(self, filename, mode='a'):
self.filename = filename
self.mode = mode
self.file = None
self.start_time = None
def __enter__(self):
"""进入上下文时调用"""
self.start_time = time.time()
self.file = open(self.filename, self.mode, encoding='utf-8')
self.file.write(f"=== 日志开始于 {time.ctime(self.start_time)} ===\n")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""退出上下文时调用"""
end_time = time.time()
duration = end_time - self.start_time
if exc_type is not None:
# 如果有异常发生
self.file.write(f"[ERROR] 异常: {exc_val}\n")
self.file.write(f"[ERROR] 耗时: {duration:.2f}秒\n")
else:
# 正常结束
self.file.write(f"=== 日志结束于 {time.ctime(end_time)} ===\n")
self.file.write(f"=== 总耗时: {duration:.2f}秒 ===\n")
self.file.close()
# 返回False表示异常需要传播,True表示已处理
return False
def log(self, message, level="INFO"):
"""记录日志消息"""
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
self.file.write(f"[{timestamp}] [{level}] {message}\n")
self.file.flush() # 立即写入
# 使用自定义上下文管理器
try:
with FileLogger('app.log') as logger:
logger.log("应用程序启动")
time.sleep(0.1)
logger.log("处理数据...")
# 模拟一个错误
# raise ValueError("测试错误")
logger.log("应用程序正常关闭")
except Exception as e:
print(f"捕获到异常: {e}")
# 读取日志文件
with open('app.log', 'r') as f:
print("应用程序日志:")
print(f.read())
# 2. 使用 contextlib 创建上下文管理器
print("\n2. 使用 contextlib 创建上下文管理器:")
from contextlib import contextmanager
@contextmanager
def timed_file_operation(filename, operation='read'):
"""计时文件操作的上下文管理器"""
start_time = time.time()
file = None
try:
if operation == 'read':
file = open(filename, 'r', encoding='utf-8')
print(f"开始读取文件: {filename}")
yield file
elif operation == 'write':
file = open(filename, 'w', encoding='utf-8')
print(f"开始写入文件: {filename}")
yield file
else:
raise ValueError(f"不支持的操作: {operation}")
finally:
if file:
file.close()
end_time = time.time()
duration = end_time - start_time
print(f"文件操作完成,耗时: {duration:.3f}秒")
# 使用装饰器创建的上下文管理器
with timed_file_operation('example.txt', 'read') as f:
content = f.read()
print(f"读取到 {len(content)} 字符")
# 3. 多重上下文管理器
print("\n3. 多重上下文管理器:")
# 同时打开多个文件
try:
with open('file1.txt', 'w') as f1, open('file2.txt', 'w') as f2:
f1.write("文件1内容\n")
f2.write("文件2内容\n")
print("同时写入两个文件")
except Exception as e:
print(f"错误: {e}")
# 4. 上下文管理器的实际应用
print("\n4. 上下文管理器的实际应用:")
class DatabaseConnection:
"""模拟数据库连接的上下文管理器"""
def __init__(self, db_name):
self.db_name = db_name
self.connected = False
def __enter__(self):
print(f"连接到数据库: {self.db_name}")
self.connected = True
# 模拟连接耗时
time.sleep(0.05)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if self.connected:
print(f"断开数据库连接: {self.db_name}")
self.connected = False
if exc_type:
print(f"数据库操作异常: {exc_val}")
return False
def query(self, sql):
"""执行查询"""
if not self.connected:
raise ConnectionError("数据库未连接")
print(f"执行查询: {sql}")
time.sleep(0.02)
return ["结果1", "结果2", "结果3"]
# 使用数据库上下文管理器
with DatabaseConnection('my_database') as db:
results = db.query("SELECT * FROM users")
print(f"查询结果: {results}")
# 5. 资源清理模式
print("\n5. 资源清理模式:")
class ResourceManager:
"""资源管理器,确保资源被正确清理"""
def __init__(self):
self.resources = []
def add_resource(self, resource, cleanup_func):
"""添加资源及其清理函数"""
self.resources.append((resource, cleanup_func))
return resource
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
# 逆序清理所有资源
for resource, cleanup_func in reversed(self.resources):
try:
cleanup_func(resource)
print(f"已清理资源: {resource}")
except Exception as e:
print(f"清理资源时出错: {e}")
return False
# 使用资源管理器
with ResourceManager() as rm:
# 创建文件资源
file1 = rm.add_resource(open('temp1.txt', 'w'), lambda f: f.close())
file1.write("临时文件1\n")
file2 = rm.add_resource(open('temp2.txt', 'w'), lambda f: f.close())
file2.write("临时文件2\n")
# 模拟其他资源
import socket
sock = rm.add_resource(socket.socket(), lambda s: s.close())
print("资源已添加")
print("所有资源已清理")
context_management()
3. 文件编码和格式处理
python
# ==================== 文件编码和格式处理 ====================
def file_encoding_handling():
print("\n=== 文件编码和格式处理 ===")
# 1. 不同编码的文件操作
print("1. 不同编码的文件操作:")
test_content = "Hello, 世界! 🌍\n这是一条测试消息。\n"
# 使用不同编码写入文件
encodings = ['utf-8', 'utf-16', 'gbk', 'latin-1', 'ascii']
for encoding in encodings:
try:
filename = f'test_{encoding}.txt'
with open(filename, 'w', encoding=encoding) as f:
f.write(test_content)
print(f"使用 {encoding} 编码写入成功")
# 读取验证
with open(filename, 'r', encoding=encoding) as f:
content = f.read()
print(f" {encoding} 读取: {content[:20]}...")
except UnicodeEncodeError as e:
print(f" {encoding} 编码错误: {e}")
except Exception as e:
print(f" {encoding} 其他错误: {e}")
# 2. 自动检测文件编码
print("\n2. 自动检测文件编码:")
import chardet # 需要安装: pip install chardet
def detect_encoding(filename):
"""检测文件编码"""
try:
with open(filename, 'rb') as f:
raw_data = f.read()
result = chardet.detect(raw_data)
return result['encoding'], result['confidence']
except Exception as e:
return None, 0
# 测试编码检测
test_file = 'test_utf-8.txt'
encoding, confidence = detect_encoding(test_file)
print(f"文件 {test_file} 编码: {encoding} (置信度: {confidence:.2%})")
# 3. 处理未知编码的文件
print("\n3. 处理未知编码的文件:")
def read_file_with_fallback(filename):
"""尝试多种编码读取文件"""
encodings_to_try = ['utf-8', 'gbk', 'gb2312', 'big5', 'latin-1']
for encoding in encodings_to_try:
try:
with open(filename, 'r', encoding=encoding) as f:
return f.read(), encoding
except UnicodeDecodeError:
continue
except Exception as e:
print(f"使用 {encoding} 读取时出错: {e}")
continue
# 如果所有编码都失败,尝试二进制读取
with open(filename, 'rb') as f:
return f.read(), 'binary'
content, used_encoding = read_file_with_fallback(test_file)
print(f"使用编码 {used_encoding} 成功读取文件")
print(f"内容预览: {content[:50]}...")
# 4. BOM(字节顺序标记)处理
print("\n4. BOM(字节顺序标记)处理:")
# 写入带BOM的UTF-8文件
bom_content = '\ufeff' + "带BOM的UTF-8文件内容\n"
with open('with_bom.txt', 'w', encoding='utf-8-sig') as f:
f.write(bom_content)
# 读取带BOM的文件
with open('with_bom.txt', 'r', encoding='utf-8-sig') as f:
content = f.read()
print(f"读取带BOM文件: {repr(content[:20])}...")
# 5. 行结束符处理
print("\n5. 行结束符处理:")
# 不同平台的换行符
unix_content = "Unix/Linux换行\n第二行\n"
windows_content = "Windows换行\r\n第二行\r\n"
mac_content = "旧Mac换行\r第二行\r"
# 写入不同换行符的文件
with open('unix.txt', 'w', newline='\n') as f:
f.write(unix_content)
with open('windows.txt', 'w', newline='\r\n') as f:
f.write(windows_content)
# 读取时统一处理换行符
with open('unix.txt', 'r', newline='') as f:
lines = f.readlines()
print(f"Unix文件行数: {len(lines)}")
with open('windows.txt', 'r', newline='') as f:
lines = f.readlines()
print(f"Windows文件行数: {len(lines)}")
# 6. 二进制和文本模式转换
print("\n6. 二进制和文本模式转换:")
# 文本转二进制
text_data = "Hello, World! 你好,世界!"
binary_data = text_data.encode('utf-8')
with open('text_to_binary.bin', 'wb') as f:
f.write(binary_data)
# 二进制转文本
with open('text_to_binary.bin', 'rb') as f:
loaded_binary = f.read()
loaded_text = loaded_binary.decode('utf-8')
print(f"二进制文件解码为文本: {loaded_text}")
# 7. 处理混合编码文件
print("\n7. 处理混合编码文件(如日志文件):")
def read_log_file_safely(filename):
"""安全读取可能包含混合编码的日志文件"""
result_lines = []
with open(filename, 'rb') as f:
for line_num, line_bytes in enumerate(f, 1):
try:
# 尝试UTF-8解码
line = line_bytes.decode('utf-8').rstrip('\n\r')
result_lines.append(line)
except UnicodeDecodeError:
try:
# 尝试其他编码
line = line_bytes.decode('latin-1').rstrip('\n\r')
result_lines.append(f"[编码问题行{line_num}] {line}")
except:
# 如果所有编码都失败,使用替代字符
result_lines.append(f"[二进制行{line_num}] <无法解码>")
return result_lines
# 创建混合编码的测试文件
mixed_content = b"正常UTF-8行\n" + "包含中文的行\n".encode('utf-8') + b"Invalid \x80\x81 bytes\n"
with open('mixed_encoding.log', 'wb') as f:
f.write(mixed_content)
lines = read_log_file_safely('mixed_encoding.log')
print("混合编码文件内容:")
for line in lines:
print(f" {line}")
# 8. CSV文件编码处理
print("\n8. CSV文件编码处理:")
import csv
# 创建包含中文的CSV数据
csv_data = [
['姓名', '年龄', '城市'],
['张三', '25', '北京'],
['李四', '30', '上海'],
['王五', '28', '广州']
]
# 使用UTF-8-BOM写入CSV(Excel兼容)
with open('data_utf8_bom.csv', 'w', encoding='utf-8-sig', newline='') as f:
writer = csv.writer(f)
writer.writerows(csv_data)
# 读取CSV文件
with open('data_utf8_bom.csv', 'r', encoding='utf-8-sig') as f:
reader = csv.reader(f)
print("CSV文件内容:")
for row in reader:
print(f" {row}")
# 9. JSON文件编码
print("\n9. JSON文件编码:")
import json
# 创建包含中文的JSON数据
json_data = {
"name": "张三",
"age": 25,
"city": "北京",
"languages": ["Python", "JavaScript", "中文"]
}
# 写入JSON文件(ensure_ascii=False保留中文)
with open('data.json', 'w', encoding='utf-8') as f:
json.dump(json_data, f, ensure_ascii=False, indent=2)
# 读取JSON文件
with open('data.json', 'r', encoding='utf-8') as f:
loaded_data = json.load(f)
print(f"JSON数据: {loaded_data}")
# 10. 处理大文件的编码问题
print("\n10. 处理大文件的编码问题:")
def process_large_encoded_file(filename, encoding='utf-8', chunk_size=1024):
"""分块处理大编码文件"""
print(f"处理大文件 {filename},编码: {encoding}")
with open(filename, 'r', encoding=encoding) as f:
buffer = ''
chunk_count = 0
char_count = 0
while True:
chunk = f.read(chunk_size)
if not chunk:
break
chunk_count += 1
char_count += len(chunk)
buffer += chunk
# 按行处理缓冲区
while '\n' in buffer:
line, buffer = buffer.split('\n', 1)
if chunk_count <= 2: # 只显示前几行
print(f" 行: {line[:50]}...")
print(f" 总共处理 {chunk_count} 块,{char_count} 字符")
# 创建一个大文件测试
with open('large_encoded.txt', 'w', encoding='utf-8') as f:
for i in range(1000):
f.write(f"第{i+1}行: 这是一些测试数据,包含中文和特殊字符。\n")
process_large_encoded_file('large_encoded.txt')
file_encoding_handling()
三、实际应用示例
1. 日志文件分析器
python
# ==================== 日志文件分析器 ====================
def log_file_analyzer():
print("\n=== 日志文件分析器 ===")
# 创建模拟日志文件
log_content = """2024-01-15 10:15:23 INFO 应用程序启动
2024-01-15 10:15:25 INFO 用户登录成功: user123
2024-01-15 10:16:10 WARNING 磁盘空间不足: 85% used
2024-01-15 10:16:30 ERROR 数据库连接失败
2024-01-15 10:17:05 INFO 尝试重新连接数据库
2024-01-15 10:17:10 INFO 数据库连接恢复
2024-01-15 10:18:45 WARNING 内存使用率: 78%
2024-01-15 10:20:00 INFO 用户登出: user123
2024-01-15 10:20:10 ERROR 文件保存失败: permission denied
2024-01-15 10:20:30 INFO 应用程序关闭
"""
with open('app_server.log', 'w') as f:
f.write(log_content)
# 日志分析类
class LogAnalyzer:
def __init__(self, log_file):
self.log_file = log_file
self.stats = {
'total_lines': 0,
'info': 0,
'warning': 0,
'error': 0,
'by_hour': {},
'messages': []
}
def analyze(self):
"""分析日志文件"""
try:
with open(self.log_file, 'r') as f:
for line in f:
self._process_line(line.strip())
return True
except FileNotFoundError:
print(f"错误: 日志文件 {self.log_file} 不存在")
return False
except Exception as e:
print(f"错误: 分析日志时出错 - {e}")
return False
def _process_line(self, line):
"""处理单行日志"""
if not line:
return
self.stats['total_lines'] += 1
self.stats['messages'].append(line)
# 解析日志级别
if 'INFO' in line:
self.stats['info'] += 1
level = 'INFO'
elif 'WARNING' in line:
self.stats['warning'] += 1
level = 'WARNING'
elif 'ERROR' in line:
self.stats['error'] += 1
level = 'ERROR'
else:
level = 'UNKNOWN'
# 按小时统计
try:
# 提取时间部分
time_part = line.split()[1] # 获取时间部分 "10:15:23"
hour = time_part.split(':')[0] # 获取小时 "10"
if hour not in self.stats['by_hour']:
self.stats['by_hour'][hour] = {'INFO': 0, 'WARNING': 0, 'ERROR': 0}
self.stats['by_hour'][hour][level] += 1
except IndexError:
pass
def print_report(self):
"""打印分析报告"""
print(f"\n日志分析报告: {self.log_file}")
print("=" * 50)
print(f"总计日志行数: {self.stats['total_lines']}")
print(f"INFO 级别: {self.stats['info']}")
print(f"WARNING 级别: {self.stats['warning']}")
print(f"ERROR 级别: {self.stats['error']}")
# 按小时统计
print("\n按小时统计:")
for hour in sorted(self.stats['by_hour'].keys()):
counts = self.stats['by_hour'][hour]
total = sum(counts.values())
print(f" 小时 {hour}:00 - 总计 {total} 条")
for level in ['INFO', 'WARNING', 'ERROR']:
if counts.get(level, 0) > 0:
print(f" {level}: {counts[level]}")
# 错误摘要
print("\n错误摘要:")
error_lines = [msg for msg in self.stats['messages'] if 'ERROR' in msg]
for i, error in enumerate(error_lines[:3], 1): # 显示前3个错误
print(f" 错误{i}: {error}")
if len(error_lines) > 3:
print(f" 还有 {len(error_lines) - 3} 个错误...")
# 建议
print("\n分析建议:")
if self.stats['error'] > 5:
print(" ⚠️ 错误数量较多,建议检查系统状态")
if self.stats['warning'] > 10:
print(" ⚠️ 警告数量较多,建议进行系统优化")
if self.stats['info'] < self.stats['total_lines'] * 0.5:
print(" ℹ️ 正常日志比例较低,可能需要调整日志级别")
# 保存报告到文件
self._save_report()
def _save_report(self):
"""保存分析报告到文件"""
report_file = f"{self.log_file}_analysis.txt"
with open(report_file, 'w') as f:
f.write(f"日志分析报告: {self.log_file}\n")
f.write(f"生成时间: {time.ctime()}\n")
f.write("=" * 50 + "\n")
f.write(f"总计日志行数: {self.stats['total_lines']}\n")
f.write(f"INFO 级别: {self.stats['info']}\n")
f.write(f"WARNING 级别: {self.stats['warning']}\n")
f.write(f"ERROR 级别: {self.stats['error']}\n")
print(f"\n详细报告已保存到: {report_file}")
# 使用日志分析器
analyzer = LogAnalyzer('app_server.log')
if analyzer.analyze():
analyzer.print_report()
# 实时日志监控(简化版)
print("\n=== 实时日志监控(简化版) ===")
def tail_log_file(filename, n=10):
"""模拟tail命令,显示文件最后n行"""
try:
with open(filename, 'r') as f:
# 高效获取最后n行的方法
lines = []
buffer_size = 8192
f.seek(0, 2) # 移动到文件末尾
file_size = f.tell()
buffer = ''
position = file_size
while position > 0 and len(lines) < n:
# 计算要读取的大小
size_to_read = min(buffer_size, position)
position -= size_to_read
# 读取数据块
f.seek(position)
chunk = f.read(size_to_read)
# 处理缓冲区
buffer = chunk + buffer
# 分割行
while '\n' in buffer:
# 从后面前进
pos = buffer.rfind('\n')
if pos != -1:
line = buffer[pos+1:]
if line.strip():
lines.append(line)
buffer = buffer[:pos]
# 添加剩余的缓冲区内容
if buffer.strip():
lines.append(buffer)
# 反转行顺序
lines.reverse()
print(f"最后 {min(n, len(lines))} 行日志:")
for i, line in enumerate(lines[-n:], 1):
print(f" {i:2d}: {line.strip()}")
except Exception as e:
print(f"读取日志失败: {e}")
tail_log_file('app_server.log', 5)
log_file_analyzer()
2. 配置文件管理器
python
# ==================== 配置文件管理器 ====================
def config_file_manager():
print("\n=== 配置文件管理器 ===")
import json
import configparser
import yaml # 需要安装: pip install pyyaml
# 1. INI配置文件处理
print("1. INI配置文件处理:")
# 创建configparser对象
config = configparser.ConfigParser()
# 添加配置节和选项
config['DATABASE'] = {
'host': 'localhost',
'port': '5432',
'username': 'admin',
'password': 'secret123',
'database': 'mydb'
}
config['APPLICATION'] = {
'debug': 'true',
'log_level': 'INFO',
'max_connections': '100',
'timeout': '30'
}
config['PATHS'] = {
'data_dir': '/var/data',
'log_dir': '/var/logs',
'cache_dir': '/tmp/cache'
}
# 写入INI文件
with open('config.ini', 'w') as f:
config.write(f)
print("INI配置文件已创建: config.ini")
# 读取INI文件
config_read = configparser.ConfigParser()
config_read.read('config.ini')
print("\n读取的配置:")
for section in config_read.sections():
print(f"\n[{section}]")
for key, value in config_read.items(section):
print(f" {key} = {value}")
# 获取特定值
db_host = config_read.get('DATABASE', 'host')
debug_mode = config_read.getboolean('APPLICATION', 'debug')
max_conn = config_read.getint('APPLICATION', 'max_connections')
print(f"\n特定配置值:")
print(f" 数据库主机: {db_host}")
print(f" 调试模式: {debug_mode}")
print(f" 最大连接数: {max_conn}")
# 2. JSON配置文件处理
print("\n2. JSON配置文件处理:")
json_config = {
"database": {
"host": "localhost",
"port": 5432,
"credentials": {
"username": "admin",
"password": "secret123"
},
"connections": {
"max": 100,
"timeout": 30
}
},
"application": {
"name": "MyApp",
"version": "1.0.0",
"debug": True,
"settings": {
"log_level": "INFO",
"cache_enabled": True,
"cache_size": 1000
}
},
"features": ["auth", "api", "dashboard", "reports"]
}
# 写入JSON文件
with open('config.json', 'w', encoding='utf-8') as f:
json.dump(json_config, f, indent=2, ensure_ascii=False)
print("JSON配置文件已创建: config.json")
# 读取JSON文件
with open('config.json', 'r', encoding='utf-8') as f:
loaded_json_config = json.load(f)
print(f"\n应用名称: {loaded_json_config['application']['name']}")
print(f"版本: {loaded_json_config['application']['version']}")
print(f"功能列表: {', '.join(loaded_json_config['features'])}")
# 3. YAML配置文件处理
print("\n3. YAML配置文件处理:")
yaml_config = """
# 应用配置
application:
name: "MyApp"
version: "1.0.0"
debug: true
port: 8080
# 数据库配置
database:
primary:
host: "db1.example.com"
port: 5432
database: "production"
replica:
host: "db2.example.com"
port: 5432
database: "replica"
# 特性开关
features:
- name: "authentication"
enabled: true
- name: "analytics"
enabled: false
- name: "notifications"
enabled: true
# 服务端点
endpoints:
api: "/api/v1"
docs: "/docs"
health: "/health"
"""
# 写入YAML文件
with open('config.yaml', 'w') as f:
f.write(yaml_config)
print("YAML配置文件已创建: config.yaml")
# 读取YAML文件
try:
with open('config.yaml', 'r') as f:
loaded_yaml = yaml.safe_load(f)
print(f"\n应用端口: {loaded_yaml['application']['port']}")
print(f"主数据库: {loaded_yaml['database']['primary']['host']}")
print(f"启用的特性:")
for feature in loaded_yaml['features']:
if feature['enabled']:
print(f" - {feature['name']}")
except ImportError:
print("注意: 需要安装pyyaml库才能读取YAML文件")
# 4. 环境变量配置文件
print("\n4. 环境变量配置文件 (.env):")
env_content = """# 数据库配置
DB_HOST=localhost
DB_PORT=5432
DB_NAME=mydb
DB_USER=admin
DB_PASSWORD=secret123
# 应用配置
APP_DEBUG=true
APP_PORT=8080
APP_SECRET_KEY=my-secret-key
# 第三方API
API_KEY=abcdef123456
API_SECRET=xyz789
# 路径配置
LOG_LEVEL=INFO
LOG_FILE=/var/log/app.log
"""
with open('.env', 'w') as f:
f.write(env_content)
print("环境变量文件已创建: .env")
# 简单的.env文件解析器
def parse_env_file(filename):
"""解析.env文件"""
config = {}
try:
with open(filename, 'r') as f:
for line_num, line in enumerate(f, 1):
line = line.strip()
# 跳过空行和注释
if not line or line.startswith('#'):
continue
# 解析键值对
if '=' in line:
key, value = line.split('=', 1)
config[key.strip()] = value.strip()
else:
print(f"警告: 第{line_num}行格式错误: {line}")
except FileNotFoundError:
print(f"错误: 文件 {filename} 不存在")
return config
env_config = parse_env_file('.env')
print(f"\n解析的环境变量数量: {len(env_config)}")
print(f"数据库主机: {env_config.get('DB_HOST')}")
print(f"应用端口: {env_config.get('APP_PORT')}")
# 5. 配置管理器类
print("\n5. 配置管理器类:")
class ConfigManager:
"""统一的配置管理器"""
def __init__(self):
self.config = {}
self.config_files = {}
def load_config(self, filename):
"""加载配置文件"""
import os
if not os.path.exists(filename):
print(f"错误: 配置文件不存在 - {filename}")
return False
ext = os.path.splitext(filename)[1].lower()
try:
if ext == '.ini':
self._load_ini(filename)
elif ext == '.json':
self._load_json(filename)
elif ext in ['.yaml', '.yml']:
self._load_yaml(filename)
elif ext == '.env':
self._load_env(filename)
else:
print(f"错误: 不支持的文件格式 - {ext}")
return False
self.config_files[filename] = ext
print(f"成功加载配置文件: {filename}")
return True
except Exception as e:
print(f"错误: 加载配置文件失败 - {e}")
return False
def _load_ini(self, filename):
"""加载INI文件"""
parser = configparser.ConfigParser()
parser.read(filename)
for section in parser.sections():
self.config[section] = {}
for key, value in parser.items(section):
# 尝试转换类型
if value.lower() in ('true', 'false'):
self.config[section][key] = value.lower() == 'true'
elif value.isdigit():
self.config[section][key] = int(value)
else:
try:
self.config[section][key] = float(value)
except ValueError:
self.config[section][key] = value
def _load_json(self, filename):
"""加载JSON文件"""
with open(filename, 'r', encoding='utf-8') as f:
self.config.update(json.load(f))
def _load_yaml(self, filename):
"""加载YAML文件"""
try:
import yaml
with open(filename, 'r') as f:
self.config.update(yaml.safe_load(f))
except ImportError:
print("错误: 需要安装pyyaml库才能读取YAML文件")
raise
def _load_env(self, filename):
"""加载.env文件"""
env_config = parse_env_file(filename)
self.config.update(env_config)
def get(self, key, default=None):
"""获取配置值"""
# 支持点分隔的键路径,如 'database.host'
keys = key.split('.')
value = self.config
for k in keys:
if isinstance(value, dict) and k in value:
value = value[k]
else:
return default
return value
def set(self, key, value):
"""设置配置值"""
keys = key.split('.')
config_ref = self.config
# 导航到嵌套字典
for k in keys[:-1]:
if k not in config_ref or not isinstance(config_ref[k], dict):
config_ref[k] = {}
config_ref = config_ref[k]
config_ref[keys[-1]] = value
def save(self, filename=None, format=None):
"""保存配置到文件"""
if not filename and not self.config_files:
print("错误: 没有指定文件名且没有已加载的配置文件")
return False
if filename:
target_file = filename
target_format = format or os.path.splitext(filename)[1].lower()
else:
# 保存到第一个加载的文件
target_file = next(iter(self.config_files))
target_format = self.config_files[target_file]
try:
if target_format == '.ini':
self._save_ini(target_file)
elif target_format == '.json':
self._save_json(target_file)
elif target_format in ['.yaml', '.yml']:
self._save_yaml(target_file)
elif target_format == '.env':
self._save_env(target_file)
else:
print(f"错误: 不支持的文件格式 - {target_format}")
return False
print(f"配置已保存到: {target_file}")
return True
except Exception as e:
print(f"错误: 保存配置失败 - {e}")
return False
def _save_ini(self, filename):
"""保存为INI格式"""
parser = configparser.ConfigParser()
# 将嵌套字典转换为扁平结构
for key, value in self.config.items():
if isinstance(value, dict):
parser[key] = value
else:
if 'DEFAULT' not in parser:
parser['DEFAULT'] = {}
parser['DEFAULT'][key] = str(value)
with open(filename, 'w') as f:
parser.write(f)
def _save_json(self, filename):
"""保存为JSON格式"""
with open(filename, 'w', encoding='utf-8') as f:
json.dump(self.config, f, indent=2, ensure_ascii=False)
def _save_yaml(self, filename):
"""保存为YAML格式"""
try:
import yaml
with open(filename, 'w') as f:
yaml.dump(self.config, f, default_flow_style=False)
except ImportError:
print("错误: 需要安装pyyaml库才能保存YAML文件")
raise
def _save_env(self, filename):
"""保存为.env格式"""
with open(filename, 'w') as f:
for key, value in self.config.items():
if isinstance(value, (dict, list)):
# 跳过复杂类型
continue
f.write(f"{key}={value}\n")
def print_summary(self):
"""打印配置摘要"""
print("\n配置摘要:")
print(f"已加载文件: {list(self.config_files.keys())}")
print(f"配置项数量: {self._count_items(self.config)}")
# 显示一些关键配置
print("\n关键配置项:")
for key in ['database', 'application', 'debug', 'port']:
value = self.get(key)
if value is not None:
print(f" {key}: {value}")
def _count_items(self, config_dict):
"""递归计算配置项数量"""
count = 0
for key, value in config_dict.items():
if isinstance(value, dict):
count += self._count_items(value)
else:
count += 1
return count
# 使用配置管理器
print("\n测试配置管理器:")
manager = ConfigManager()
# 加载JSON配置
manager.load_config('config.json')
# 获取配置值
app_name = manager.get('application.name')
db_port = manager.get('database.port')
print(f"应用名称: {app_name}")
print(f"数据库端口: {db_port}")
# 设置新配置
manager.set('application.version', '1.1.0')
manager.set('new_feature.enabled', True)
# 保存配置
manager.save('config_updated.json')
# 打印摘要
manager.print_summary()
config_file_manager()
3. 文件备份和同步工具
python
# ==================== 文件备份和同步工具 ====================
def file_backup_sync():
print("\n=== 文件备份和同步工具 ===")
import os
import shutil
import hashlib
import time
from pathlib import Path
# 1. 简单文件备份
print("1. 简单文件备份:")
def simple_backup(source_file, backup_dir='backups'):
"""创建文件的简单备份"""
# 确保源文件存在
if not os.path.exists(source_file):
print(f"错误: 源文件不存在 - {source_file}")
return False
# 创建备份目录
os.makedirs(backup_dir, exist_ok=True)
# 生成备份文件名(带时间戳)
source_path = Path(source_file)
timestamp = time.strftime("%Y%m%d_%H%M%S")
backup_name = f"{source_path.stem}_backup_{timestamp}{source_path.suffix}"
backup_path = Path(backup_dir) / backup_name
try:
# 复制文件
shutil.copy2(source_file, backup_path)
print(f"已创建备份: {backup_path}")
# 验证备份
if os.path.getsize(source_file) == os.path.getsize(backup_path):
print("备份验证成功: 文件大小一致")
return True
else:
print("警告: 备份文件大小不一致")
return False
except Exception as e:
print(f"备份失败: {e}")
return False
# 测试简单备份
with open('important_data.txt', 'w') as f:
f.write("这是重要数据\n需要备份的内容\n")
simple_backup('important_data.txt')
# 2. 增量备份(基于文件哈希)
print("\n2. 增量备份(基于文件哈希):")
def calculate_file_hash(filename, algorithm='md5'):
"""计算文件的哈希值"""
hash_func = hashlib.new(algorithm)
try:
with open(filename, 'rb') as f:
# 分块读取大文件
for chunk in iter(lambda: f.read(4096), b''):
hash_func.update(chunk)
return hash_func.hexdigest()
except Exception as e:
print(f"计算哈希失败: {e}")
return None
def incremental_backup(source_file, backup_dir='incremental_backups', max_backups=5):
"""创建增量备份"""
if not os.path.exists(source_file):
print(f"错误: 源文件不存在 - {source_file}")
return None
# 计算当前文件的哈希
current_hash = calculate_file_hash(source_file)
if not current_hash:
return None
# 检查是否需要备份
backup_dir_path = Path(backup_dir)
backup_dir_path.mkdir(exist_ok=True)
# 查找已有的备份
existing_backups = []
for backup_file in backup_dir_path.glob(f"*{Path(source_file).suffix}"):
if 'backup_' in backup_file.name:
existing_backups.append(backup_file)
# 检查是否有相同哈希的备份
for backup in existing_backups:
if calculate_file_hash(backup) == current_hash:
print(f"文件未更改,跳过备份 (哈希: {current_hash[:8]}...)")
return backup
# 创建新备份
source_path = Path(source_file)
timestamp = time.strftime("%Y%m%d_%H%M%S")
backup_name = f"{source_path.stem}_backup_{timestamp}_{current_hash[:8]}{source_path.suffix}"
backup_path = backup_dir_path / backup_name
try:
shutil.copy2(source_file, backup_path)
print(f"创建增量备份: {backup_path.name}")
# 清理旧备份(保留最新的max_backups个)
existing_backups.sort(key=os.path.getmtime, reverse=True)
if len(existing_backups) >= max_backups:
for old_backup in existing_backups[max_backups-1:]:
os.remove(old_backup)
print(f"删除旧备份: {old_backup.name}")
return backup_path
except Exception as e:
print(f"增量备份失败: {e}")
return None
# 测试增量备份
print("第一次备份(文件初始状态):")
backup1 = incremental_backup('important_data.txt')
# 修改文件
with open('important_data.txt', 'a') as f:
f.write("新增的内容\n")
print("\n第二次备份(文件已修改):")
backup2 = incremental_backup('important_data.txt')
print("\n第三次备份(文件未修改):")
backup3 = incremental_backup('important_data.txt')
# 3. 目录同步
print("\n3. 目录同步:")
def sync_directories(source_dir, target_dir, dry_run=False):
"""同步两个目录(单向)"""
source_path = Path(source_dir)
target_path = Path(target_dir)
if not source_path.exists():
print(f"错误: 源目录不存在 - {source_dir}")
return False
# 创建目标目录
target_path.mkdir(parents=True, exist_ok=True)
changes = {
'created': 0,
'updated': 0,
'deleted': 0,
'skipped': 0
}
# 同步文件
for source_file in source_path.rglob('*'):
if source_file.is_file():
# 计算相对路径
rel_path = source_file.relative_to(source_path)
target_file = target_path / rel_path
# 确保目标目录存在
target_file.parent.mkdir(parents=True, exist_ok=True)
# 检查是否需要同步
sync_needed = False
if not target_file.exists():
# 目标文件不存在
sync_needed = True
action = "创建"
else:
# 检查文件是否相同(大小和修改时间)
source_stat = source_file.stat()
target_stat = target_file.stat()
if (source_stat.st_size != target_stat.st_size or
source_stat.st_mtime > target_stat.st_mtime):
sync_needed = True
action = "更新"
else:
action = "跳过"
# 执行同步
if sync_needed and not dry_run:
try:
shutil.copy2(source_file, target_file)
if action == "创建":
changes['created'] += 1
else:
changes['updated'] += 1
print(f"[{action}] {rel_path}")
except Exception as e:
print(f"[错误] 同步 {rel_path} 失败: {e}")
changes['skipped'] += 1
elif dry_run:
print(f"[模拟 {action}] {rel_path}")
if action in ["创建", "更新"]:
changes[action.lower() + 'd'] += 1
else:
changes['skipped'] += 1
# 删除目标目录中不存在于源目录的文件
for target_file in target_path.rglob('*'):
if target_file.is_file():
rel_path = target_file.relative_to(target_path)
source_file = source_path / rel_path
if not source_file.exists():
if not dry_run:
try:
target_file.unlink()
changes['deleted'] += 1
print(f"[删除] {rel_path}")
except Exception as e:
print(f"[错误] 删除 {rel_path} 失败: {e}")
changes['skipped'] += 1
else:
print(f"[模拟 删除] {rel_path}")
changes['deleted'] += 1
# 报告结果
print(f"\n同步完成:")
print(f" 创建: {changes['created']} 个文件")
print(f" 更新: {changes['updated']} 个文件")
print(f" 删除: {changes['deleted']} 个文件")
print(f" 跳过: {changes['skipped']} 个文件")
return True
# 创建测试目录结构
test_source = Path('test_source')
test_target = Path('test_target')
# 清理旧目录
shutil.rmtree(test_source, ignore_errors=True)
shutil.rmtree(test_target, ignore_errors=True)
# 创建源目录结构
(test_source / 'docs').mkdir(parents=True, exist_ok=True)
(test_source / 'images').mkdir(parents=True, exist_ok=True)
# 创建一些测试文件
(test_source / 'file1.txt').write_text('文件1内容')
(test_source / 'docs' / 'readme.txt').write_text('说明文档')
(test_source / 'images' / 'icon.png').write_bytes(b'fake image data')
# 创建目标目录(已有一些文件)
test_target.mkdir(exist_ok=True)
(test_target / 'old_file.txt').write_text('旧文件')
print("执行目录同步(模拟运行):")
sync_directories(test_source, test_target, dry_run=True)
print("\n执行实际同步:")
sync_directories(test_source, test_target, dry_run=False)
# 4. 文件版本控制系统(简化版)
print("\n4. 文件版本控制系统(简化版):")
class SimpleVersionControl:
"""简单的文件版本控制"""
def __init__(self, repo_dir='.simple_vcs'):
self.repo_dir = Path(repo_dir)
self.versions_file = self.repo_dir / 'versions.json'
self.versions = {}
# 初始化仓库
self.repo_dir.mkdir(exist_ok=True)
if self.versions_file.exists():
try:
import json
with open(self.versions_file, 'r') as f:
self.versions = json.load(f)
except:
self.versions = {}
def add_file(self, filepath, message=""):
"""添加文件到版本控制"""
source_path = Path(filepath)
if not source_path.exists():
print(f"错误: 文件不存在 - {filepath}")
return False
# 计算文件哈希
file_hash = calculate_file_hash(filepath)
if not file_hash:
return False
# 检查是否已有相同版本
if filepath in self.versions:
last_hash = self.versions[filepath]['versions'][-1]['hash']
if file_hash == last_hash:
print(f"文件未更改,跳过版本控制: {filepath}")
return True
# 创建版本记录
version_info = {
'timestamp': time.time(),
'hash': file_hash,
'message': message,
'size': os.path.getsize(filepath)
}
# 保存文件版本
version_dir = self.repo_dir / 'files' / file_hash[:2]
version_dir.mkdir(parents=True, exist_ok=True)
version_file = version_dir / file_hash
try:
shutil.copy2(filepath, version_file)
# 更新版本记录
if filepath not in self.versions:
self.versions[filepath] = {
'created': time.time(),
'versions': []
}
self.versions[filepath]['versions'].append(version_info)
# 保存版本信息
self._save_versions()
print(f"添加版本: {filepath} ({message})")
return True
except Exception as e:
print(f"添加版本失败: {e}")
return False
def list_versions(self, filepath=None):
"""列出版本"""
if not self.versions:
print("版本库为空")
return
if filepath:
if filepath in self.versions:
print(f"\n文件: {filepath}")
print(f"创建时间: {time.ctime(self.versions[filepath]['created'])}")
print("版本历史:")
for i, version in enumerate(self.versions[filepath]['versions']):
print(f" [{i}] {time.ctime(version['timestamp'])} "
f"- {version['message']} "
f"(哈希: {version['hash'][:8]}..., "
f"大小: {version['size']}字节)")
else:
print(f"文件不在版本控制中: {filepath}")
else:
print("\n版本库中的所有文件:")
for filepath, info in self.versions.items():
version_count = len(info['versions'])
print(f" {filepath}: {version_count}个版本")
def restore_version(self, filepath, version_index=-1):
"""恢复文件到指定版本"""
if filepath not in self.versions:
print(f"错误: 文件不在版本控制中 - {filepath}")
return False
versions = self.versions[filepath]['versions']
if version_index < 0:
version_index = len(versions) + version_index
if version_index < 0 or version_index >= len(versions):
print(f"错误: 无效的版本索引 {version_index}")
return False
version_info = versions[version_index]
version_hash = version_info['hash']
# 查找版本文件
version_file = self.repo_dir / 'files' / version_hash[:2] / version_hash
if not version_file.exists():
print(f"错误: 版本文件不存在 - {version_hash}")
return False
try:
# 备份当前文件
current_file = Path(filepath)
if current_file.exists():
backup_file = current_file.with_suffix(current_file.suffix + '.bak')
shutil.copy2(current_file, backup_file)
print(f"已创建当前文件备份: {backup_file}")
# 恢复版本
shutil.copy2(version_file, filepath)
print(f"已恢复文件到版本 [{version_index}] "
f"({time.ctime(version_info['timestamp'])})")
return True
except Exception as e:
print(f"恢复版本失败: {e}")
return False
def _save_versions(self):
"""保存版本信息"""
try:
import json
with open(self.versions_file, 'w') as f:
json.dump(self.versions, f, indent=2)
except Exception as e:
print(f"保存版本信息失败: {e}")
# 测试简单版本控制
print("测试简单版本控制系统:")
vcs = SimpleVersionControl()
# 添加文件版本
vcs.add_file('important_data.txt', '初始版本')
# 修改文件
with open('important_data.txt', 'a') as f:
f.write("第二次修改\n")
vcs.add_file('important_data.txt', '第二次修改')
# 再次修改
with open('important_data.txt', 'a') as f:
f.write("第三次修改\n")
vcs.add_file('important_data.txt', '第三次修改')
# 列出版本
vcs.list_versions('important_data.txt')
# 恢复版本
print("\n恢复文件到第一个版本:")
vcs.restore_version('important_data.txt', 0)
# 验证恢复
with open('important_data.txt', 'r') as f:
print(f"恢复后的内容:\n{f.read()}")
# 清理测试文件
print("\n清理测试文件...")
test_files = [
'test.txt', 'example.txt', 'data.txt', 'write_test.txt',
'writelines_test.txt', 'append_test.txt', 'rplus_test.txt',
'binary_write.bin', 'data.csv', 'buffered_write.txt',
'safe_test.txt', 'locked_file.txt', 'large_data.txt',
'test_utf-8.txt', 'test_utf-16.txt', 'test_gbk.txt',
'test_latin-1.txt', 'test_ascii.txt', 'with_bom.txt',
'unix.txt', 'windows.txt', 'text_to_binary.bin',
'mixed_encoding.log', 'data_utf8_bom.csv', 'data.json',
'large_encoded.txt', 'app_server.log', 'config.ini',
'config.json', 'config.yaml', '.env', 'config_updated.json',
'important_data.txt', 'app.log', 'temp1.txt', 'temp2.txt'
]
for file in test_files:
if os.path.exists(file):
try:
os.remove(file)
print(f"删除: {file}")
except:
pass
# 清理目录
import shutil
dirs_to_clean = ['backups', 'incremental_backups', '.simple_vcs']
for dir_name in dirs_to_clean:
if os.path.exists(dir_name):
shutil.rmtree(dir_name)
print(f"删除目录: {dir_name}")
print("清理完成!")
file_backup_sync()
四、最佳实践和性能优化
python
# ==================== 最佳实践和性能优化 ====================
def best_practices_and_performance():
print("\n=== 文件操作最佳实践和性能优化 ===")
import time
import os
# 1. 大文件处理策略
print("1. 大文件处理策略:")
def process_large_file_efficiently(filename, process_func):
"""高效处理大文件"""
print(f"处理大文件: {filename}")
start_time = time.time()
try:
with open(filename, 'r', encoding='utf-8') as f:
# 逐行处理,避免一次性加载到内存
line_count = 0
for line in f:
process_func(line.strip())
line_count += 1
# 进度提示
if line_count % 10000 == 0:
elapsed = time.time() - start_time
print(f" 已处理 {line_count} 行,耗时 {elapsed:.2f}秒")
elapsed = time.time() - start_time
print(f"完成! 总共处理 {line_count} 行,耗时 {elapsed:.2f}秒")
return line_count
except Exception as e:
print(f"处理文件时出错: {e}")
return 0
# 创建测试大文件
large_filename = 'large_test_file.txt'
with open(large_filename, 'w', encoding='utf-8') as f:
for i in range(50000):
f.write(f"这是第{i+1}行数据,包含一些文本用于测试大文件处理性能。\n")
# 定义处理函数
def count_words(line):
return len(line.split())
total_words = 0
def process_line(line):
global total_words
total_words += count_words(line)
print("逐行处理大文件:")
lines_processed = process_large_file_efficiently(large_filename, process_line)
print(f"总单词数: {total_words}")
# 2. 内存映射文件
print("\n2. 内存映射文件:")
import mmap
def memory_mapped_example(filename):
"""使用内存映射处理大文件"""
print(f"使用内存映射处理: {filename}")
file_size = os.path.getsize(filename)
print(f"文件大小: {file_size:,} 字节")
with open(filename, 'r+b') as f:
# 创建内存映射
with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
# 在内存中直接搜索
search_bytes = b"第10000行"
position = mm.find(search_bytes)
if position != -1:
# 读取找到的内容
mm.seek(position)
content = mm.read(100).decode('utf-8', errors='ignore')
print(f"找到 '第10000行' 在位置 {position}: {content}")
else:
print("未找到搜索内容")
# 统计换行符数量(行数)
newline_count = mm.count(b'\n')
print(f"估计行数: {newline_count}")
# 测试内存映射
memory_mapped_example(large_filename)
# 3. 并行文件处理
print("\n3. 并行文件处理:")
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
def parallel_file_processing(filename, num_workers=4):
"""并行处理文件的不同部分"""
print(f"并行处理文件,使用 {num_workers} 个工作线程")
file_size = os.path.getsize(filename)
chunk_size = file_size // num_workers
def process_chunk(chunk_id, start, end):
"""处理文件块"""
local_count = 0
with open(filename, 'r', encoding='utf-8') as f:
f.seek(start)
# 调整起始位置到行首
if start > 0:
f.readline() # 跳过可能不完整的行
# 处理指定范围内的行
while f.tell() < end:
line = f.readline()
if not line:
break
local_count += 1
return chunk_id, local_count
# 计算各块的起始位置
chunks = []
for i in range(num_workers):
start = i * chunk_size
end = start + chunk_size if i < num_workers - 1 else file_size
chunks.append((i, start, end))
# 并行处理
start_time = time.time()
total_lines = 0
with ThreadPoolExecutor(max_workers=num_workers) as executor:
futures = [executor.submit(process_chunk, *chunk) for chunk in chunks]
for future in futures:
chunk_id, count = future.result()
total_lines += count
print(f" 块{chunk_id}: 处理了 {count} 行")
elapsed = time.time() - start_time
print(f"并行处理完成: 总共 {total_lines} 行,耗时 {elapsed:.2f}秒")
return total_lines
# 测试并行处理
parallel_lines = parallel_file_processing(large_filename, 4)
# 4. 文件操作缓存
print("\n4. 文件操作缓存:")
import functools
class FileCache:
"""文件内容缓存"""
def __init__(self, max_size=10):
self.cache = {}
self.max_size = max_size
self.access_order = []
@functools.lru_cache(maxsize=10)
def read_file_cached(self, filename):
"""带缓存的文件读取"""
print(f"缓存读取: {filename}")
with open(filename, 'r', encoding='utf-8') as f:
return f.read()
def get_file_stats(self, filename):
"""获取文件统计信息(带缓存)"""
if filename in self.cache:
stats, timestamp = self.cache[filename]
# 检查缓存是否过期
current_mtime = os.path.getmtime(filename)
if current_mtime <= timestamp:
print(f"使用缓存: {filename}")
return stats
# 重新计算并缓存
print(f"计算并缓存: {filename}")
stats = {
'size': os.path.getsize(filename),
'mtime': os.path.getmtime(filename),
'lines': self._count_lines(filename)
}
self.cache[filename] = (stats, time.time())
self.access_order.append(filename)
# 清理旧缓存
if len(self.cache) > self.max_size:
oldest = self.access_order.pop(0)
del self.cache[oldest]
return stats
def _count_lines(self, filename):
"""快速统计行数"""
count = 0
with open(filename, 'r', encoding='utf-8') as f:
for _ in f:
count += 1
return count
# 测试文件缓存
cache = FileCache(max_size=3)
print("第一次获取统计(会计算):")
stats1 = cache.get_file_stats(large_filename)
print(f" 大小: {stats1['size']:,} 字节,行数: {stats1['lines']}")
print("\n第二次获取统计(使用缓存):")
stats2 = cache.get_file_stats(large_filename)
print(f" 大小: {stats2['size']:,} 字节")
# 5. 异步文件操作
print("\n5. 异步文件操作:")
import asyncio
async def async_read_file(filename):
"""异步读取文件"""
print(f"开始异步读取: {filename}")
# 在单独的线程中执行阻塞IO
loop = asyncio.get_event_loop()
def read_file_sync():
with open(filename, 'r', encoding='utf-8') as f:
return f.read()
content = await loop.run_in_executor(None, read_file_sync)
print(f"异步读取完成: {filename} ({len(content):,} 字符)")
return content
async def async_write_file(filename, content):
"""异步写入文件"""
print(f"开始异步写入: {filename}")
loop = asyncio.get_event_loop()
def write_file_sync():
with open(filename, 'w', encoding='utf-8') as f:
return f.write(content)
written = await loop.run_in_executor(None, write_file_sync)
print(f"异步写入完成: {filename} ({written} 字符)")
return written
async def async_file_operations():
"""异步文件操作示例"""
print("执行异步文件操作...")
# 并发执行多个文件操作
read_task = async_read_file(large_filename)
write_task = async_write_file('async_output.txt', '异步写入的内容\n')
# 等待所有任务完成
results = await asyncio.gather(read_task, write_task)
print(f"异步操作完成,读取 {len(results[0]):,} 字符,写入 {results[1]} 字符")
# 运行异步示例
try:
asyncio.run(async_file_operations())
except RuntimeError:
print("注意: 异步示例需要在异步环境中运行")
# 6. 文件操作性能对比
print("\n6. 文件操作性能对比:")
def performance_comparison():
"""不同文件操作方式的性能对比"""
test_data = 'x' * 1000000 # 1MB数据
# 测试1: 普通写入
start = time.time()
with open('test1.txt', 'w') as f:
f.write(test_data)
time1 = time.time() - start
# 测试2: 带缓冲的写入
start = time.time()
with open('test2.txt', 'w', buffering=1024*1024) as f: # 1MB缓冲
f.write(test_data)
time2 = time.time() - start
# 测试3: 二进制写入
start = time.time()
with open('test3.txt', 'wb') as f:
f.write(test_data.encode('utf-8'))
time3 = time.time() - start
# 测试4: 分块写入
start = time.time()
chunk_size = 8192
with open('test4.txt', 'w') as f:
for i in range(0, len(test_data), chunk_size):
f.write(test_data[i:i+chunk_size])
time4 = time.time() - start
print("写入性能对比 (1MB数据):")
print(f" 普通写入: {time1:.4f}秒")
print(f" 带缓冲写入: {time2:.4f}秒")
print(f" 二进制写入: {time3:.4f}秒")
print(f" 分块写入: {time4:.4f}秒")
# 清理测试文件
for i in range(1, 5):
try:
os.remove(f'test{i}.txt')
except:
pass
performance_comparison()
# 7. 安全文件操作
print("\n7. 安全文件操作:")
def secure_file_operations():
"""安全文件操作最佳实践"""
import tempfile
# 1. 使用临时文件处理敏感数据
with tempfile.NamedTemporaryFile(mode='w', delete=False) as tmp:
tmp.write("敏感数据\n")
tmp_path = tmp.name
try:
# 处理临时文件
with open(tmp_path, 'r') as f:
content = f.read()
print(f"处理敏感数据: {content[:20]}...")
finally:
# 确保删除临时文件
os.unlink(tmp_path)
print("临时文件已安全删除")
# 2. 安全的文件权限
secure_file = 'secure_data.txt'
# 创建文件时设置权限
with open(secure_file, 'w') as f:
f.write("敏感信息\n")
# 设置文件权限(仅所有者可读写)
os.chmod(secure_file, 0o600)
# 检查权限
stat_info = os.stat(secure_file)
print(f"文件权限: {oct(stat_info.st_mode)[-3:]}")
# 3. 安全删除文件(覆盖数据)
def secure_delete(filename, passes=3):
"""安全删除文件,覆盖数据"""
try:
file_size = os.path.getsize(filename)
with open(filename, 'rb+') as f:
for pass_num in range(passes):
# 写入随机数据
f.seek(0)
f.write(os.urandom(file_size))
f.flush()
os.fsync(f.fileno())
# 重命名文件
temp_name = filename + '.deleted'
os.rename(filename, temp_name)
# 删除文件
os.unlink(temp_name)
print(f"文件已安全删除: {filename}")
return True
except Exception as e:
print(f"安全删除失败: {e}")
return False
# 测试安全删除
secure_delete(secure_file)
secure_file_operations()
# 清理大文件
os.remove(large_filename)
print(f"\n已清理测试文件: {large_filename}")
best_practices_and_performance()
总结
Python文件操作要点:
-
打开模式:
'r': 只读'w': 写入(覆盖)'a': 追加'x': 创建新文件'b': 二进制模式'+': 读写模式
-
推荐实践:
- 始终使用
with语句确保文件正确关闭 - 指定明确的编码(如
utf-8) - 处理文件操作异常
- 对大文件使用迭代而不是一次性读取
- 始终使用
-
性能优化:
- 对大文件使用逐行或分块处理
- 考虑使用内存映射(
mmap)处理超大文件 - 对频繁读取的文件使用缓存
- 考虑异步或并行处理
-
安全性:
- 验证文件路径,防止路径遍历攻击
- 对敏感数据使用临时文件
- 设置适当的文件权限
- 安全删除敏感文件
-
现代特性:
- 使用
pathlib进行路径操作(Python 3.4+) - 使用
os.scandir()进行高效目录遍历 - 考虑使用异步IO处理大量文件操作
- 使用
掌握这些文件操作技术可以帮助你编写更高效、更安全、更可靠的Python程序。
现代C++的文件操作核心是基于流的<fstream>库,它提供了类似控制台I/O(cin/cout)的接口来处理文件。为了方便你快速理解其与Python的差异,我先用一个表格进行对比:
| 特性 | Python | C++ (现代) |
|---|---|---|
| 核心机制 | 基于文件对象 (open函数) | 基于流对象 (ifstream, ofstream, fstream) |
| 打开方式 | 模式字符串 (如 "r", "wb") |
模式标志组合 (如 `std::ios::in |
| 资源管理 | 依赖 with 语句或手动 close() |
RAII (推荐) :对象析构时自动关闭;也可手动 close() |
| 读取接口 | read(), readline(), 迭代 |
流提取符 (>>) 、getline()、read() (二进制) |
| 写入接口 | write(), writelines() |
流插入符 (<<) 、write() (二进制) |
| 路径处理 | os.path, pathlib (Python 3.4+) |
std::filesystem (C++17) |
| 错误处理 | 抛出异常 (如 FileNotFoundError) |
主要检查流状态 (如 !ifile),也可设置异常模式 |
| 现代特性 | - | C++17 std::filesystem;C++20 std::format 格式化 |
📝 核心操作与代码示例
1. 基础文本文件读写
这是最常用的场景,使用流操作符 << 和 >> 就像使用 cout 和 cin。
cpp
#include <iostream>
#include <fstream>
#include <string>
int main() {
// 写入文件 (输出文件流)
std::ofstream out_file("example.txt"); // 默认模式为写入并截断
if (!out_file) { // 检查文件是否成功打开
std::cerr << "打开文件失败 (写入)!" << std::endl;
return 1;
}
out_file << "Hello, File!\n"; // 像使用cout一样写入
out_file << 42 << " " << 3.14 << std::endl;
// 文件会在out_file析构时自动关闭
// 读取文件 (输入文件流)
std::ifstream in_file("example.txt");
if (!in_file.is_open()) { // 另一种检查方式
std::cerr << "打开文件失败 (读取)!" << std::endl;
return 1;
}
std::string line;
int num;
double pi;
std::getline(in_file, line); // 读取一整行
in_file >> num >> pi; // 按空格分隔读取数据
std::cout << "行内容: " << line << std::endl;
std::cout << "数字: " << num << ", PI: " << pi << std::endl;
return 0;
}
2. 二进制文件读写
处理图像、结构体等非文本数据时,必须使用二进制模式(std::ios::binary)和 read/write 方法。
cpp
#include <iostream>
#include <fstream>
#include <cstring> // for strcpy (仅为示例,实践中可用std::string或std::array)
struct Person {
char name[50];
int age;
};
int main() {
Person people[2] = {{"Alice", 30}, {"Bob", 25}};
// 写入二进制文件
std::ofstream b_out("data.bin", std::ios::binary); // 必须指定二进制模式
if (b_out) {
// write参数为(char*)和字节数
b_out.write(reinterpret_cast<const char*>(people), 2 * sizeof(Person));
// 无需显式关闭,析构函数处理
}
// 读取二进制文件
Person loaded_people[2];
std::ifstream b_in("data.bin", std::ios::binary);
if (b_in) {
b_in.read(reinterpret_cast<char*>(loaded_people), 2 * sizeof(Person));
for (const auto& p : loaded_people) {
std::cout << "姓名: " << p.name << ", 年龄: " << p.age << std::endl;
}
}
return 0;
}
3. 文件指针与随机访问
通过 seekg(用于输入/get)和 seekp(用于输出/put)可以移动文件指针,实现随机读写。
cpp
#include <iostream>
#include <fstream>
int main() {
std::fstream file("random_access.txt", std::ios::in | std::ios::out | std::ios::trunc);
file << "1234567890ABCDEFGHIJ"; // 写入测试数据
// 将"读指针"移动到第10个字节(从0开始,即'A')
file.seekg(10, std::ios::beg); // 从文件开始处偏移
char ch;
file.get(ch); // 读取一个字符
std::cout << "位置10的字符: " << ch << std::endl; // 输出: A
// 获取当前"写指针"位置(在末尾)
std::streampos write_pos = file.tellp();
std::cout << "当前写指针位置: " << write_pos << std::endl;
// 将"写指针"从当前位置回退5个字节
file.seekp(-5, std::ios::cur);
file << "OVER"; // 覆盖数据
// 此时文件内容变为 "1234567890OVEREFGHIJ"
return 0;
}
🚀 现代C++的增强特性
1. 使用 std::filesystem (C++17)
C++17引入的 <filesystem> 库极大简化了路径和文件系统操作。
cpp
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem; // 别名简化
int main() {
fs::path file_path = "data/subdir/example.txt";
// 创建目录(包括父目录)
fs::create_directories(file_path.parent_path());
// 检查文件状态
if (fs::exists(file_path)) {
std::cout << "文件大小: " << fs::file_size(file_path) << " 字节\n";
}
// 遍历目录
for (const auto& entry : fs::directory_iterator(".")) {
std::cout << entry.path() << " - "
<< (fs::is_directory(entry.status()) ? "目录" : "文件") << std::endl;
}
// 复制文件
fs::copy_file(file_path, "backup.txt", fs::copy_options::overwrite_existing);
return 0;
}
2. 结合 std::string_view 和 std::format (C++17/20)
使用现代字符串视图和格式化工具,使代码更安全、高效。
cpp
#include <iostream>
#include <fstream>
#include <string_view>
#include <format> // C++20
void write_log(std::string_view filename, std::string_view message, int id) {
std::ofstream log(filename.data(), std::ios::app); // 追加模式
if (log) {
// C++20 格式化 (更安全、类型安全)
log << std::format("[ID:{:03d}] {}\n", id, message);
}
}
int main() {
write_log("app.log", "程序启动", 1);
write_log("app.log", "处理数据", 2);
return 0;
}
💡 最佳实践与要点
- 坚持RAII原则:优先在局部作用域内声明文件流对象,利用析构函数自动关闭文件,避免资源泄漏。
- 务必检查打开状态 :文件打开可能因权限、路径错误等失败,在操作前使用
if (stream)或is_open()进行检查是必需的。 - 区分文本与二进制模式 :处理图片、结构体或跨平台需求时,务必使用
std::ios::binary模式,否则可能发生意外的字符转换(如换行符)。 - 理解文件打开模式 :常用的组合有
std::ios::in | std::ios::out(读写)、std::ios::app(追加)等。 - 优先使用标准库 :对于路径操作,优先使用
std::filesystem而非手动拼接字符串,它更安全且跨平台。
如果你想进一步了解如何在C++中处理特定格式的文件(如CSV或JSON),我可以为你提供更具体的代码示例和库推荐。