Chinaunix

标题: 正则表达式能否捕捉多重括号中对等括号中的内容? [打印本页]

作者: hztj2005    时间: 2016-08-02 15:04
标题: 正则表达式能否捕捉多重括号中对等括号中的内容?
本帖最后由 hztj2005 于 2016-08-02 15:05 编辑

正则表达式能否捕捉对等括号中的内容?
100-( 5*(4+89-(34/5)))

我的意思是能否捕捉: -号后的 ( 5*(4+89-(34/5)))
                             *号后的(4+89-(34/5))

谢谢!
作者: 104359176    时间: 2016-08-02 20:16
这要用到正则中的递归,这个很复杂,不如直接写一个遍历,自己设置层数,进行捕获。

if meet '(' -> depth++
if meet ') -> depth--
作者: sunzhiguolu    时间: 2016-08-02 20:27
本帖最后由 sunzhiguolu 于 2016-08-02 20:32 编辑

Jason680 大神学的, 不知道这样用是否正确, 如有误, 还请大家指正:
参见 - 11楼, 17楼
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;

  4. local $_ = "100-( 5*(4+89-(34/5)))";
  5. print "$1\n$2\n" while (m/-((.*?(((?:[^(]|(?2))*))))/g);
复制代码
perl abc.pl
-----------------------------------------------------------
( 5*(4+89-(34/5)))
(4+89-(34/5))


作者: 104359176    时间: 2016-08-02 21:13
This code is difficult to write, read and change.
作者: hztj2005    时间: 2016-08-02 22:21
回复 2# 104359176

我也是用程序实现的。
正则表达式从内向外,从左到右,捕捉括号对,push到 一个 数组。再根据需要pop。


   
作者: hztj2005    时间: 2016-08-02 22:23
回复 3# sunzhiguolu

我表示看不懂。
不过,谢谢了。有时间再来研究。
   
作者: sunzhiguolu    时间: 2016-08-02 22:47
本帖最后由 sunzhiguolu 于 2016-08-02 22:53 编辑
104359176 发表于 2016-08-02 20:16
这要用到正则中的递归,这个很复杂,不如直接写一个遍历,自己设置层数,进行捕获。

if meet '(' -> dep ...

大神, 在这个地方我与您的观点不同.
利用正则的递归功能可以有效的缩减代码量, 而且我感觉这个功能非常适合做这些 (深层次的 OR 层次不确定) 的成对匹配操作.
可能会造成一些开销, 这些开销对于现在的计算机来说 应该问题不大吧, 有不对之处请指正.

作者: sunzhiguolu    时间: 2016-08-02 22:56
本帖最后由 sunzhiguolu 于 2016-08-02 22:57 编辑

回复 5# hztj2005
正则表达式从内向外,从左到右,捕捉括号对,push到 一个 数组。再根据需要pop。

大神您能否提供一个示例代码, 关于您说的那个解决方案. 先谢谢大神了.

   
作者: RE_HASH    时间: 2016-08-03 01:58
递归一个
$>  echo '100-(5*(4+89-(34/5)))'|perl -ne '$,="\n"; print /(\((\d[^(]+|(?R))*\))/g;'
(5*(4+89-(34/5)))
(4+89-(34/5))

作者: RE_HASH    时间: 2016-08-03 02:04
带空格的:
$>  echo '100-( 5* ( 4+89-( 34/5 ) ) )'|perl -ne '$,="\n"; print /(\(\s*([^(]+?|(?R)\s*)*\))/g;'
( 5* ( 4+89-( 34/5 ) ) )
( 4+89-( 34/5 ) )

作者: 104359176    时间: 2016-08-03 10:26
在语言中,这种嵌套的结构非常普遍,而这种嵌套是最简单的单结构。如果同时有几个结构嵌套在一起。

    (1,2,3,[4,5,6,{7,8}], (9,10))

想同时获取这些嵌套结构,而且清楚的表明结构,用正则表达式处理,就是到了天花板了。

我们解析文本,就是为了能将反映文本信息的数据结构提取出来。如果只提取了数据,没有结构。
就会失去很多信息,而用普通正则去还原这些结构,就是在刀尖上跳舞了。

所以,如果对解析数据技术想深入研究的,就学习 Perl6, 用它的 grammar 来描述更加复杂的文本结构。
让递归捕获和数据结构重组交给语言本身,那么就能做出用途更广的解析代码。
作者: jason680    时间: 2016-08-03 10:45
本帖最后由 jason680 于 2016-08-05 02:39 编辑

Dear All:  给回应者,楼主:

  说困难很困难,要解析结构文法(grammar)+ debug + ...
     对学过的文法架构解析也是很简单,写出正确文法描述,就能做出所要(四则运算)代码。
     S -> T + T | T - T | T
     T -> F * F | F / F | F
     F -> NUMBER | '(' S ')'
     NUMBER ->  0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

  说简单很简单,只要求值(value) ---- eval function

$ echo "100- ( 5* (4+89- (34/5) ) )" | perl -lpe 'print eval'
-331

作者: hztj2005    时间: 2016-08-03 23:17
sunzhiguolu 发表于 2016-08-02 22:56
回复 5# hztj2005

大神您能否提供一个示例代码, 关于您说的那个解决方案. 先谢谢大神了.


完全正则表达式可能就有困难,但程序实现好简单的,不用代码。
1、用一个正则表达式  m/(  \(   [^\(\)]  \)  )/ ,$1就捕捉到最里层的括号内容了。push 到一个数组。
2、再用一个正则表达式  s/\(   ([^\(\)])  \) /{ $1}/,把里层的括号替换掉。
继续循环就可以了,当然最后结果,需要把{}替换成()。不用大括号,用【】也可以。

















]
作者: sunzhiguolu    时间: 2016-08-04 21:47
谢谢大神指点!
作者: hztj2005    时间: 2016-08-05 00:41
104359176 发表于 2016-08-03 10:26
在语言中,这种嵌套的结构非常普遍,而这种嵌套是最简单的单结构。如果同时有几个结构嵌套在一起。

     ...


