Python 基础回顾:文件读写与数据清洗
做 GROMACS 批处理质控,说白了第一步就是把数据从磁盘上弄进来,弄干净。这一步如果写不好,后面所有分析都是空中楼阁。我个人习惯在开始任何脚本之前,先把文件读写和数据清洗模块搭好——像搭积木一样,基础稳了,上层才能跑得稳。
文件读写:打开、读、写、关
Python 里文件读写最常用的就是内置的 open() 函数,配合 with 上下文管理器。为什么推荐 with?它会在代码块结束自动关闭文件,省得你忘了关导致资源泄漏。我在项目里见过有人用 f = open() 然后忘了 f.close(),结果跑大批量的时候文件描述符耗尽……嗯,从那以后我所有代码都用 with。
常用模式:
'r'— 只读(默认),文件必须存在。'w'— 写入,会覆盖已有文件。'a'— 追加,在文件末尾写入。'rb'/'wb'— 二进制模式,处理日志或二进制输出。
举一个读取 GROMACS 能量文件的例子:
with open('ener.edr', 'rb') as f:
# 二进制模式读取 edr 文件
raw = f.read()
# 接下来用 xdrlib 或工具包解析
实际处理文本日志(比如 md.log)时,我更习惯逐行读:
with open('md.log', 'r', encoding='utf-8', errors='replace') as f:
for line in f:
# 处理每一行
print(line.strip())
注意:编码问题是个大坑。很多生物软件输出日志默认用本地编码,如果你直接
open('r') 可能收到 UnicodeDecodeError。我建议始终指定 encoding='utf-8' 并加上 errors='replace',遇到非法字符替换掉而不是炸掉。
写入也类似,用 'w' 或 'a' 模式:
with open('result.txt', 'w') as f:
f.write('phi_mean,psi_mean\n')
for phi, psi in zip(phi_list, psi_list):
f.write(f'{phi:.3f},{psi:.3f}\n')
数据清洗:把脏数据变整齐
真实项目中的日志文件经常包含多余空格、空行、注释行。比如 GROMACS 的 .xvg 文件,开头有若干以 @ 或 # 开头的元数据行。你必须把这些清理掉才能拿到纯数据。
我个人常用的几个清洗手段:
strip()— 去掉首尾空白符。split()— 默认按任何空白切分,处理变长空格很省心。startswith()— 过滤注释/标题行。- 正则
re.sub()— 替换多余空格或特殊符号。
举个经典的过滤 xvg 文件的代码:
import re
with open('traj.xvg', 'r') as f:
lines = []
for line in f:
line = line.strip()
if not line or line.startswith(('@', '#', '&')):
continue
# 将多个空格替换为单个空格
line = re.sub(r'\s+', ' ', line)
lines.append(line)
# 现在 lines 是纯数据行,可以用 split() 转为数字列表
data = [list(map(float, row.split())) for row in lines]
小技巧:处理 CSV 或制表符分隔的文件时,直接用
line.split('\t') 比 csv.reader 更快。但如果遇到转义、引号,还是得用 csv 模块。我在清洗出几千个 log 文件时发现 split 快了三倍,代价是丢失了 CSV 的健壮性——自己权衡。
实战对比:经典 vs 推荐写法
| 场景 | 容易犯错的写法 | 我推荐的写法 |
|---|---|---|
| 读取并过滤空行 | for line in open('f'): 不关闭文件 |
with open('f') as f: for line in f: 自动关闭 |
| 处理大量文本 | content = f.read().splitlines() 内存爆炸 |
逐行迭代 for line in f: |
| 数字提取 | 用 split(' ') 遇到连续空格就挂 |
split() 默认按任意空白 |
| 字符串判断 | if line[0] == '#': 可能越界 |
if line.startswith('#'): 安全 |
避坑指南:我踩过的几个雷
- 曾经我写批处理脚本时,忘了在
write后加\n,结果所有输出粘成一坨。后来我统一用f.write(line + '\n')或print(line, file=f)。 - 曾经读取一批 Windows 下生成的 log,发现每行末尾有
\r\n,导致split()意外。解决办法是用line.replace('\r', '')或line.rstrip()。 - 曾经在循环里反复
open同一个文件(比如追加模式)写入,性能极差。应该先收集所有行,最后一次性写入,或者用with保持打开。
核心法则:读文件用 with open + 迭代器;写文件先准备数据;清洗数据用 strip + split + startswith;遇到不确定的编码一律 encoding='utf-8', errors='replace'。
知识结构图:从原始文件到结构化数据
你从这张图里可以看到,整个流程并不复杂。真正考验人的是细节——编码、换行、异常处理。把这些基础打牢,后续处理成百上千个模拟轨迹文件时,你就不会手忙脚乱了。