【python】CAN报文格式转换工具
Python 实现 CAN 报文 ASC 与 BLF 格式互转,附完整源码
目录
一、背景介绍
在汽车电子和工业控制领域,CAN(Controller Area Network)总线是最常用的现场总线之一。无论是车辆调试、ECU 测试还是数据分析,工程师经常需要与 CAN 日志文件打交道。
目前最常见的两种 CAN 日志存储格式是:
| 格式 | 全称 | 特点 |
|---|---|---|
| ASC | ASCII Logging Format | 文本格式,可读性强,可直接用文本编辑器打开,但文件体积大 |
| BLF | Binary Logging Format | 二进制格式,体积小,读写速度快,适合大数据量场景 |
典型的 ASC 文件内容长这样:
date Tue Jul 27 10:52:55 am 2026
base hex timestamps absolute
Internal events logged
Begin Triggerblock Tue Jul 27 10:52:55 am 2026
10.525500 1 100 Rx d 8 00 00 00 00 00 00 00 00
10.526200 1 101 Rx d 8 11 22 33 44 55 66 77 88
End TriggerBlock
而 BLF 则是紧凑的二进制编码,肉眼无法直接阅读,但解析和存储效率极高。
二、为什么需要格式转换
实际工作中,不同工具对格式的支持各有侧重:
- Vector CANoe / CANalyzer:默认导出 ASC,但分析大数据量时 BLF 更高效
- BUSMASTER:主推 BLF 格式
- python-can 生态:两种格式均支持,方便程序化处理
- 自定义分析脚本:读文本格式(ASC)更简单,但批量处理用 BLF 更快
因此,一个能在 ASC 和 BLF 之间自由切换的小工具会大大提升日常效率。
三、工具概览
本项目提供两个 Python 脚本,基于 python-can 库实现:
| 脚本 | 功能 | 核心类 |
|---|---|---|
asc_to_blf.py |
ASC → BLF | ASCReader + BLFWriter |
blf_to_asc.py |
BLF → ASC | BLFReader + ASCWriter |
特性:
- ✅ 支持单文件转换
- ✅ 支持批量目录转换
- ✅ 支持大小写扩展名(
.asc/.ASC/.blf/.BLF) - ✅ 清晰的进度提示和错误处理
- ✅ 命令行参数灵活
四、环境准备
只需安装 python-can 库:
pip install python-can
推荐 Python 3.7+ 环境。
五、源码:ASC → BLF 转换脚本
5.1 完整代码
#!/usr/bin/env python
"""
ASC to BLF converter using python-can library.
用法: python asc_to_blf.py # 转换当前目录下所有 .asc 文件
python asc_to_blf.py <input.asc> # 转换单个 .asc 文件
python asc_to_blf.py <input.asc> <output.blf> # 指定输出文件名
依赖安装: pip install python-can
"""
import sys
import os
import glob
def convert_asc_to_blf(asc_path, blf_path):
"""将单个 ASC 文件转换为 BLF 文件"""
import can
print(f"正在读取: {asc_path}")
msgs = []
with can.ASCReader(asc_path) as reader:
for msg in reader:
msgs.append(msg)
if not msgs:
print(f"警告: {asc_path} 中未找到任何 CAN 消息,跳过。")
return False
print(f"正在写入 {len(msgs)} 条消息到: {blf_path}")
with can.BLFWriter(blf_path) as writer:
for msg in msgs:
writer.on_message_received(msg)
print(f"转换完成: {asc_path} -> {blf_path}")
return True
def convert_all_asc_in_directory(directory):
"""批量转换目录下所有 .asc 文件"""
asc_files = glob.glob(os.path.join(directory, "*.asc"))
asc_files.extend(glob.glob(os.path.join(directory, "*.ASC"))) # 支持大写扩展名
asc_files = list(set(asc_files)) # 去重
if not asc_files:
print(f"在目录 {directory} 中未找到任何 .asc 文件")
return 0, 0
print(f"找到 {len(asc_files)} 个 ASC 文件")
print("-" * 50)
success_count = 0
fail_count = 0
for asc_path in asc_files:
blf_path = os.path.splitext(asc_path)[0] + ".blf"
try:
if convert_asc_to_blf(asc_path, blf_path):
success_count += 1
else:
fail_count += 1
except Exception as e:
print(f"转换失败: {asc_path} - {e}")
fail_count += 1
print("-" * 50)
print(f"批量转换完成: 成功 {success_count} 个, 失败 {fail_count} 个")
return success_count, fail_count
if __name__ == '__main__':
try:
import can
except ImportError:
print("错误: 未安装 python-can。请执行: pip install python-can")
sys.exit(1)
# 获取当前脚本所在目录
script_dir = os.path.dirname(os.path.abspath(__file__))
if len(sys.argv) >= 2:
# 命令行参数模式:支持单文件或指定目录
input_path = sys.argv[1]
if os.path.isdir(input_path):
# 指定了目录
success, fail = convert_all_asc_in_directory(input_path)
sys.exit(0 if fail == 0 else 1)
elif os.path.isfile(input_path):
# 指定了单个文件
if len(sys.argv) >= 3:
blf_path = sys.argv[2]
else:
blf_path = os.path.splitext(input_path)[0] + ".blf"
try:
if convert_asc_to_blf(input_path, blf_path):
sys.exit(0)
else:
sys.exit(1)
except Exception as e:
print(f"错误: {e}")
sys.exit(1)
else:
print(f"错误: 路径不存在 - {input_path}")
sys.exit(1)
else:
# 无参数:转换当前脚本所在目录下所有 .asc 文件
success, fail = convert_all_asc_in_directory(script_dir)
sys.exit(0 if fail == 0 else 1)
5.2 核心逻辑解析
asc_to_blf.py 的架构分为三层:
- 依赖检查层(
__main__入口):先import can,失败则提示安装 - 路由层(参数解析):根据
sys.argv数量判断进入哪种模式- 无参数 → 批量转换脚本所在目录
- 1 个参数 → 判断是文件还是目录
- 2 个参数 → 指定输入 + 输出文件
- 转换层(核心函数):
convert_asc_to_blf():单个文件的读→写流程convert_all_asc_in_directory():目录遍历 + 统计汇总
六、源码:BLF → ASC 转换脚本
6.1 完整代码
#!/usr/bin/env python
"""
BLF to ASC converter using python-can library.
用法: python blf_to_asc.py # 转换当前目录下所有 .blf 文件
python blf_to_asc.py <input.blf> # 转换单个 .blf 文件
python blf_to_asc.py <input.blf> <output.asc> # 指定输出文件名
依赖安装: pip install python-can
"""
import sys
import os
import glob
def convert_blf_to_asc(blf_path, asc_path):
"""将单个 BLF 文件转换为 ASC 文件"""
import can
print(f"正在读取: {blf_path}")
msgs = []
with can.BLFReader(blf_path) as reader:
for msg in reader:
msgs.append(msg)
if not msgs:
print(f"警告: {blf_path} 中未找到任何 CAN 消息,跳过。")
return False
print(f"正在写入 {len(msgs)} 条消息到: {asc_path}")
with can.ASCWriter(asc_path) as writer:
for msg in msgs:
writer.on_message_received(msg)
print(f"转换完成: {blf_path} -> {asc_path}")
return True
def convert_all_blf_in_directory(directory):
"""批量转换目录下所有 .blf 文件"""
blf_files = glob.glob(os.path.join(directory, "*.blf"))
blf_files.extend(glob.glob(os.path.join(directory, "*.BLF"))) # 支持大写扩展名
blf_files = list(set(blf_files)) # 去重
if not blf_files:
print(f"在目录 {directory} 中未找到任何 .blf 文件")
return 0, 0
print(f"找到 {len(blf_files)} 个 BLF 文件")
print("-" * 50)
success_count = 0
fail_count = 0
for blf_path in blf_files:
asc_path = os.path.splitext(blf_path)[0] + ".asc"
try:
if convert_blf_to_asc(blf_path, asc_path):
success_count += 1
else:
fail_count += 1
except Exception as e:
print(f"转换失败: {blf_path} - {e}")
fail_count += 1
print("-" * 50)
print(f"批量转换完成: 成功 {success_count} 个, 失败 {fail_count} 个")
return success_count, fail_count
if __name__ == '__main__':
try:
import can
except ImportError:
print("错误: 未安装 python-can。请执行: pip install python-can")
sys.exit(1)
# 获取当前脚本所在目录
script_dir = os.path.dirname(os.path.abspath(__file__))
if len(sys.argv) >= 2:
# 命令行参数模式:支持单文件或指定目录
input_path = sys.argv[1]
if os.path.isdir(input_path):
# 指定了目录
success, fail = convert_all_blf_in_directory(input_path)
sys.exit(0 if fail == 0 else 1)
elif os.path.isfile(input_path):
# 指定了单个文件
if len(sys.argv) >= 3:
asc_path = sys.argv[2]
else:
asc_path = os.path.splitext(input_path)[0] + ".asc"
try:
if convert_blf_to_asc(input_path, asc_path):
sys.exit(0)
else:
sys.exit(1)
except Exception as e:
print(f"错误: {e}")
sys.exit(1)
else:
print(f"错误: 路径不存在 - {input_path}")
sys.exit(1)
else:
# 无参数:转换当前脚本所在目录下所有 .blf 文件
success, fail = convert_all_blf_in_directory(script_dir)
sys.exit(0 if fail == 0 else 1)
6.2 核心逻辑解析
blf_to_asc.py 的结构与 asc_to_blf.py 完全对称,唯一的区别在于 Reader/Writer 的组合:
# ASC → BLF
ASCReader(input) → BLFWriter(output)
# BLF → ASC
BLFReader(input) → ASCWriter(output)
这种对称设计得益于 python-can 库统一的 Reader/Writer 接口抽象。
七、使用方法
7.1 ASC 转 BLF
# 方式1:无参数 — 批量转换脚本所在目录下所有 .asc 文件
python asc_to_blf.py
# 方式2:指定单个 ASC 文件 — 同目录生成同名 .blf 文件
python asc_to_blf.py example.asc
# 方式3:指定输入和输出文件
python asc_to_blf.py example.asc output.blf
# 方式4:指定一个目录 — 批量转换该目录下所有 .asc 文件
python asc_to_blf.py D:\my_logs\
运行效果:
找到 5 个 ASC 文件
--------------------------------------------------
正在读取: log1.asc
正在写入 1234 条消息到: log1.blf
转换完成: log1.asc -> log1.blf
...
--------------------------------------------------
批量转换完成: 成功 5 个, 失败 0 个
7.2 BLF 转 ASC
# 方式1:无参数 — 批量转换脚本所在目录下所有 .blf 文件
python blf_to_asc.py
# 方式2:指定单个 BLF 文件 — 同目录生成同名 .asc 文件
python blf_to_asc.py example.blf
# 方式3:指定输入和输出文件
python blf_to_asc.py example.blf output.asc
# 方式4:指定一个目录 — 批量转换该目录下所有 .blf 文件
python blf_to_asc.py D:\my_logs\
运行效果:
找到 3 个 BLF 文件
--------------------------------------------------
正在读取: log1.blf
正在写入 5678 条消息到: log1.asc
转换完成: log1.blf -> log1.asc
...
--------------------------------------------------
批量转换完成: 成功 3 个, 失败 0 个
八、技术原理
两个脚本均基于 python-can 库的标准 Reader/Writer 接口,核心转换流程如下:
# 第一步:读取源文件
with can.XXXReader(input_path) as reader:
msgs = [msg for msg in reader]
# 第二步:写入目标文件
with can.YYYWriter(output_path) as writer:
for msg in msgs:
writer.on_message_received(msg)
| 方向 | 读取器 | 写入器 |
|---|---|---|
| ASC → BLF | can.ASCReader |
can.BLFWriter |
| BLF → ASC | can.BLFReader |
can.ASCWriter |
设计要点:
- 将
import can放在函数内部:在__main__入口先做一次try import检查依赖,转换函数内部按需导入,避免未安装时报错信息不友好 - 返回布尔值表示成功/失败:每个转换函数返回
True/False,便于批量模式下统计 - 空文件检查:消息列表为空时给出警告并返回
False,避免生成无意义的空输出文件 - 扩展名大小写兼容:分别用
*.asc和*.ASC匹配后去重,兼容不同操作系统命名习惯
九、常见问题
Q1:运行报错 “未安装 python-can”
执行以下命令安装依赖:
pip install python-can
Q2:转换后 BLF 文件比 ASC 文件小,是否正常?
完全正常。 ASC 是文本格式,每条 CAN 报文以可读字符串存储(含时间戳、ID、数据等);BLF 是二进制格式,数据以紧凑的字节流编码,通常只有 ASC 文件体积的 1/3 ~ 1/10。
Q3:支持大写扩展名吗(.ASC / .BLF)?
支持。脚本在匹配文件时同时搜索小写和大写扩展名,并通过 set() 去重。
Q4:转换失败怎么排查?
按以下步骤排查:
- 确认源文件完整无损,未损坏
- 确认
python-can版本兼容(建议python-can >= 4.0) - 查看终端输出的具体错误信息,常见问题包括文件编码异常、不支持的 CAN 消息类型等
Q5:可以处理 CAN FD 报文吗?
python-can 库从 4.0 版本开始支持 CAN FD。如果你的 ASC 或 BLF 文件包含 CAN FD 报文,请确保 python-can 版本足够新。
十、总结
本文介绍的两个 Python 脚本利用 python-can 库提供的 Reader/Writer 接口,以极少的代码量实现了 ASC 和 BLF 两种 CAN 日志格式的互转。
核心优势:
- 🚀 代码简洁:每个脚本不到 110 行,核心转换逻辑仅 10 行
- 🔧 灵活实用:支持单文件/批量/指定目录等多种使用方式
- 📦 依赖轻量:仅需
python-can一个第三方库 - 🛡️ 健壮可靠:完善的错误处理和空文件检查
如果你日常工作中经常和 CAN 报文打交道,这两个脚本可以作为工具箱里的常备利器,也欢迎大家在此基础上按需扩展功能。
源码仓库:可直接复制上方代码保存为
.py文件使用。参考文档:python-can 官方文档
更多推荐
所有评论(0)