以前看编译原理,谈到按乔姆斯基的文法理论,印象模糊了,在网上找到下面这段文字:
在计算机领域中真正被使用的只有两者:三型文法和二型文法。前者的特征是语法中不存在递归下降结构,它的代表是基本正则表达式(扩展后的正则表达式情况略有不同);而二型文法即上下文无关文法,特征是任何语言元素在任何上下文中的含义始终保持一致。事实上,多数如今的程序设计语言语法都以此为基础。以上两者构成了如今所有实用计算机程序设计语言的分析器理论基础,也有成熟的数据结构和算法支持:三型文法的 NFA/DFA,以及二型文法的递归下降/LL(x)/LR(x)/LALR(x)。
而再向上的一型文法(上下文有关文法)和零型文法(任意图灵机可识别文法),计算机工程界则通常不会涉足。虽然有些时候,我们会开玩笑地将一些语法发展得极其复杂的语言称为上下文有关语言,比如 Perl;但事实上,这类语言仍然是通过二型文法进行分析,只是通过增补一部分规则来解决;至于真正可以解析上下文有关文法的线性有界自动机,则可以肯定地说,没有程序语言开发者会试图实现。
顺便说一句,一型文法事实上可以用来表述许多自然语言,拿来表述程序设计语言,多少有点杀鸡用牛刀的意思。


作者: jinzheng02279    时间: 2016-08-05 17:52
本帖最后由 jinzheng02279 于 2016-08-05 17:53 编辑
  1. #!/usr/bin/perl
  2. use strict;
  3. while (<DATA>){
  4.     if (/(\(\d+\*(.*)\))/){
  5.         print $1 . "\n";
  6.         print $2 . "\n";
  7.     }   
  8. }

  9. __DATA__
  10. 100-(5*(4+89-(34/5)))
复制代码
仅以这道题来说,这样匹配就足够了。

结果:
(5*(4+89-(34/5)))
(4+89-(34/5))

作者: hztj2005    时间: 2016-08-28 11:50
本帖最后由 hztj2005 于 2016-08-28 12:03 编辑

转自perlmonks.org/?node_id=308039,用Regexp::Common模块实现:


use Regexp::Common;

my $balanced = qr/[^()]+|$RE{balanced}{-parens=>'()'}/;

sub extract {
    my ($want, $from) = @_;
    my $nested = qr/$balanced* \Q$want\E $balanced*/x;
    $from =~ m/( \( $nested \) | \Q$want\E )/x;
    return $1;
}

my $expr = '(((B,G),A,(A,B)),C,(D,(E))),F';

