Matlab编程技巧:通过正则表达式解析DBC文件

本文介绍如何使用Matlab的正则表达式函数regexp解析汽车CAN网络通信中的DBC文件,提取报文帧信息。通过实例演示了如何匹配和提取DBC文件中的关键数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文通过Matlab函数regexp的正则表达式功能,解析DBC文件中的报文帧信息。DBC文件中的信号等其他信息都可以通过类似的方式解析出来。

1 DBC文件

DBC数据库文件是用来描述CAN网络节点间数据通讯的一种文件,做汽车CAN网络通信的话肯定是绕不开DBC文件的。

关于DBC文件格式的内容可以参考另一位博主的文章关于DBC文件的格式解析(DBC文件系列其二),本文后面也会引用到其中的一些知识内容。

DBC文件的格式有很强的规律性,所以可以很轻松地通过正则表达式函数regexp解析出其中地信息。实际上,Matlab提供了封装好的函数canDatabase(),可以更加快速准确地解析DBC中的帧和信号。本文没有使用封装的函数,是希望以DBC为例,训练正则表达式的应用技巧,以便在遇到其他需求没有现成的函数时,自己也能造出轮子。

2 正则表达式函数

在Matlab中,正则表达式函数是regexp。关于正则表达式的语法不用死记硬背,用到的时候去查Matlab帮助文档就行。

3 实例:解析报文帧信息

首先,自己新建一个DBC文件,一般是通过CANdb++这类DBC工具。不过这里为了方便就不去手动建立了,直接把如下内容拷贝进文本文件,并命名为DBC_Demo.dbc。

BS_:
BU_: AVNT ACU HUD
BO_996 HUD_1_B: 8 HUD
 SG_ HUD_OffSt : 0|1@0- (1,0) [-1|0] "" ACU,AVNT
 SG_ HUD_HeightLv : 7|7@0+ (1,0) [0|127] "lv" ACU,AVNT
 SG_ HUD_BrightnessLv : 15|4@0+ (1,0) [0|15] "lv" ACU,AVNT
CM_ BO_ 996 "The message of control hud status";
CM_ SG_ 996 HUD_BrightnessLv "Control hud brighness level";

其中,BO_开头的那一行就是报文帧,其格式定义如下:
在这里插入图片描述

4 Matlab脚本

1)首先要将DBC文件的文本内容导入到Matlab中。在Matlab命令行输入

DBCText = fileread('DBC_Demo.dbc')

就会将DBC文件内容作为字符串返回到DBCText变量中。
在这里插入图片描述
2)用正则表达式提取出报文帧那一行,也就是BO_开头的那一行。这里开始就要研究BO_这一行的模式了。

  1. BO关键字,在正则表达式模式中直接用BO_完全匹配即可;
  2. 报文ID,是若干个数字可以用\d+表示1个或以上的阿拉伯数字;
  3. 报文名称,一般是由字母、数字和下划线组成的字符串,所以可以用\w+表示;
  4. 报文数据域字节数,无符号整数,直接用\d+表示;
  5. 网络节点:一般是由字母、数字和下划线组成的字符串,所以可以用\w+表示;

所以报文帧这一行的模式可以写为:

>> pattern = 'BO_\s*\d+\s+\w+:\s+\d+\s+\w+';

中间的\s+或者\s*代表空格符。

然后就可以通过regexp解析出BO这一行了。注意加上’match’这个参数以返回字符串。

>> BO_Cell = regexp(DBCText,pattern,'match')

这里要注意,返回的的是一个字符串组成的元胞数组。由于demo中只有一个报文帧的定义,所以元胞数组中只有一个字符串。
在这里插入图片描述
3)解析元胞数组中的每一个报文帧行,提取出信息,这里只以一个报文帧为例。首先解析出报文ID,只要把之前的pattern改一下就行。

pattern_MessageId = '(?<=BO_\s*)\d+(?=\s+\w+:\s+\d+\s+\w+)';

在这里插入图片描述
这里用了正则表达式的上下文匹配,语法是*(?=test)(?<=test)*,其中test是上下文内容。这样就能根据上下文匹配出报文帧了。

再比如说报文名称也用上下文匹配:

