使用perl正则表达式捕获可选字符串

时间:2013-07-19 09:24:29

标签: regex perl

我正在尝试解析具有此模式的字符串

src [interface_name:source_address[/source_port]] 

括号中的部分是可选的。所以有3种可能的变种

src
src LAN:10.115.1.204
src LAN:10.115.1.204/8080

我想从此字符串中捕获界面源IP 源端口

我的第三个变体的正则表达式是

($srcinterface,$srcip,$src_port) = m/^src (.*?):(.*?)\/(.*?)/;

但我不知道如何制作适用于所有3种变体的正则表达式。

修改 问题的大部分是像src dst信息也从系统接收,我需要重复正则表达式。见下面的字符串: -

src dst outside:125.22.32.192
src outside:182.201.183.178 dst outside:125.22.32.192
src outside:182.201.183.178/5525 dst outside:125.22.32.192/8595

4 个答案:

答案 0 :(得分:1)

改为使用:

/^src(?> (\w++):((?>[0-9]{1,3}\.){3}[0-9]{1,3})(?>\/([0-9]++))?)?/

示例脚本:

#!/usr/bin/perl

use strict;

my $str = "src
src LAN:10.115.1.204
src LAN:10.115.1.204/8080";
my $i = 0;
while($str =~ /^src(?> (\w++):((?>[0-9]{1,3}\.){3}[0-9]{1,3})(?>\/([0-9]++))?)?/gm) {
print "\n[match " . ++$i . "]"
    . "\nWhole match    : $&"
    . "\nCapture group 1: $1"
    . "\nCapture group 2: $2"
    . "\nCapture group 3: $3\n";
}

对于更宽松的模式,您可以使用:

/^src(?> (\w++):([^\/\n]++)(?>\/([^\n]++))?)?/gm

或者这个:

/^src(?> (\w++):([^\/\n]++)(?>\/(\S++))?)?/gm

这些模式的想法是使用否定的字符类,例如[^\/\n]表示所有不是斜杠或换行符的字符。您可以轻松地根据需要添加或删除字符来调整这些类。

答案 1 :(得分:1)

我不是Perl大师,但也许这有效:

($srcinterface,$srcip,$src_port) = m/^src\s*(?:(.*?):(.*?)(?:\/(.*?))?)?/;

?:应该将其设为隐藏组,组末尾的?使其成为可选组。

嗯,可读性变得混乱......

答案 2 :(得分:1)

目前尚不清楚哪些字段是可选字段,但您可以简单地拆分正则表达式来分隔那些字段。

在此程序中,@fields数组将包含指定的字段数。假设可选字段从右侧消失(即,没有源地址没有接口名称,没有源端口,没有名称和地址),您可以简单地计算@fields中的字段以查看提供的字段。

use strict;
use warnings;

use Data::Dump;

for (
    'src',
    'src LAN:10.115.1.204',
    'src LAN:10.115.1.204/8080') {

    my @fields = split /[\/\s]+/;

    dd \@fields;
}

<强>输出

["src"]
["src", "LAN:10.115.1.204"]
["src", "LAN:10.115.1.204", 8080]

答案 3 :(得分:1)

这个正则表达式对我有用

($srcinterface, $srcip, $src_port) = m@^src (?:([^:]+):([^/]+))?(?(1)(?:/(.+))?)@;

注意:

  • 我正在使用否定的字符类(例如[^:])和+因为.*?会导致变体2和3出现问题,因为正则表达式遵循以下.*?定义不明确(简单地说,.*?将与零长度字符串匹配)。

  • 我将 interface_name:source_address 部分设为可选,并附上(?:...)?

  • 然后我使用了条件正则表达式(?(1)pattern),这意味着“如果捕获组1成功匹配则匹配pattern

    实际上,如果匹配 interface_name:source_address ,请查找 / port

  • 由于 / port 是可选的,我将该部分包装在条件正则表达式中的另一个(?:...)? 里面。

对于它的价值,我认为Borodin's split-based answer 方式更简单,Casimir et Hippolyte's regex-based answer在稳健性方面更好,因为它实际上验证了每个组件。我只是为了完成而发布这个。