Perl - 按逗号拆分字符串。忽略空格。

3

我有这个字符串:

$str="     a, b,    c>d:e,  f,    g ";

在这个字符串中可能会有空格和/或制表符。
我用Perl分割了这个字符串:
my (@COLUMNS) = split(/[\s\t,]+/, $str));

但是这会在位置[0]创建一个前导空格。
@COLUMNS=[

    a
    b
    c>d:e
    f
    g
]

我想要这个:

@COLUMNS=[
    a
    b
    c>d:e
    f
    g
]

3
仅作为旁注:你不必明确说明\t,因为\s已经包括了空格和制表符(以及其他任何_空白字符_,尽管我不知道其他的是什么)。 - PerlDuck
2
@PerlDog:\s匹配的ASCII字符的Unicode名称包括CHARACTER TABULATIONLINE FEEDLINE TABULATIONFORM FEEDCARRIAGE RETURNSPACE,即[\t\n\x0B\f\r ]。还有十几个ASCII之外的字符也符合匹配条件。 - Borodin
2个回答

7
我建议您使用全局正则表达式匹配,查找所有既不是逗号也不是空格的字符子序列。这将产生与您的split(/[\s\t,]+/相同的输出。(请注意,\t是多余的,因为\s也可以匹配制表符)。但是,将创建一个没有任何空元素的列表。
use strict;
use warnings 'all';

my $str = "     a, b,    c>d:e,  f,    g ";

my @columns = $str =~ /[^\s,]+/g;

use Data::Dump;
dd \@columns;

输出

["a", "b", "c>d:e", "f", "g"]

请注意,与您的分割方法一样,此方法将忽略任何空字段:例如a,,,b将返回[ 'a', 'b' ]而不是[ 'a', '', '', 'b' ]。同时,包含空格的列也会被拆分,因此a,two words,b将生成[ 'a', 'two', 'words', 'b' ]而不是[ 'a', 'two words', 'b' ]。只有您能告诉这些情况是否可能出现。
如果有任何可能导致此方法产生错误结果的情况,则最好只在逗号上进行拆分,并编写一个子例程来修整生成的字段。
use strict; 
use warnings 'all';

sub trim(;$);

my $str="     a  ,, ,two words ,,, b";
my @columns = map trim, split /,/, $str;

use Data::Dump;
dd \@columns;


sub trim(;$) {
    (my $trimmed = $_[0] // $_) =~ s/\A\s+|\s+\z//g;
    $trimmed;
}

输出

["a", "", "", "two words", "", "", "b"]

这些对我都不起作用,第一个返回[" ", ", ", ", ", ", "]等等。 - A.D
你是直接从我的答案中复制的代码吗?这是相当基本的 Perl 代码,自 v5.8 以来没有改变过。 - Borodin
啊!我忘记在“\s”前面加上“^”了。这段代码比我先前接受的答案要干净得多。我会使用这个,非常感谢! - A.D

7

解决这个问题的一个常见方法是转换从split返回的值。在这种情况下,您希望删除任何前导或尾随空格,通常称为修剪操作。使用这种方法,您不必担心在拆分操作中的空格:

use strict; 
use warnings; 

my $str="     a, b,    c>d:e,  f,    g ";
my @columns = map { s/^\s*|\s*$//gr } split(/,/, $str);
print join(',', @columns), "\n";

另一个解决方案,如上面@toolic所提到的,是预先删除所有空格:
use strict; 
use warnings; 

my $str="     a, b,    c>d:e,  f,    g ";
$str =~ s/\s+//g; # remove all occurrences of 1 or more spaces
my @columns = split(/,/, $str);
print join(',', @columns), "\n";

以上两种解决方案都会返回以下输出结果:

a,b,c>d:e,f,g

关于 /r 修饰符的更多信息:

/r 是一种可以应用于替换操作的非破坏性修饰符。这意味着原始字符串不会被修改,而是创建一个副本,进行修改,并返回该副本。这样做有优势,因为通常在标量上下文中,s/// 运算符将返回发生替换的次数,而不是修改后的字符串。这仅适用于 Perl 版本 >= 5.14。对于低于此版本的 Perl,等效的语句为:

my $original = "some_string";
(my $copy = $original) =~ s/$search_pattern/$replace_pattern/;

并用于地图:

map { 
   (my $temp = $_) =~ s/$search_pattern/$replace_pattern/; $temp 
} split /$delimiter/, $original;

示例:

my $string = 'abc'; 
my $num_substitutions = $string =~ s/a/d/; # 1 

my $string = 'abc';
my $new_string = $string =~ s/a/d/r; # dbc

1
"map { s/^\s*|\s*$//gr }" 的意思是您能详细解释一下吗? - ssr1012
我的 @columns 数组 = map { s/^\s*|\s*$//gr } split(/,/, $str); 工作得非常完美。谢谢! - A.D
2
注意:r正则表达式修饰符仅适用于Perl版本> 5.14。 - Sobrique

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接