>> MessageName_Cell = regexp(BO_Cell{1},'(?<=BO_\s*\d+\s+)\w+(?=:\s+\d+\s+\w+)','match')

MessageName_Cell =

  1×1 cell 数组

    {'HUD_1_B'}

5 总结

用正则表达式解析DBC文件比较容易,只是需要一点耐心慢慢地调。除了报文帧,其他信号、波特率、节点等信息都能通过类似的方式解析。这样的技术也可以迁移到其他文件,只要我们了解了该文件的书写规则就行。

>>返回个人博客总目录

附 完整代码

测试用的DBC文件如下:

BS_:
BU_: AVNT ACU HUD
BO_996 HUD_1_B: 8 HUD
 SG_ HUD_OffSt : 0|1@0- (1,0) [-1|0] "" ACU,AVNT
 SG_ HUD_HeightLv : 7|7@0+ (1,0) [0|127] "lv" ACU,AVNT
 SG_ HUD_BrightnessLv : 15|4@0+ (1,0) [0|15] "lv" ACU,AVNT
CM_ BO_ 996 "The message of control hud status";
CM_ SG_ 996 HUD_BrightnessLv "Control hud brighness level";

博主自己写的脚本,可能会有考虑不周的地方,不一定适用于任何DBC文件。但是思路和方法是这样的,可以在遇到问题时尝试修改脚本,不断迭代。脚本内容如下:

function Message_Struct = ImportDBC()
%% 导入DBC文本
DBCText = fileread('DBC_Demo.dbc');

%% 解析BO_和SG_
pattern = 'BO_\s*\d+\s+\w+:\s+\d+\s+\w+\s*\r\n(\s*SG_.*?\r\n)*';
BO_Cell = regexp(DBCText,pattern,'match');

for i = 1:length(BO_Cell)
    BO_Str = BO_Cell{i};
    Message_Struct = Parse_Message(BO_Str);
    SG_Cell = regexp(BO_Str,'SG.*?\r\n','match');
    Signal_Cell = {};
    for j = 1:length(SG_Cell)
        SG_Str = SG_Cell{j};
        Signal_Struct = Parse_Signal(SG_Str);
        Signal_Cell{end+1} = Signal_Struct;
    end
    Message_Struct = setfield(Message_Struct,'Signal',Signal_Cell);
end
end

%解析信号属性
function Signal_Struct = Parse_Signal(SG_Str)
%pattern:SG_\s*\w+\s*:\s*\d+\|\d+@(0|1)\s*(\+|\-)\s*\(-*\d+,-*\d+\)\s*\[-*\d+\|-*\d+\]\s*"\w*"\s*(\w|\,)+\r\n
%SignalName
Temp = regexp(SG_Str,'(?<=SG_\s*)\w+(?=\s*:\s*\d+\|\d+@(0|1)\s*(\+|\-)\s*\(-*\d+,-*\d+\)\s*\[-*\d+\|-*\d+\]\s*"\w*"\s*(\w|\,)+\r\n)','match');
SignalName = Temp{1};
%StartBit
Temp = regexp(SG_Str,'(?<=SG_\s*\w+\s*:\s*)\d+(?=\|\d+@(0|1)\s*(\+|\-)\s*\(-*\d+,-*\d+\)\s*\[-*\d+\|-*\d+\]\s*"\w*"\s*(\w|\,)+\r\n)','match');
StartBit = Temp{1};
%SignalSize
Temp = regexp(SG_Str,'(?<=SG_\s*\w+\s*:\s*\d+\|)\d+(?=@(0|1)\s*(\+|\-)\s*\(-*\d+,-*\d+\)\s*\[-*\d+\|-*\d+\]\s*"\w*"\s*(\w|\,)+\r\n)','match');
SignalSize = Temp{1};
%ByteOrder
Temp = regexp(SG_Str,'(?<=SG_\s*\w+\s*:\s*\d+\|\d+@)(0|1)(?=\s*(\+|\-)\s*\(-*\d+,-*\d+\)\s*\[-*\d+\|-*\d+\]\s*"\w*"\s*(\w|\,)+\r\n)','match');
if(strcmp(Temp{1},'0'))
    ByteOrder = 'Motorola';
