从非结构化消息中提取数据的模式

本文关键字:数据 模式 提取 结构化 消息 | 更新日期: 2023-09-27 18:17:57

我们的一个应用程序接收传感器发送的数据。通过检查消息的内容,应用程序必须弄清楚它正在查看什么类型的消息以及传感器运行在哪个版本的固件上。较新的固件版本发送额外的数据,必须以不同的方式处理。

我在下面提供了一些示例,展示了不同版本的数据消息以及使用不同结构的配置消息。

您可以看到,有些数据是逗号分隔的,而有些数据是用新行分隔的。消息中还有一些标记,可用于确定消息类型。

鉴于我们不能更改消息结构,并且消息不指示其固件版本,我正在寻找解释数据的清晰,可维护,可扩展方法的建议和示例。我们所能做的就是尽可能优雅地处理事情。

旧版本的正常消息

[MSG]
4 031116 080423
543215432154321
3711mV
30
1,0,0
[READINGS]
00451,00450,00402,06017
00000,021116 083000
00000
00000
00000
00000,031116 080000
[MSGEND]

正常消息的更新版本

[MSG]
4 031116 080423
543215432154321
3711mV
30
1,0,0
**0000006216**  <- Extra data added on extra line
[READINGS]
00451,00450,00402,06017
00000,021116 083000
00000
00000
00000
00000,031116 080000
[MSGEND]
<<p> 配置报告/strong>
[MSG]
2 050416 194503
3913mV
30
1,1,0
0000006216
[CONFIG]
543215432154321
234,15,0037,01DE,-60,234,15,0037,42B0,-76
[MSGEND]

从非结构化消息中提取数据的模式

看起来你的一般消息格式是:

[MSG]
<one or more lines>
blank line
<one or more lines>
blank line
[SOME TEXT]  // (i.e. [READINGS or CONFIG]
<one or more lines>
[MSGEND]

至少,您所展示的三条消息具有这种通用格式。

我建议分阶段进行。首先,收集数据,直到在缓冲区中获得完整的消息。也就是说,消息以[MSG]开始,以[MSGEND]结束。在这里,我将忽略沟通失败或腐败的可能性,因为它不会从根本上改变基本思想。以某种方式将完整的消息放入缓冲区。

现在可以开始解析了。看起来消息的第一行(在[MSG]之后)包含一些信息,您可以从中确定它是哪种消息。在您的示例中,正常消息在第一行以'4'开头,配置在第一行以'2'开头。

下一组行似乎给出了特定类型的信息。如果您知道消息类型是什么,那么您就知道如何解释它。在新旧正常消息的情况下,您可以通过第五行来判断它是什么类型的消息。

当然,[CONFIG][READINGS]后面的行给出了具体的信息。

创建用于消息处理的通用模板应该非常简单:

Read "[MSG]"
Read identification information up to a blank line
Read blank line
Read more specific message information -- up to a blank line
Read blank line
Read line with "[CONFIG]" or "[READINGS]"
Read data lines up to "[MSGEND]"
Read "[MSGEND]"

如果您将这些数据行读取到四个不同的列表中,那么您就可以检查数据并确定它是什么类型的消息。从那里,分支到解析和处理特定消息类型的特定函数。

你最终会重复一些代码,但一旦你弄清楚这是否是你想要的处理方式,你总是可以抽象出来的。

这当然是清晰和可维护的。它是否可扩展取决于你对可扩展的定义。使用此模型,您可以轻松地添加新类型的消息及其相应的处理方法,只要所有消息都遵循相同的通用格式。

我要提醒大家不要尝试创建一个正则表达式,它可以接受一条消息并将其拆分为相关部分。经过足够的思考,您可能可以创建这样一个正则表达式。可以肯定的是,这个正则表达式对大多数人来说几乎是不可读的,很难修改和证明是正确的,而且很可能对轻微的格式偏差是非常不可原谅的。

我认为如果您分析足够大的消息样本(例如100个),您将注意到一个模式。携带传感器读数的非结构化消息非常罕见。

一旦确定了

,您可能会发现按类型匹配消息(例如,根据您的示例,数据消息和配置)是最简单和最干净的。例如,这个正则表达式将匹配"旧/遗留"或新消息,但不匹配配置消息。如果消息是"新",将有2个捕获组-第一个带有10位固件,第二个带有数据(传感器读数)。如果消息是"old/legacy",则正则表达式将只包含1个捕获组,其传感器读数为:

^'[MSG']'n['d ]+'n{2}'d{15}'n'd{4}mV'n'd{2}'n'd,'d,'d'n?'*{0,2}('d{10})?'*{0,2}'n{2}'[READINGS']'n(?m:(['d's,]*))'[MSGEND']$

当然,我的例子只是一个例子,它需要更多的数据来验证,更多的捕获组来确定传感器类型等;但这就是我对你问题的看法。

Regex资源:

在regex101.com上有这个正则表达式的链接。

我也很喜欢regexper.com,因为它可以显示正则表达式(恕我直言,它们写起来比读起来容易)。这是同一个正则表达式的可视化。

注意,我从数据捕获组的可视化中删除了多行标志- (?m: