【问题标题】:regular expression (perl like)正则表达式(类似 perl)
【发布时间】:2011-10-31 12:09:09
【问题描述】:

$str1="ssh_2-4^accept IN=ETH2 OUT=eth33 MAC=00:d0:c9:96:62:c0:00:1c:f0:98:19:57:08:00 SRC=192.168.200.30 DST=192.168.200.224 LEN=48 TOS=0x00 PREC=0x00 TTL=128 ID=30546 DF PROTO=TCP SPT=10159 DPT=4319 WINDOW=7300 RES=0x00 SYN URGP=0";

$str2="ssh_2-4^accept IN=ETH2 OUT=eth33 MAC=00:d0:c9:96:62:c0:00:1c:f0:98:19:57:08:00 SRC=192.168.200.30 DST=192.168.200.224 LEN=48 TOS=0x00 PREC=0x00 TTL=128 ID=30546 DF PROTO=ICMP 窗口=7300 RES=0x00 URGP=0";

我需要捕捉:

对于 $str1 ==> ssh_2-4, 接受, ETH@, eth33, 192.168.200.30, 192.168.200.224, TCP, 10159, 4319

对于 $str2 ==> ssh_2-4, 接受, ETH@, eth33, 192.168.200.30, 192.168.200.224, ICMP

我使用下面的正则表达式并且对 $str1 非常有效,但不适用于 $str2:

(\w*)\^(\w*).*IN=(\S*).*OUT=(\S*).*SRC=(\S* ).*DST=(\S*).*PROTO=(\S*).*SPT=(\d*).*DPT=(\d*).*

什么是适合这个目的的正则表达式?

【问题讨论】:

  • 它与$str2 不匹配,因为缺少SPTDPT 字段。

标签: regex string perl


【解决方案1】:

对我来说,拆分似乎更健壮和干净。例如:

$str2=~  /^(.*?)\^(\w*)\s+(.*)$/;
my($version,$action,$args) = ($1,$2,$3);
my %argsmap =  split(/[= ]/, $args);
print "proto=$argsmap{'PROTO'} \n";

已编辑:我错误地认为每个“字段”都有一个 key=value 对。固定版本:

  my(@args) = split(/ /,$str2);
  my($version,$action) = split(/\^/,shift @args);
  my %argsmap = map { $_ =~ /(.*)=(.*)/ ? ($1,$2) : ($_,'') } @args;

【讨论】:

  • 我在尝试此操作时收到“奇数个元素”错误。我认为您需要对此进行补偿。
【解决方案2】:
$str1="ssh_2-4^accept IN=ETH2 OUT=eth33 MAC=00:d0:c9:96:62:c0:00:1c:f0:98:19:57:08:00 SRC=192.168.200.30 DST=192.168.200.224 LEN=48 TOS=0x00 PREC=0x00 TTL=128 ID=30546 DF PROTO=TCP SPT=10159 DPT=4319 WINDOW=7300 RES=0x00 SYN URGP=0";
$str2="ssh_2-4^accept IN=ETH2 OUT=eth33 MAC=00:d0:c9:96:62:c0:00:1c:f0:98:19:57:08:00 SRC=192.168.200.30 DST=192.168.200.224 LEN=48 TOS=0x00 PREC=0x00 TTL=128 ID=30546 DF PROTO=ICMP WINDOW=7300 RES=0x00 URGP=0";

foreach my $i ($str1, $str2) {
    if ($i =~ /^(.+)\^(\w+)\s+IN=(\S+)\s+OUT=(\S+).*?SRC=(\S+)\s+DST=(\S+).*?PROTO=(\S+)(?:.*?SPT=(\d+)\s+DPT=(\d+))?/) {
        print "/1=$1/2=$2/3=$3/4=$4/5=$5/6=$6/7=$7/8=$8/9=$9\n";
    }
}

这给了