else
    ByteOrder = 'Intel';
end
%ValueType
Temp = regexp(SG_Str,'(?<=SG_\s*\w+\s*:\s*\d+\|\d+@(0|1)\s*)(\+|\-)(?=\s*\(-*\d+,-*\d+\)\s*\[-*\d+\|-*\d+\]\s*"\w*"\s*(\w|\,)+\r\n)','match');
if(strcmp(Temp{1},'+'))
    ValueType = 'Unsigned';
else
    ValueType = 'Signed';
end
%Factor
Temp = regexp(SG_Str,'(?<=SG_\s*\w+\s*:\s*\d+\|\d+@(0|1)\s*(\+|\-)\s*\()-*\d+(?=,-*\d+\)\s*\[-*\d+\|-*\d+\]\s*"\w*"\s*(\w|\,)+\r\n)','match');
Factor = Temp{1};
%Offset
Temp = regexp(SG_Str,'(?<=SG_\s*\w+\s*:\s*\d+\|\d+@(0|1)\s*(\+|\-)\s*\(-*\d+,)-*\d+(?=\)\s*\[-*\d+\|-*\d+\]\s*"\w*"\s*(\w|\,)+\r\n)','match');
Offset = Temp{1};
%Min
Temp = regexp(SG_Str,'(?<=SG_\s*\w+\s*:\s*\d+\|\d+@(0|1)\s*(\+|\-)\s*\(-*\d+,-*\d+\)\s*\[)-*\d+(?=\|-*\d+\]\s*"\w*"\s*(\w|\,)+\r\n)','match');
Min = Temp{1};
%Max
Temp = regexp(SG_Str,'(?<=SG_\s*\w+\s*:\s*\d+\|\d+@(0|1)\s*(\+|\-)\s*\(-*\d+,-*\d+\)\s*\[-*\d+\|)-*\d+(?=\]\s*"\w*"\s*(\w|\,)+\r\n)','match');
Max = Temp{1};
%Unit
Temp = regexp(SG_Str,'(?<=SG_\s*\w+\s*:\s*\d+\|\d+@(0|1)\s*(\+|\-)\s*\(-*\d+,-*\d+\)\s*\[-*\d+\|-*\d+\]\s*)"\w*"(?=\s*(\w|\,)+\r\n)','match');
Unit = Temp{1};
%Receiver
Temp = regexp(SG_Str,'(?<=SG_\s*\w+\s*:\s*\d+\|\d+@(0|1)\s*(\+|\-)\s*\(-*\d+,-*\d+\)\s*\[-*\d+\|-*\d+\]\s*"\w*"\s*)(\w|\,)+(?=\r\n)','match');
Receiver = Temp{1};
%Signal_Struct
Signal_Struct = struct('SignalName',SignalName,'StartBit',StartBit,'SignalSize',SignalSize,'ByteOrder',ByteOrder,'ValueType',ValueType,...
    'Factor',Factor,'Offset',Offset,'Min',Min,'Max',Max,'Unit',Unit,'Receiver',Receiver);
end

% 解析报文属性
function Message_Struct = Parse_Message(BO_Str)
%pattern:'BO_\s*\d+\s+\w+:\s+\d+\s+\w+'
%MessageId
Temp = regexp(BO_Str,'(?<=BO_\s*)\d+(?=\s+\w+:\s+\d+\s+\w+)','match');
MessageId = Temp{1};
%MessageName
Temp = regexp(BO_Str,'(?<=BO_\s*\d+\s+)\w+(?=:\s+\d+\s+\w+)','match');
MessageName = Temp{1};
%MessageSize
Temp = regexp(BO_Str,'(?<=BO_\s*\d+\s+\w+:\s+)\d+(?=\s+\w+)','match');
MessageSize = Temp{1};
%Transmitter
Temp = regexp(BO_Str,'(?<=BO_\s*\d+\s+\w+:\s+\d+\s+)\w+','match');
Transmitter = Temp{1};
%Message_Struct
Message_Struct = struct('MessageId',MessageId,'MessageName',MessageName,'MessageSize',MessageSize,'Transmitter',Transmitter,'Signal','');
end
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chhttty

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值