for ('A'..'H') {
    print "$_: ", extract($_,$expr),"\n";
  }

exit;

A: ((B,G),A,(A,B))
B: (B,G)
C: (((B,G),A,(A,B)),C,(D,(E)))
D: (D,(E))
E: (E)
F: F
G: (B,G)
H:
作者: hztj2005    时间: 2016-08-28 12:03
转自perlmonks.org/?node_id=308039,一般程序实现:

#Here is a simple way to do it by keeping track of how deep you are into parenthesis' and what depth your match occured at:
use strict;
my $str = "((A,B),C,(D,E))";
foreach my $m (qw(A B C D E)) {
  print $m." => ".FindMinParens($m,$str)."\n";
}
sub FindMinParens
{
  my $match = shift;
  my $string = shift;
  my @start_pos;
  my $depth = -1;
  # keep matching parens or $match
  while ($string=~/([()]|$match)/g) {
    if ($1 eq "(") {
      # record opening paren positions
      push @start_pos,pos($string);
    }
    elsif ($1 eq ")") {
      # if we reached the closing parens for the minimum pair
      # get the sub string and exit
      if ($#start_pos == $depth) {
        my $start = $start_pos[-1];
        my $len = pos($string) - $start -1;
        return substr($string,$start,$len);
      }else{
        pop @start_pos;
      }
    }
    else {
      # store depth of $match
      $depth = $#start_pos;
    }
  }
  return "";
}

exit;
作者: hztj2005    时间: 2016-08-28 12:37
本帖最后由 hztj2005 于 2016-08-28 12:39 编辑

这个方法的输出比较直观:
This is probably a klunky solution, but it reminded me of a parser I had written.. (Evaluate Expressions.)

Quickly hacking at it and only testing a few simple cases (you've been warned)

You could use the returned structure to search for whatever criteria you desire. I'm sure someone here has a much more elegant solution, but my regex abilities are rather limited...

use warnings;
#use strict;
use Data:umper;
my $input = '(a,b,(c,d,(e,f,g)))';

print Dumper parse_expression($input);

sub parse_expression {
    my $exp = shift;
    my @tokens = ();
    $exp=~s/\s*([()])\s*/ $1 /go;
    # Get tokens
    push @tokens, $1 while $exp=~/\G\s*(".*?"/gc or $exp=~/\G\s*('.*?')/gc or $exp=~/\G\s*(\S+)/gc;
   
    # Find any parens.
    my (@lp,@rp) = ();
    for (my $p =0; $p < @tokens; $p++){
        if ($tokens[$p] eq '('){
            push @lp,$p;
        }elsif($tokens[$p] eq ')'){
            push @rp,$p;
        }
    }
   
    if ( @lp != @rp){
        warn "Mismatched parens in expression.\n";
        return;

    }

    my @temp = @tokens;
    for (my $i=0; $i < @rp; $i++){
         my @wanted;
         for (my $j = $#lp; $j >= 0 ; $j--)
         {
            if ( defined $lp[$j] && $lp[$j] < $rp[$i] )
            {
                (undef,@wanted) = @tokens[ $lp[$j] ..  ($rp[$i] - 1 ) ] ;   
                @tokens[ $lp[$j] ..  ($rp[$i]) ] = [ grep {defined $_ } @wanted];
                push @temp, map {split /\s*,\s*/} @wanted;
                $lp[$j] = $rp[$i] = undef;
                last;
            }
         }
    }

  return $tokens[0];

}

# OUTPUT
$VAR1 =
          [
            'a,b,',
            [
              'c,d,',
              [
                'e,f,g'
              ]
            ]
          ];

作者: hztj2005    时间: 2016-08-28 12:55
#这个实现简捷
#!/usr/bin/perl -w
#use strict;
use Data:umper;

my $expr = '((A,B),C,(D,E))';
print extract2('C',$expr),"\n";
print extract2('B',$expr),"\n";
print extract2('D',$expr),"\n";

sub extract2 {
    my $char = shift;
    my ($str,$dup) = (shift) x 2;
    1 while $dup =~ s/\([^()$char]*\)/'.' x length $&/e;
    print $dup."\n";#转帖时添加,便于寻究其思路
    $dup =~ m/(\([^()$char]*$char[^()]*\))/;
    return substr($str,$-[1],length $1);
}


exit;




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