Chinaunix

标题: 嵌套括号的递归匹配中,look-around assertions不生效 [打印本页]

作者: routesf    时间: 2013-08-21 18:14
标题: 嵌套括号的递归匹配中,look-around assertions不生效
本帖最后由 routesf 于 2013-08-23 11:54 编辑

如下程序,括号{}的递归匹配被DATA中的引号中的一个}干扰,导致最终输出的结果少一个},我试图用(?<!\\)来屏蔽 “dfa-pattern "(ST\x00 01\xC|\x00\xX\x08 00\x\}\x08 0d 0a\x).*";”中的那个导致问题的\},但是不成功,有没有谁用过类似的东西

一个方案是用Text::Balanced,它可以忽略文中处于引号中的{},但是对格式要求比较严格,不如正则灵活,我想用正则来解决这个问题,但是一直没找到可行的方法
  1. #!/usr/bin/perl5.12 -w

  2. use strict;

  3. my $ptn_no_keyword = qr/(
  4.            ([\S]+)\s
  5.                         (
  6.                                 \{
  7.                                         (
  8.                                                 (?:
  9.                                                         (?>[^{}]+)
  10.                                                         |
  11.                                                         (?3)
  12.                                                  )*
  13.                                         )
  14.                                 \}
  15.                         )
  16.         )/x;
  17. local $/;
  18. my $cfg = <DATA>;
  19. if ($cfg =~ /$ptn_no_keyword/) {
  20.                 print "$1\n";
  21. } else {
  22.                 print "Not matched\n";
  23. }
  24. __DATA__
  25. application junos:PCANYWHERE {
  26.         type PCANYWHERE;
  27.         index 121;
  28.         port-mapping {
  29.                 port-range {
  30.                         tcp 5631;
  31.                         udp 5632;
  32.                 }
  33.         }
  34.         signature {
  35.                 port-range {
  36.                         tcp 0-65535;
  37.                         udp 0-65535;
  38.                 }
  39.                 client-to-server {
  40.                         dfa-pattern "(ST|\x00 00 00 00\x).*";
  41.                 }
  42.                 server-to-client {
  43.                         dfa-pattern "(ST\x00 01\xC|\x00\xX\x08 00\x\}\x08 0d 0a\x).*";
  44.                 }
  45.                 min-data 8;
  46.                 order 85;
  47.         }
  48. }
复制代码

作者: mcshell    时间: 2013-08-22 16:08
{:3_186:} 写的有点蛋疼 ,效果实现了,我在引号中多加了几个\{\}回复 1# routesf
  1. #!/usr/bin/perl5.12 -w
  2. use strict;
  3. local $/;
  4. my $cfg = <DATA>;
  5. my @m   = $cfg =~ /"[^"]+"/g;    #取引号中的字符串
  6. my $count;
  7. for (@m) {
  8.     1 while (/\\}(?{$count++})/g);
  9.     1 while (/\\{(?{$count--})/g);    #计算引号中的\{ 或\}个数
  10. }
  11. my $ptn_no_keyword = qr/((?{local $a=$count})(?>(?:([^{}]+)|  
  12.                         {(?{$a++})|      #遇到开括号$a+1
  13.                         }(?(?{$a!=0})(?{$a--})|  #如果$a不等于0则遇到闭括号$a-1
  14.                         (?!)))*)(?(?{$a!=0})(?!)))/x
  15.   ;    # 否则继续匹配,如果继续遇到$a不等于0 继续匹配到行尾
  16. if ( $cfg =~ /$ptn_no_keyword/ ) {
  17.     print "$1\n";
  18. }
  19. else {
  20.     print "Not matched\n";
  21. }
  22. __DATA__
  23. application junos:PCANYWHERE {
  24.         type PCANYWHERE;
  25.         index 121;
  26.         port-mapping {
  27.                 port-range {
  28.                         tcp 5631;
  29.                         udp 5632;
  30.                 }
  31.         }
  32.         signature {
  33.                 port-range {
  34.                         tcp 0-65535;
  35.                         udp 0-65535;
  36.                 }
  37.                 client-to-server {
  38.                         dfa-pattern "(ST|\x00 00 \}00 00\x).*";
  39.                 }
  40.                 server-to-client {
  41.                         dfa-pattern "(ST\x00 01\xC|\x00\xX\x08\} \{00\x\}\x08 0d 0a\x).*";
  42.                 }
  43.                 min-data 8;
  44.                 order 85;
  45.         }
  46. }
复制代码