/1=ssh_2-4/2=accept/3=ETH2/4=eth33/5=192.168.200.30/6=192.168.200.224/7=TCP/8=10159/9=4319
/1=ssh_2-4/2=accept/3=ETH2/4=eth33/5=192.168.200.30/6=192.168.200.224/7=ICMP/8=/9=

在可选的子括号中捕获 SPT 和 DPT 部分:(?:.*?SPT=(\d+)\s+DPT=(\d+))?

【讨论】:

    【解决方案3】:

    贪心量词意味着每次表达式进行匹配时,它将.* 匹配到该行中的所有其余字符。这意味着 每次 匹配它必须消耗输入,找不到下一个表达式,然后回溯直到找到。这是高度低效的。

    相反,您想使用非贪婪形式:.*?。然后为了确保你得到完整的单词/键,你可以使用分词符:\b,就像这样:

    my $re 
        = qr/
            ([\w-]*) \^ (\w*) .*? 
            \bIN=(\S*)  .*?
            \bOUT=(\S*) .*?
            \bSRC=(\S*) .*?
            \bDST=(\S*) .*?
            \bPROTO=(\S*)
            (?: .*? 
                \bSPT=(\d*) 
                .*?
                \bDPT=(\d*)
            )?
        /x;
    

    现在,由于您在每行中都没有 SPT 和 DPT 字段,因此您希望将匹配条件设为 (?:...)?

    这就是我需要做的:

    while ( <$data> ) {
        my @flds = m/$re/;
        print join( ',', grep { defined and length } @flds ), "\n"; 
    }
    

    【讨论】:

      【解决方案4】:

      基于leonbloy's 答案的更加充实的拆分版本。由于元素数量为奇数,直接拆分将不起作用。因此,我们在= 上显式拆分,并允许未定义空值以保留哈希键/值对。

      代码:

      use strict;
      use warnings;
      
      my $str1="ssh_2-4^accept IN=ETH2 OUT=eth33 MAC=00:d0:c9:96:62:c0:00:1c:f0:98:19:57:08:00 SRC=192.168.200.30 DST=192.168.200.224 LEN=48 TOS=0x00 PREC=0x00 TTL=128 ID=30546 DF PROTO=TCP SPT=10159 DPT=4319 WINDOW=7300 RES=0x00 SYN URGP=0";
      my $str2="ssh_2-4^accept IN=ETH2 OUT=eth33 MAC=00:d0:c9:96:62:c0:00:1c:f0:98:19:57:08:00 SRC=192.168.200.30 DST=192.168.200.224 LEN=48 TOS=0x00 PREC=0x00 TTL=128 ID=30546 DF PROTO=ICMP WINDOW=7300 RES=0x00 URGP=0";
      
      my @data;
      for my $str ($str1, $str2) {
          my %hash;
          # First we extract the "header"
          $str =~ s/^([^^]+)\^(\w+) // || die "Did not match header";
          $hash{'version'} = $1;
          $hash{'action'} = $2;
      
          # Now process the args
          for my $line (split ' ', $str) {
              my ($key, $val) = split /=/, $line;
              $hash{$key} = $val;
          }
          # Save the hash into an array
          push @data, \%hash;
      }
      
      for my $href (@data) {
          # Now output the selected elements from each hash
          my $out = join ", ",
              @$href{'version','action','IN','OUT','SRC','DST','PROTO'};
          if ($href->{'PROTO'} eq 'TCP') {
              $out = join ", ", $out, @$href{'SPT', 'DPT'};
          }
          print "$out\n";
      }
      

      输出:

      ssh_2-4, accept, ETH2, eth33, 192.168.200.30, 192.168.200.224, TCP, 10159, 4319
      ssh_2-4, accept, ETH2, eth33, 192.168.200.30, 192.168.200.224, ICMP
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-22
        • 1970-01-01
        • 1970-01-01
        • 2023-03-13
        • 1970-01-01
        相关资源
        最近更新 更多