【问题标题】:Splitting a string around outer delimiters, respecting character groups在外部分隔符周围拆分字符串,尊重字符组
【发布时间】:2013-07-30 00:29:08
【问题描述】:

假设我有一个字符串:

my $string = "foo{a},bar{b}, baz{c,d,e}";

使用一组分组字符来区分两个级别:

$grouping_characters = "{}"

我想将此字符串拆分为“外部”逗号 (,),以尊重 $grouping_characters 内部的所有内容。

对于上面的例子,输出应该是:

my @result = ("foo{a}", "bar{b}", "baz{c,d,e}")

如何在 Perl 中做到这一点?

【问题讨论】:

  • [] 引入了一个匿名数组(参考)。你可能遇到my @result = ("foo{a}, "bar{b}", "baz{c,d,e}");
  • 谢谢@choroba!好点。刚刚修好了。

标签: regex string perl


【解决方案1】:

首先:如果您想正确解析某些编程语言或配置格式,您可能需要使用actual parser

但是,您可以使用正则表达式来完成您的任务。但是我们不会编写正则表达式来匹配我们想要拆分的逗号。相反,我们编写了一个匹配列表部分的正则表达式:

my $regex = qr/
  \w+           # item can begin with some identifier
  \{ [^\}]* \}  # followed by some stuff in braces
  [,;]          # must end with comma or semicolon
/x;

然后我们可以像这样提取匹配项

my $string = "foo{a},bar{b}, baz{c,d,e};";
my @result = $string =~ /$regex/g;
dd @result; # using dd from Data::Dump

输出:

("foo{a},", "bar{b},", "baz{c,d,e};")

相当不错。现在,我们通过两种方式改进我们的正则表达式:

  1. 逗号不是匹配字符串的一部分
  2. 我们确保匹配是相邻的,并且中间没有垃圾
  3. 我们以最简单的方式使分隔符可插入:我们将一些值插入到 charclass 中。

一起:

my $delims = quotemeta "{}";
my $regex = qr/
    \w+
    [$delims] [^$delims]* [$delims]
/x;

my $string = "foo{a},bar{b}, baz{c,d,e};";
my @result = $string =~ /\G ($regex) [,;] \s*/xg;
dd @result;

\G 断言锚定上一个匹配停止的位置。

输出:

("foo{a}", "bar{b}", "baz{c,d,e}")

太棒了。这可以通过两种方式进一步细化:

  1. 大括号里的东西是允许递归的
  2. 我们区分打开和关闭分隔符,并且只允许正确的对。事实上,foo}a{ 将是一个有效的项目......。

如果不需要所有这些,当前的正则表达式应该就可以了。

【讨论】:

    【解决方案2】:

    尝试使用这个正则表达式:

    (.*[}]),\s*(.*[}]),\s*(.*[{].*[}])
    

    像这样:

    my $string = "foo{a},bar{b}, baz{c,d,e}";
    
    print grep(/(.*[}]),\s*(.*[}]),\s*(.*[{].*[}])/, $string);
    

    【讨论】:

      【解决方案3】:

      你可以试试:

      my $string = "foo{a},bar{b}, baz{c,d,e}";
      
      print join(",",split(/,\s*(?=\w+{[a-z,]+})/g,$string));
      

      【讨论】:

        【解决方案4】:

        简单解析器:

        #!/usr/bin/perl
        use warnings;
        use strict;
        
        my $string = 'foo{a},bar{b}, baz{c,d,e}';
        my @parts;
        
        my $inside;
        my $from = 0;
        for my $i (0 .. length $string) {
        
            my $char = substr $string, $i, 1;
        
            if ('{' eq $char) {
                $inside++;
        
            } elsif ('}' eq $char) {
                $inside--
        
            } elsif (',' eq $char and not $inside) {
                push @parts, substr $string, $from, $i - $from;
                $from = $i + 1;
            }
        }
        
        push @parts, substr $string, $from;
        print "$_\n" for @parts;
        

        删除空格留给读者作为练习。

        【讨论】:

          【解决方案5】:
          > echo "foo{a},bar{b}, baz{c,d,e}" | perl -lne 'push @a,/.*?{.*?},?/g;for(@a){print}'
          foo{a},
          bar{b},
           baz{c,d,e}
          >
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-10-03
            • 2014-06-29
            • 2016-08-07
            • 1970-01-01
            • 1970-01-01
            • 2020-01-07
            相关资源
            最近更新 更多