Perl因regexp问题而分裂



我正在用Perl编写一些解析器,这里有一个split问题。这是我的代码:

my $str = 'a,b,"c,d",e';
my @arr = split(/,(?=([^"]*"[^"]*")*[^"]*$)/, $str);
# try to split the string by comma delimiter, but only if comma is followed by the even or zero number of quotes 
foreach my $val (@arr) {
    print "$valn"
}

我期待以下内容:

a
b
"c,d"
e

但这才是我真正收到的:

a
b,"c,d"
b
"c,d"
"c,d"
e

我看到我的字符串部分在数组中,它们的索引是0、2、4、6。但是,如何避免在生成的数组中出现这些奇怪的b,"c,d"和其他剩余字符串部分呢?我的regexp分隔符中有错误吗?或者有一些特殊的split选项吗?

您需要使用一个非捕获组:

my @arr = split(/,(?=(?:[^"]*"[^"]*")*[^"]*$)/, $str);
                      ^^

查看IDEONE演示

否则,捕获的文本将作为结果数组的一部分输出。

参见perldoc参考:

如果正则表达式具有分组,则生成的列表包含分组中匹配的子字符串以及

split中的一个功能让您感到困惑,因为如果您正在使用一个组,并且它被设置为捕获-它也会返回捕获的"位"。

但是,与其使用split,我建议使用Text::CSV模块,它已经为您处理了报价:

#!/usr/bin/env perl
use strict;
use warnings;
use Text::CSV;
my $csv    = Text::CSV->new();
my $fields = $csv->getline( *DATA );
print join "n", @$fields;
__DATA__
a,b,"c,d",e

打印:

a
b
c,d
e

我的推理相当简单——你正在进行引号匹配,可能会有引号/转义引号等。这意味着你正在尝试进行递归解析,而regex根本不适合这样做。

如果您对regex:没有真正的限制,则可以使用Text::ParseWords的parse_line()

use  Text::ParseWords;
my $str = 'a,b,"c,d",e';
my @arr = parse_line(',', 1, $str);
foreach (@arr)
{
    print "$_n";
}

输出:

a
b
"c,d"
e

进行匹配而不是拆分。

use strict; use warnings;
my $str = 'a,b,"c,d",e';
my @matches = $str =~ /"[^"]*"|[^,]+/g;
foreach my $val (@matches) {
    print "$valn"
}

最新更新