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 的架构分为三层:

  1. 依赖检查层__main__ 入口):先 import can,失败则提示安装
  2. 路由层(参数解析):根据 sys.argv 数量判断进入哪种模式
    • 无参数 → 批量转换脚本所在目录
    • 1 个参数 → 判断是文件还是目录
    • 2 个参数 → 指定输入 + 输出文件
  3. 转换层(核心函数):
    • 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

设计要点:

  1. import can 放在函数内部:在 __main__ 入口先做一次 try import 检查依赖,转换函数内部按需导入,避免未安装时报错信息不友好
  2. 返回布尔值表示成功/失败:每个转换函数返回 True/False,便于批量模式下统计
  3. 空文件检查:消息列表为空时给出警告并返回 False,避免生成无意义的空输出文件
  4. 扩展名大小写兼容:分别用 *.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:转换失败怎么排查?

按以下步骤排查:

  1. 确认源文件完整无损,未损坏
  2. 确认 python-can 版本兼容(建议 python-can >= 4.0
  3. 查看终端输出的具体错误信息,常见问题包括文件编码异常、不支持的 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 官方文档

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