Regex 我有原始数据。如何使用多个不同的分隔符匹配可变长度
我有上面模式中的原始hextump数据。08、09和1A是分隔符。问题是D列和F列可能是09。可以匹配正则表达式吗?我需要这些分隔符之间的数据 我的代码不准确:Regex 我有原始数据。如何使用多个不同的分隔符匹配可变长度,regex,perl,Regex,Perl,我有上面模式中的原始hextump数据。08、09和1A是分隔符。问题是D列和F列可能是09。可以匹配正则表达式吗?我需要这些分隔符之间的数据 我的代码不准确: $dat[1] = "\x08\xB3\xE3\x0C\x09\x07\x4D\x6F\x68\x61\x6D\x65\x64\x1A"; $dat[2] = "\x08\x84\x03\x09\x03\x53\x6F\x6C\x6C\x1A"; $dat[3] = "\x08\xD4\xEA\x0E\x09\x03\x54\x6F\x
$dat[1] = "\x08\xB3\xE3\x0C\x09\x07\x4D\x6F\x68\x61\x6D\x65\x64\x1A";
$dat[2] = "\x08\x84\x03\x09\x03\x53\x6F\x6C\x6C\x1A";
$dat[3] = "\x08\xD4\xEA\x0E\x09\x03\x54\x6F\x6C\x1A";
$dat[4] = "\x08\xD5\x09\x03\x55\x6F\x6C\x1A";
$dat[5] = "\x08\xD4\xEA\x09\x09\x03\x54\x6F\x6C\x1A";
$dat[6] = "\x08\xD4\xEA\xOE\x09\x09\x54\x6F\x6C\x61\x6D\x65\x64\x61\x61\x1A";
$dat[7] = "\x08\xD4\xEA\x09\x09\x09\x54\x6F\x6C\x61\x6D\x65\x64\x61\x61\x1A";
我假设记录格式定义如下: 每个记录由以类型开头的字段组成,例如08、09、1A。 字段类型1A是一种特殊类型,表示记录结束。 所有记录都有一个1A型字段。 字段类型08后面是使用编码的数字。 字段类型09后面跟着一个字节,该字节定义字段其余部分的字节数,该字节似乎是ASCII编码的字符串。另一个合理的假设是字段类型09后面跟着一个字节,该字节定义了使用UTF-8编码的代码点的数量。 一个记录不能有两个相同类型的字段。 我没有对以下内容做出任何假设:
m/\x08(.+?\x09?)\x09(.+?)\x1A/s;
是否必须存在08类型的字段。
是否必须存在类型为09的字段。
字段的顺序。
要解析此类记录,可以使用以下命令:
m/\x08(.+?\x09?)\x09(.+?)\x1A/s;
$dat[4]似乎是无效数据。至少第一个字段应该包含第二个字节,因为D5表示后面至少还有一个字节
$dat[2]也是无效数据,因为0x09的长度字段为0x03,但字段本身包含四个字符
$dat[5]包含无效的十六进制转义。我使用\xE0而不是\xEO
通过这两个更正,您可以使用以下函数解析输入消息:
for ($file) { # Makes $_ an alias for $file.
REC: while (1) {
my %rec;
FIELD: while (1) {
my $field_start = pos() || 0;
if (!/\G ( . )/sxgc) {
last REC if !%rec;
die("Premature EOF\n");
}
if ($type eq "\x1A") {
last;
}
elsif ($type eq "\x08") {
!exists($rec{"09"})
or warn(sprintf("Duplicate field of type %02X at pos %s\n", $type, $field_start));
/\G ( [\x80-\xFF]*[\x00-\x7F] ) /sxgc
or die(sprintf("Bad field of type %02X at pos %s\n", $type, $field_start));
$rec{"08"} = unpack("w", "$1");
}
elsif ($type eq "\x09") {
!exists($rec{"09"})
or warn(sprintf("Duplicate field of type %02X at pos %s\n", $type, $field_start));
/\G ( . ) /sxgc
or die(sprintf("Bad field of type %02X at pos %s\n", $type, $field_start));
my $len = ord($1);
length() >= pos() + $len
or die(sprintf("Bad field of type %02X at pos %s\n", $type, $field_start));
$rec{"09"} = substr($_, pos(), $len);
pos() += $len;
}
else {
die(sprintf("Unrecognized record type %02X at pos %s\n", $type, $field_start));
}
}
# Do something with %rec
}
}
解包模板指的是:
x-扔掉这个字节0x08
w-读取BER编码的数字
x-扔掉这个字节0x09
C-读取此字节并将其用作以下字符串的长度
a-读取下一个字节并将其用作字符串
x-扔掉这个字节0x1A
如果还想保留字段编号,请使用
my( $number, $name ) = unpack 'xwxC/ax', $d;
至少对于显示的数据,解包模板是有效的,符合我所述的假设。如果这是实际的ASN.1数据,那么应该有更多的验证等,如果字段分隔符可能丢失,那么@ikegami所示的基于regexp的方法肯定更可靠
固定/动态字段顺序
模板依赖于字段的固定顺序。如果字段顺序不一定固定,则需要根据循环中每个字段的类型确定解包模板。这使得解包方法接近池上的方法
unpack 'CwCC/aC', $d
有关固定字段顺序,请参阅以下完整程序:
my ($message_type), $d = unpack 'CA*', $d;
if( $message_type eq "\x08" ) {
my ($number), $d = unpack 'wA*', $d;
print "Field 0x08: $number\n";
} elsif ...
输出
另见
你的输入格式是什么?当然,它不是图片,因为不能在图像中的文本上使用正则表达式。是Excel文件吗?还是CSV文件?您向我们显示的列的分隔符是什么?文件里有12和1A吗\x12是一个控制字符,而不是两个字符1和2。请提供答案和您的问题,告诉我们您的实际投入是什么。如果它包含控制字符,您可以使用Data::Dumper将其字符串化并显示给我们。这是原始hexdump数据。是的,F列是字节数。请再次刷新。我刚刚编辑过。是的,非常好。我紧急编辑了好几次这个问题。使用解包'xwxC/ax'可能是个有趣的主意,但我懒得去检查/解决它。令我惊讶的是,解包确实有效,但如果输入数据像OP显示的那样不可靠,而不是像某些东西所建议的那样仅仅是键入错误,那么基于正则表达式的方法就更明智了。@Corion,哦,我没有意识到数字格式是一个标准的支持包!不过,您对字段的顺序和每种类型的字段的数量做了一些额外的假设。是的,在某些时候总是需要进行错误检查/报告,所以现在解包可能不错,但这可能只是一个临时解决方案。最终需要一个合适的解析器,比如我发布的解析器。不一定要重新设计,抱歉。数据字段08只是示例,因为我急于编辑问题。其中一些是无效的@ต้องเอกมัย - 如果输入无效,您希望如何正确解析输入?!你们两个都很酷@ikegami在上一个问题上帮了我很多忙。真实的数据是非常有效的。所以我认为解包方法很容易使用。我看到有人使用延迟评估。它也很好吗/\x08.+?\x09.?{$n=ord$1;{$n}}\x1A/
$VAR1 = [
8,
848268,
9,
'Mohamed',
26
];
Field 0x08: 848268
Field 0x09: Mohamed
Field 0x1A
$VAR1 = [
8,
515,
9,
'Sol',
26
];
Field 0x08: 515
Field 0x09: Sol
Field 0x1A
$VAR1 = [
8,
1389838,
9,
'Tol',
26
];
Field 0x08: 1389838
Field 0x09: Tol
Field 0x1A
$VAR1 = [
8,
1389833,
9,
'Tol',
26
];
Field 0x08: 1389833
Field 0x09: Tol
Field 0x1A
$VAR1 = [
8,
1389838,
9,
'Tolamedaa',
26
];
Field 0x08: 1389838
Field 0x09: Tolamedaa
Field 0x1A
$VAR1 = [
8,
1389833,
9,
'Tolamedaa',
26
];
Field 0x08: 1389833
Field 0x09: Tolamedaa
Field 0x1A