第4章:文件操作实战:使用 open()、pathlib 读写配置文件、日志文件和测试报告
文件操作,说白了就是验证工程师的「基本功」。你想想看,每天跑仿真、看日志、写报告,哪样离得开文件?我刚开始做验证那会儿,还傻乎乎地手动复制粘贴日志内容,后来被 mentor 骂了一顿,才老老实实把文件操作练扎实了。
这一章,咱们就聊聊怎么用 Python 的 open() 和 pathlib 来搞定配置文件、日志文件和测试报告。嗯,这两个工具用好了,能省下你不少头发。
4.1 传统 open() 函数:基本功要扎实
open() 是 Python 内置的文件操作函数,简单直接。我个人习惯用它来处理小文件,比如几百行的配置文件。
4.1.1 基本用法
# 读取配置文件
with open('config.ini', 'r') as f:
content = f.read()
print(content)
# 写入日志文件
with open('sim.log', 'w') as f:
f.write('[INFO] Simulation started\n')
f.write('[INFO] Test case passed\n')
这里有个关键点:一定要用 with 语句。为什么?因为 with 会自动帮你关闭文件,省得你忘了关导致文件句柄泄漏。我在项目中遇到过一位同事,跑了三天三夜的仿真,结果因为文件没关好,日志写到一半就崩了……那叫一个惨。
4.1.2 模式选择
| 模式 | 说明 | 常用场景 |
|---|---|---|
'r' |
只读 | 读取配置文件 |
'w' |
写入(覆盖) | 生成新报告 |
'a' |
追加 | 写日志文件 |
'rb' |
二进制只读 | 读取波形文件 |
我曾经用
'w' 模式打开一个重要的配置文件,结果内容全被清空了……后来我养成了一个习惯:写文件之前,先备份原文件。尤其是配置文件,千万别手滑。
4.2 pathlib:现代文件操作利器
pathlib 是 Python 3.4 引入的模块,它把文件路径当作对象来处理。说实话,我第一次用的时候觉得「这不就是花架子吗?」但用久了才发现,真香。
4.2.1 路径操作
from pathlib import Path
# 创建路径对象
log_dir = Path('./logs')
report_dir = Path('./reports')
# 检查目录是否存在,不存在则创建
log_dir.mkdir(exist_ok=True)
report_dir.mkdir(parents=True, exist_ok=True)
# 拼接路径
log_file = log_dir / 'sim_2024.log'
report_file = report_dir / 'test_report.html'
你看,用 / 拼接路径,比 os.path.join() 清爽多了。我个人习惯用 pathlib 来处理所有路径相关的操作,尤其是跨平台项目——Windows 和 Linux 的路径分隔符不一样,pathlib 会自动帮你处理好。
4.2.2 文件读写
# 读取配置文件
config = Path('config.yaml').read_text()
print(config)
# 写入测试报告
report_content = """
<h1>Test Report</h1>
<p>Passed: 42</p>
<p>Failed: 3</p>
"""
Path('report.html').write_text(report_content)
pathlib 的 read_text() 和 write_text() 方法,内部已经帮你处理了文件打开和关闭。对于小文件,一行代码搞定,省心省力。
4.3 实战:读写配置文件
配置文件在验证项目中太常见了。比如测试用例的参数、仿真环境的设置,都放在配置文件里。我一般用 .ini 或 .yaml 格式。
4.3.1 读取 INI 配置文件
import configparser
config = configparser.ConfigParser()
config.read('test_config.ini')
# 读取参数
sim_time = config.get('SIMULATION', 'time')
test_name = config.get('TEST', 'name')
print(f'Simulation time: {sim_time}')
print(f'Test name: {test_name}')
嗯,这里要注意:configparser 默认会把键名转成小写。如果你需要保留大小写,可以设置 ConfigParser(allow_no_value=True)。
4.3.2 读取 YAML 配置文件
import yaml
with open('config.yaml', 'r') as f:
config = yaml.safe_load(f)
# 读取嵌套参数
sim_time = config['simulation']['time']
test_cases = config['test']['cases']
print(f'Test cases: {test_cases}')
我个人更推荐 YAML 格式,因为它支持嵌套结构,可读性更好。但要注意:
yaml.load() 有安全风险,一定要用 yaml.safe_load()。
4.4 实战:日志文件管理
日志文件是验证工程师的「黑匣子」。仿真出问题了,第一件事就是翻日志。我见过有人把日志写得跟天书一样,结果 debug 的时候自己都看不懂……
4.4.1 使用 logging 模块
import logging
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('simulation.log'),
logging.StreamHandler()
]
)
# 记录日志
logging.info('Simulation started')
logging.warning('Clock frequency mismatch detected')
logging.error('Test case failed: timeout')
你看,用 logging 模块,日志会自动带上时间戳和级别。我曾经接手过一个项目,日志全是 print() 输出的,找问题的时候简直想骂人……从那以后,我坚持用 logging。
4.4.2 日志轮转
from logging.handlers import RotatingFileHandler
handler = RotatingFileHandler(
'sim.log',
maxBytes=10*1024*1024, # 10MB
backupCount=5
)
logging.getLogger().addHandler(handler)
仿真跑个几天几夜,日志文件能大到几十 GB。如果不做轮转,磁盘空间会被撑爆。我建议设置
maxBytes 为 10MB 或 50MB,保留 3-5 个备份文件。
4.5 实战:生成测试报告
测试报告是验证工作的「成绩单」。老板不看你的代码多漂亮,只看报告里 pass/fail 的数字。所以,报告要清晰、直观、自动化。
4.5.1 生成 HTML 报告
from pathlib import Path
def generate_report(test_results):
"""生成测试报告"""
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<title>Test Report</title>
<style>
.pass {{ color: green; }}
.fail {{ color: red; }}
</style>
</head>
<body>
<h1>Test Report</h1>
<table border="1">
<tr>
<th>Test Case</th>
<th>Status</th>
<th>Duration</th>
</tr>
"""
for test in test_results:
status_class = 'pass' if test['status'] == 'PASS' else 'fail'
html_content += f"""
<tr>
<td>{test['name']}</td>
<td class="{status_class}">{test['status']}</td>
<td>{test['duration']}s</td>
</tr>
"""
html_content += """
</table>
</body>
</html>
"""
Path('test_report.html').write_text(html_content)
print('Report generated: test_report.html')
4.5.2 生成 CSV 报告
import csv
def generate_csv_report(test_results):
"""生成 CSV 格式报告"""
with open('test_report.csv', 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow(['Test Case', 'Status', 'Duration'])
for test in test_results:
writer.writerow([
test['name'],
test['status'],
test['duration']
])
print('CSV report generated')
我个人习惯同时生成 HTML 和 CSV 两种格式。HTML 给老板看,直观漂亮;CSV 给自己用,方便后续数据分析。你想想看,老板看到漂亮的 HTML 报告,心情好了,项目验收也顺利多了。
4.6 综合示例:自动化文件操作脚本
最后,咱们来个综合示例。把配置文件读取、日志记录、报告生成串起来,形成一个完整的自动化脚本。
from pathlib import Path
import logging
import configparser
import csv
from datetime import datetime
class VerificationAutomation:
def __init__(self, config_path):
self.config_path = Path(config_path)
self.log_dir = Path('./logs')
self.report_dir = Path('./reports')
# 创建目录
self.log_dir.mkdir(exist_ok=True)
self.report_dir.mkdir(exist_ok=True)
# 配置日志
self.setup_logging()
# 读取配置
self.config = self.read_config()
def setup_logging(self):
log_file = self.log_dir / f'sim_{datetime.now():%Y%m%d_%H%M%S}.log'
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(log_file),
logging.StreamHandler()
]
)
self.logger = logging.getLogger(__name__)
def read_config(self):
config = configparser.ConfigParser()
config.read(self.config_path)
self.logger.info(f'Configuration loaded from {self.config_path}')
return config
def run_tests(self):
"""模拟测试执行"""
test_results = []
test_cases = ['test_alu', 'test_mem', 'test_ctrl']
for test in test_cases:
self.logger.info(f'Running test: {test}')
# 模拟测试结果
result = {
'name': test,
'status': 'PASS',
'duration': 12.5
}
test_results.append(result)
self.logger.info(f'Test {test}: {result["status"]}')
return test_results
def generate_report(self, test_results):
"""生成测试报告"""
report_file = self.report_dir / f'report_{datetime.now():%Y%m%d_%H%M%S}.html'
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<title>Test Report</title>
<style>
body {{ font-family: Arial; margin: 20px; }}
.pass {{ color: green; }}
.fail {{ color: red; }}
table {{ border-collapse: collapse; width: 100%; }}
th, td {{ border: 1px solid #ddd; padding: 8px; }}
th {{ background-color: #4CAF50; color: white; }}
</style>
</head>
<body>
<h1>Test Report</h1>
<p>Generated: {datetime.now()}</p>
<table>
<tr>
<th>Test Case</th>
<th>Status</th>
<th>Duration (s)</th>
</tr>
"""
for test in test_results:
status_class = 'pass' if test['status'] == 'PASS' else 'fail'
html_content += f"""
<tr>
<td>{test['name']}</td>
<td class="{status_class}">{test['status']}</td>
<td>{test['duration']}</td>
</tr>
"""
html_content += """
</table>
</body>
</html>
"""
report_file.write_text(html_content)
self.logger.info(f'Report generated: {report_file}')
def run(self):
"""主流程"""
self.logger.info('=' * 50)
self.logger.info('Verification Automation Started')
self.logger.info('=' * 50)
test_results = self.run_tests()
self.generate_report(test_results)
self.logger.info('=' * 50)
self.logger.info('Verification Automation Completed')
self.logger.info('=' * 50)
# 使用示例
if __name__ == '__main__':
automation = VerificationAutomation('test_config.ini')
automation.run()
- open():适合小文件,记得用
with语句 - pathlib:现代文件操作,路径处理更优雅
- logging:专业日志管理,别再用
print()了 - 报告生成:HTML 给老板看,CSV 给自己用
好了,这一章的内容就到这里。文件操作看似简单,但用好了能极大提升你的工作效率。我建议你把这些代码跑一遍,然后试着改一改,比如加上日志轮转、支持更多配置文件格式。嗯,动手试试吧,光看是学不会的。