作者: yinyuemi    时间: 2013-08-22 16:43
回复 1# routesf
  1. my $ptn_no_keyword = qr/(
  2.            ([\S]+)\s
  3.                         (
  4.                                 \{
  5.                                         (
  6.                                                 (?:
  7.                                                         (?>[^{}]+)
  8.                                                         |
  9.                                                         (?3)
  10.                                                         |
  11.                                                         (?>(\}|\{))   # adding another case
  12.                                                  )*
  13.                                         )
  14.                                 \}
  15.                         )
  16.         )/x;
复制代码

作者: routesf    时间: 2013-08-23 11:51
感谢回复,我试了一下这个ptn,发现数据小的时候没问题,如果数据量比较大,则会出现out of memory,可见又加一个alternation对效率的影响非常大,附件是我用的文件,我的机器是CentOS4.5, 1G mem

回复 3# yinyuemi


   
作者: routesf    时间: 2013-08-23 11:55
传不了附件啊
回复 4# routesf


   
作者: routesf    时间: 2013-08-23 11:59
mcshell对regex的了解相当纯熟啊,先感谢,回头好好研究学习一下你的code

回复 2# mcshell


   
作者: yinyuemi    时间: 2013-08-23 12:06
回复 4# routesf


    数据量大的话,一般不推荐re,尤其当使用了回溯,用打点法比较保险和高效
作者: routesf    时间: 2013-08-23 13:14
像mcshell这种算是打点法吗

回复 7# yinyuemi


   
作者: 104359176    时间: 2013-08-23 14:51
看不懂需求,通常这种情况,可以先将捣乱的符号用另外一个符号替换掉,然后在做处理。处理完后,在恢复。
作者: mcshell    时间: 2013-08-23 15:29
回复 8# routesf


    和堆栈的算法差不多。。。不过我也不懂什么算法
作者: routesf    时间: 2013-08-23 16:54
需求是这样的:
1. 有如下文本文件,内容有2种情况,一种是xxx {...多层嵌套{},层数未知},另一种是没有{}的类似 version 10.4;
fxp0 {  
        unit 0 {
            family inet {
                address 10.208.85.48/21 {
                    master-only;
                }      
            }      
        }      
    }
apply-groups [ re0 re1 global "${node}" ];
version 10.4;
lo0 {   
        unit 0 {
            family inet {
                address 74.11.0.1/32;
            }      
        }      
    }
2. 我想用一个正则来匹配所有 xxx {...}, 通过@array = $cfg =~ /$ptn/g把它存到一个数组里,如下正则大部分情况下是没问题的,但处理不了下面这种情况,也就是引号里边出现未匹配的{},但是引号里出现未匹配的{}是允许的
server-to-client {
                    dfa-pattern "(ST\x00 01\xC|\x00\xX\x08 00\x\}\x08 0d 0a\x).*";
                }

my $ptn = qr/(
([\S]+)\s
    (
        \{
            (
                (?:
                    (?>[^{}]+)
                    |
                    (?3)
                )*
            )
        \}
    )
)/x;



