1. Python文件与目录操作基础
在Python开发中,文件与目录操作是最基础也是最重要的技能之一。无论是数据处理、日志记录还是配置文件管理,都离不开对文件系统的操作。Python提供了多个内置模块来处理文件和目录,主要包括:
- os模块:提供基本的操作系统功能接口
- pathlib模块(Python 3.4+):面向对象的文件系统路径操作
- shutil模块:高级文件操作工具
提示:虽然os模块功能强大,但在新项目中建议优先使用pathlib,它提供了更直观的面向对象接口,代码可读性更好。
1.1 为什么需要多种文件操作模块
不同的模块设计有不同的初衷和使用场景:
- os模块:最早的文件系统接口,直接映射操作系统API,功能全面但接口较为底层
- pathlib模块:引入面向对象设计,路径操作更直观,减少字符串拼接错误
- shutil模块:专注于高级文件操作(如复制、移动、归档),补充os模块的功能
在实际项目中,我通常会混合使用这些模块:
- 使用pathlib处理路径构造和检查
- 使用shutil进行文件复制/移动
- 使用os处理一些底层操作(如文件描述符)
2. 使用os模块进行基础操作
os模块是Python与操作系统交互的桥梁,提供了丰富的文件系统操作方法。
2.1 常用目录操作
python复制import os
# 获取当前工作目录
current_dir = os.getcwd()
print(f"当前目录: {current_dir}")
# 改变工作目录
os.chdir('/path/to/new/directory')
# 列出目录内容
contents = os.listdir('.') # 返回文件名列表
print(f"目录内容: {contents}")
# 创建目录
os.mkdir('new_dir') # 创建单个目录
os.makedirs('path/to/nested/dir') # 递归创建多级目录
# 删除目录
os.rmdir('empty_dir') # 只能删除空目录
os.removedirs('path/to/nested/dir') # 递归删除空目录
注意:使用os.makedirs()时,如果目录已存在会抛出FileExistsError,可以通过exist_ok=True参数避免错误。
2.2 文件操作与属性获取
python复制import os
import time
# 文件重命名
os.rename('old.txt', 'new.txt')
# 删除文件
os.remove('file_to_delete.txt')
# 获取文件属性
stat_info = os.stat('some_file.txt')
print(f"文件大小: {stat_info.st_size} 字节")
print(f"最后修改时间: {time.ctime(stat_info.st_mtime)}")
# 检查路径类型
print(f"是文件?: {os.path.isfile('some_file.txt')}")
print(f"是目录?: {os.path.isdir('some_dir')}")
print(f"路径存在?: {os.path.exists('some_path')}")
2.3 路径操作(os.path子模块)
os.path提供了跨平台的路径操作方法:
python复制import os
# 路径拼接
full_path = os.path.join('dir', 'subdir', 'file.txt') # 跨平台安全
# 路径拆分
dirname, filename = os.path.split('/path/to/file.txt')
print(f"目录部分: {dirname}")
print(f"文件名部分: {filename}")
# 获取绝对路径
abs_path = os.path.abspath('relative/path')
# 获取路径的基本名和目录名
print(f"基本名: {os.path.basename('/path/to/file.txt')}") # file.txt
print(f"目录名: {os.path.dirname('/path/to/file.txt')}") # /path/to
# 分离文件名和扩展名
name, ext = os.path.splitext('archive.tar.gz')
print(f"文件名: {name}, 扩展名: {ext}") # archive.tar, .gz
3. 使用pathlib进行现代化路径操作
pathlib模块在Python 3.4中引入,提供了面向对象的路径操作方式,代码更加清晰易读。
3.1 Path对象基础
python复制from pathlib import Path
# 创建Path对象
p = Path('/home/user/documents') / 'report.txt' # 使用/操作符拼接路径
# 常用属性
print(f"路径: {p}")
print(f"父目录: {p.parent}")
print(f"文件名: {p.name}")
print(f"后缀: {p.suffix}")
print(f"所有后缀: {p.suffixes}") # 对于多重扩展名如.tar.gz
# 解析路径
print(f"绝对路径: {p.absolute()}")
print(f"解析符号链接: {p.resolve()}")
3.2 文件系统操作
python复制from pathlib import Path
# 创建目录
Path('new_dir').mkdir(exist_ok=True) # exist_ok避免目录已存在错误
# 创建文件
p = Path('new_file.txt')
p.touch() # 创建空文件
# 写入文件
p.write_text('Hello, pathlib!') # 文本写入
p.write_bytes(b'Binary data') # 二进制写入
# 读取文件
content = p.read_text()
print(f"文件内容: {content}")
# 删除文件
p.unlink() # 删除文件
3.3 高级路径操作
python复制from pathlib import Path
import glob
# 遍历目录
for item in Path('.').iterdir():
print(item.name)
# 模式匹配
for py_file in Path('.').glob('*.py'):
print(f"Python文件: {py_file}")
# 递归匹配
for py_file in Path('.').rglob('**/*.py'):
print(f"递归找到Python文件: {py_file}")
# 路径有效性检查
p = Path('some_file.txt')
print(f"存在?: {p.exists()}")
print(f"是文件?: {p.is_file()}")
print(f"是目录?: {p.is_dir()}")
4. 使用shutil进行高级文件操作
shutil模块提供了比os模块更高级的文件操作功能,特别是文件复制、移动和归档。
4.1 文件复制与移动
python复制import shutil
# 复制文件
shutil.copy2('source.txt', 'destination.txt') # 保留元数据
# 复制目录
shutil.copytree('src_dir', 'dst_dir') # 递归复制整个目录树
# 移动文件/目录
shutil.move('source.txt', 'new_location.txt')
# 比较copy、copy2和copyfile的区别:
# - copyfile: 只复制内容
# - copy: 复制内容+权限
# - copy2: 复制内容+权限+元数据(如修改时间)
4.2 归档操作
python复制import shutil
# 创建zip归档
shutil.make_archive('backup', 'zip', 'directory_to_archive')
# 解压归档
shutil.unpack_archive('backup.zip', 'extract_to')
# 支持的归档格式
print(f"可用格式: {shutil.get_archive_formats()}")
# 通常包括: zip, tar, gztar, bztar, xztar
4.3 磁盘空间管理
python复制import shutil
# 获取磁盘使用情况
total, used, free = shutil.disk_usage('/')
print(f"总空间: {total // (2**30)}GB")
print(f"已用空间: {used // (2**30)}GB")
print(f"可用空间: {free // (2**30)}GB")
# 删除整个目录树
shutil.rmtree('directory_to_remove') # 慎用!不可恢复
5. 综合应用与最佳实践
在实际项目中,我们通常需要组合使用这些模块来完成复杂任务。
5.1 文件搜索工具实现
python复制from pathlib import Path
def find_files(directory, pattern, recursive=True):
"""查找目录中匹配模式的文件"""
path = Path(directory)
if recursive:
return list(path.rglob(pattern))
return list(path.glob(pattern))
# 示例:查找所有Python文件
py_files = find_files('.', '*.py')
print(f"找到的Python文件: {py_files}")
5.2 目录同步工具
python复制import shutil
from pathlib import Path
def sync_dirs(source, destination):
"""同步源目录到目标目录"""
src = Path(source)
dst = Path(destination)
# 确保目标目录存在
dst.mkdir(exist_ok=True)
# 同步文件
for item in src.iterdir():
dest_item = dst / item.name
if item.is_file():
if not dest_item.exists() or item.stat().st_mtime > dest_item.stat().st_mtime:
shutil.copy2(str(item), str(dest_item))
elif item.is_dir():
sync_dirs(item, dest_item)
# 使用示例
sync_dirs('/path/to/source', '/path/to/backup')
5.3 安全删除实现
python复制import os
import random
from pathlib import Path
def secure_delete(file_path, passes=3):
"""安全删除文件,防止数据恢复"""
path = Path(file_path)
if not path.exists():
return
file_size = path.stat().st_size
# 多次覆写文件内容
with path.open('wb') as f:
for _ in range(passes):
f.seek(0)
f.write(os.urandom(file_size))
# 重命名文件
temp_name = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz', k=10))
temp_path = path.with_name(temp_name)
path.rename(temp_path)
# 删除文件
temp_path.unlink()
6. 常见问题与解决方案
6.1 跨平台路径处理
问题:Windows和Unix-like系统的路径分隔符不同(\ vs /)
解决方案:
python复制# 不推荐 - 硬编码路径
bad_path = 'dir\\subdir\\file.txt' # Windows特定
# 推荐做法 - 使用pathlib或os.path
from pathlib import Path
good_path = Path('dir') / 'subdir' / 'file.txt' # 跨平台
# 或者使用os.path
import os.path
good_path = os.path.join('dir', 'subdir', 'file.txt')
6.2 处理Unicode文件名
问题:非ASCII字符文件名可能导致编码问题
解决方案:
python复制# 错误方式 - 直接使用str
# path = '文档/中文文件.txt' # 可能在非UTF-8系统失败
# 正确方式 - 使用pathlib或正确处理编码
from pathlib import Path
path = Path('文档') / '中文文件.txt' # 自动处理Unicode
# 或者使用os模块时指定编码
import os
filename = '中文文件.txt'.encode('utf-8').decode('latin-1') # 兼容性处理
6.3 大文件处理
问题:大文件操作可能导致内存问题
解决方案:
python复制# 不推荐 - 一次性读取大文件
# content = Path('huge_file.txt').read_text() # 可能内存不足
# 推荐 - 流式处理
with open('huge_file.txt', 'r', encoding='utf-8') as f:
for line in f: # 逐行处理
process_line(line)
# 或者使用shutil.copyfileobj进行高效复制
with open('source.bin', 'rb') as src, open('dest.bin', 'wb') as dst:
shutil.copyfileobj(src, dst, length=16*1024) # 16KB缓冲区
6.4 权限问题处理
问题:文件操作可能因权限不足失败
解决方案:
python复制import os
from pathlib import Path
def safe_remove(path):
"""安全删除文件,处理权限问题"""
try:
Path(path).unlink()
except PermissionError as e:
print(f"权限不足,无法删除 {path}: {e}")
# 尝试修改权限
os.chmod(path, 0o777)
Path(path).unlink()
except FileNotFoundError:
pass # 文件不存在无需处理
except Exception as e:
print(f"删除 {path} 时发生未知错误: {e}")
7. 性能优化技巧
7.1 批量操作优化
python复制from pathlib import Path
# 低效方式 - 逐个文件处理
for file in Path('.').glob('*.log'):
process_file(file)
# 高效方式 - 批量处理
files = list(Path('.').glob('*.log')) # 一次性获取文件列表
process_files_in_batch(files) # 批量处理函数
7.2 减少系统调用
python复制import os
from pathlib import Path
# 低效方式 - 多次检查文件属性
if Path('file.txt').exists():
if Path('file.txt').is_file():
size = Path('file.txt').stat().st_size
# 高效方式 - 减少系统调用
file_path = Path('file.txt')
try:
stat = file_path.stat()
if stat.st_size > 0:
process_file(file_path)
except FileNotFoundError:
print("文件不存在")
7.3 使用生成器处理大目录
python复制from pathlib import Path
def find_large_files(directory, min_size=1024*1024): # 默认1MB
"""查找大文件(生成器版本)"""
for item in Path(directory).rglob('*'):
try:
if item.is_file() and item.stat().st_size >= min_size:
yield item
except (PermissionError, OSError):
continue # 跳过无法访问的文件
# 使用示例
for large_file in find_large_files('/', min_size=100*1024*1024): # 100MB以上文件
print(f"大文件: {large_file} ({large_file.stat().st_size//(1024*1024)}MB)")
8. 实际项目经验分享
8.1 日志文件轮转实现
python复制from pathlib import Path
import shutil
import gzip
import time
def rotate_logs(log_dir, max_files=10):
"""日志文件轮转"""
log_dir = Path(log_dir)
log_files = sorted(log_dir.glob('*.log'), key=lambda f: f.stat().st_mtime)
# 保留最新的max_files个日志
for old_log in log_files[:-max_files]:
# 压缩旧日志
with open(old_log, 'rb') as f_in:
with gzip.open(f"{old_log}.gz", 'wb') as f_out:
shutil.copyfileobj(f_in, f_out)
old_log.unlink()
# 创建新日志文件
new_log = log_dir / f"app_{time.strftime('%Y%m%d_%H%M%S')}.log"
new_log.touch()
return new_log
8.2 项目目录结构生成器
python复制from pathlib import Path
def create_project_structure(base_dir, structure):
"""
根据描述创建项目目录结构
:param base_dir: 基础目录
:param structure: 嵌套字典描述结构
:example: {'src': {'main.py': None, 'utils': {'__init__.py': None}}}
"""
base_path = Path(base_dir)
def _create(path, struct):
for name, content in struct.items():
item_path = path / name
if content is None: # 文件
item_path.touch()
else: # 目录
item_path.mkdir(exist_ok=True)
_create(item_path, content)
_create(base_path, structure)
# 使用示例
project_structure = {
'my_project': {
'src': {
'__init__.py': None,
'main.py': None,
'utils': {
'__init__.py': None,
'file_utils.py': None
}
},
'tests': {
'__init__.py': None,
'test_main.py': None
},
'docs': {},
'data': {
'input': {},
'output': {}
}
}
}
create_project_structure('.', project_structure)
8.3 文件变更监控
python复制from pathlib import Path
import time
class FileMonitor:
"""简单文件变更监控"""
def __init__(self, path):
self.path = Path(path)
self._state = self._capture_state()
def _capture_state(self):
"""捕获当前文件状态"""
if self.path.is_dir():
return {
f: f.stat().st_mtime
for f in self.path.rglob('*')
if f.is_file()
}
return {self.path: self.path.stat().st_mtime}
def check_changes(self):
"""检查变更,返回变更文件列表"""
new_state = self._capture_state()
changed = []
# 检查修改或新增的文件
for file, mtime in new_state.items():
if file not in self._state or mtime != self._state[file]:
changed.append(file)
# 检查删除的文件
for file in set(self._state) - set(new_state):
changed.append(file)
self._state = new_state
return changed
# 使用示例
monitor = FileMonitor('.')
while True:
changes = monitor.check_changes()
if changes:
print(f"检测到变更: {changes}")
time.sleep(5)