在外部分隔符周围拆分字符串,尊重字符组

时间:2013-07-30 00:29:09

标签: regex string perl

说我有一个字符串:

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中执行此操作?

5 个答案:

答案 0 :(得分:3)

首先:如果要正确解析某些编程语言或配置格式,可能需要使用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. 我们确保匹配是相邻的,并且
  4. 之间没有垃圾
  5. 我们以最微不足道的方式使分隔符可插入:我们将一些值插入到一个charclass中。
  6. 合:

    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. 我们区分开始和结束的delims,只允许正确的对。事实上,foo}a{将是一个有效的项目......
    3. 如果不需要所有这些,那么当前的正则表达式应该可以。

答案 1 :(得分:1)

尝试使用此正则表达式:

(.*[}]),\s*(.*[}]),\s*(.*[{].*[}])
像这样:

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

print grep(/(.*[}]),\s*(.*[}]),\s*(.*[{].*[}])/, $string);

答案 2 :(得分:1)

你可以尝试:

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

print join(",",split(/,\s*(?=\w+{[a-z,]+})/g,$string));

答案 3 :(得分:1)

简单解析器:

#!/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;

删除空格留给读者练习。

答案 4 :(得分:0)

> 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}
>