2

假设我有一个字符串:

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 中做到这一点?

4

5 回答 5

3

第一:如果您想正确解析某些编程语言或配置格式,您可能需要使用实际的解析器

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

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{将是一个有效的项目......。

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

于 2013-07-30T00:49:23.273 回答
1

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

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

像这样:

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

print grep(/(.*[}]),\s*(.*[}]),\s*(.*[{].*[}])/, $string);
于 2013-07-30T00:43:26.067 回答
1

你可以试试:

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

print join(",",split(/,\s*(?=\w+{[a-z,]+})/g,$string));
于 2013-07-30T00:52:38.183 回答
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;

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

于 2013-07-30T00:56:39.843 回答
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}
>
于 2013-08-01T14:06:46.543 回答