回复 9# 104359176


   
作者: yinyuemi    时间: 2013-08-23 17:13
回复 8# routesf
  1. 我觉得是的,下面是我写的,思路基本是一样的,供你参考吧

  2. #!/usr/bin/perl5.12 -w

  3. use strict;

  4. my $tag;
  5. my $text;
  6. while(<DATA>){
  7.     if(/(?<!\\)[{]/){
  8.         $tag = 1;
  9.             $text = $_;       
  10.             while(<DATA>){
  11.             $tag += /(?<!\\)[{]/ ? 1 :
  12.                 /(?<!\\)[}]/ ? -1 : 0;
  13.                 $text .= $_;
  14.             last if ! $tag;
  15.         }
  16.             print $text;
  17.     }
  18. }


  19. __DATA__
  20. xxx
  21. application junos:PCANYWHERE {
  22.         type PCANYWHERE;
  23.         index 121;
  24.         port-mapping {
  25.                 port-range {
  26.                         tcp 5631;
  27.                         udp 5632;
  28.                 }
  29.         }
  30.         signature {
  31.                 port-range {
  32.                         tcp 0-65535;
  33.                         udp 0-65535;
  34.                 }
  35.                 client-to-server {
  36.                         dfa-pattern "(ST|\x00 00 00 00\x).*";
  37.                 }
  38.                 server-to-client {
  39.                         dfa-pattern "(ST\x00 01\xC|\x00\{\xX\x08 00\x\}\x08 0d 0a\x).*";
  40.                 }
  41.                 min-data 8;
  42.                 order 85;
  43.         }
  44. }
  45. XXX

  46. xxx
  47. application junos:PCANYWHERE {
  48.         type PCANYWHERE;
  49.         index 121;
  50.         port-mapping {
  51.                 port-range {
  52.                         tcp 5631;
  53.                         udp 5632;
  54.                 }
  55.         }
  56.         signature {
  57.                 port-range {
  58.                         tcp 0-65535;
  59.                         udp 0-65535;
  60.                 }
  61.                 client-to-server {
  62.                         dfa-pattern "(ST|\x00 00 00 00\x).*";
  63.                 }
  64.                 server-to-client {
  65.                         dfa-pattern "(ST\x00 01\xC|\x00\{\xX\x08 00\x\}\x08 0d 0a\x).*";
  66.                 }
  67.                 min-data 8;
  68.                 order 85;
  69.         }
  70. }
  71. XXX
复制代码

作者: 104359176    时间: 2013-08-23 18:16
你需要用一个算法把所有的字符串,也就是 "" 之间的东西替换掉,把替换结果保存到一个别的数据结构中。
剩下的事情就简单了多了。

code "string1" code "string2"

=> code <s1> code <s2>

{
    s1 => 'string1',
    s2 => 'string2',
}

提取完了,然后在替换回来。
作者: pitonas    时间: 2013-08-23 18:17
yinyuemi 发表于 2013-08-23 05:06
回复 4# routesf
  1. 用打点法比较保险和高效
复制代码

thanks~ what is 打点法? please give me an example about taht ..
作者: routesf    时间: 2013-08-23 18:23
不错的思路,找空尝试一下
回复 13# 104359176


   
作者: routesf    时间: 2013-08-23 18:27
我理解yinyuemi的打点法是用一个statck,遇到左括号就push,右括号就pop,类似这样的

回复 14# pitonas


   
作者: pitonas    时间: 2013-08-23 18:38
回复 16# routesf


    thanks !
please give me an example about taht...please~~
作者: yinyuemi    时间: 2013-08-23 19:27
回复 14# pitonas
  1. #!/usr/bin/env perl

  2. use strict;
  3. my $dots;
  4. my $ext;
  5. while(<DATA>){

  6.         if(/{/){
  7.                 $dots =~ s/^/./;
  8.                 p($_);
  9.                 $ext .= $_;
  10.                 while(<DATA>){
  11.                         /{/ ? do {$dots =~ s/^/./} :
  12.                         /}/ ? do {$dots =~ s/\.//} : do {$dots .= ""};
  13.                         p($_);
  14.                         $ext .= $_;
  15.                         last if $dots !~ /\./;
  16.                 }
  17.         }else{
  18.             p($_)
  19.         }
  20. }

  21. print "\nExtract: \n",$ext,"\n";

  22. sub p(){
  23.         print "Dot: $dots\t\t",shift;
  24. }
  25. __DATA__
  26. ###
  27. xxx{
  28.     yyy{
  29.             zzz{
  30.                 ###
  31.                 }
  32.         }
  33. }
  34. ###

复制代码

作者: yakczh_cu    时间: 2013-08-24 11:09
本帖最后由 yakczh_cu 于 2013-08-24 11:09 编辑
  1.     /{/ ? do {$dots =~ s/^/./} : /}/ ? do {$dots =~ s/\.//} : do {$dots .= ""};
复制代码
这是三目运算吗? 
作者: pitonas    时间: 2013-08-24 17:17
回复 18# yinyuemi
thank you very much!!

   
作者: yakczh_cu    时间: 2013-08-26 19:25

  1. ###
  2. vvv{
  3.         xxx{
  4.             yyy{
  5.                     zzz{
  6.                         ###
  7.                         }
  8.                 }
  9.         }
  10. ###
复制代码
象这种也匹配出来了,明显不匹配
作者: routesf    时间: 2013-08-26 21:56
你用的哪个正则?

回复 21# yakczh_cu


   
作者: yakczh_cu    时间: 2013-08-26 22:43
回复 22# routesf
  1. use strict;

  2. my $depth;
  3. my $last;
  4. my $ext="------";

  5. open FH,q(c:\nginx\conf\nginx.conf);
  6.      while(<FH>){
  7.             $last=$depth;
  8.                        $depth++ if /{/;
  9.                        $depth-- if  /}/;
  10.                        $ext .= $_;
  11.                     
  12.                         if( $depth==0  && $depth < $last ){
  13.                            $ext.="------";
  14.                         }
  15.                 }

  16. if($depth ==0 ){


  17. print "\nExtract: \n",$ext,"\n";
  18. }else {
  19. print "\nnot match";
  20. }
复制代码
改了一个,可以用这个来匹配nginx配置文件




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2