使用相同的语法按2条规则拆分

时间:2011-09-11 11:10:14

标签: perl

我有一句话:

Jon Favreau, Stan Lee, Justin Theroux, Robert Downey Jr. (Tony Stark) Gwyneth Paltrow (Pepper Potts) Don Cheadle (James Rhodes)

我想用逗号拆分行,并用该结果括起来:

Jon Favreau
Stan Lee
Justin Theroux
Robert Downey Jr. (Tony Stark)
Gwyneth Paltrow (Pepper Potts)
Don Cheadle (James Rhodes)

编辑:特殊情况

专栏:Jon Favreau,Stan Lee,Justin Theroux,Robert Downey(Jr。)(Tony Stark)Gwyneth Paltrow(Pepper Potts)Don Cheadle(James Rhodes)

世界(Jr.)在brakets。输出:

Jon Favreau
Stan Lee
Justin Theroux
Robert Downey (Jr.) (Tony Stark)
Gwyneth Paltrow (Pepper Potts)
Don Cheadle (James Rhodes)

5 个答案:

答案 0 :(得分:6)

使用split时,您决定是否丢弃分隔符或保留分隔符。在您的情况下,您希望保留一个分隔符(右括号)并丢弃另一个分隔符(逗号)。此外,您可能希望丢弃这些分隔符后的任何空格。

分隔符可以通过以下方式保存:

  1. split模式包含在捕获括号中。在这种情况下,分隔符本身最终将作为单独的字符串,散布在您的结果中,这不是您想要的。

  2. 在零宽度断言中指定分隔符(后视,前瞻等)。这会从匹配的字符串中排除分隔符,从而防止它被丢弃。

  3. 第二种方法适用于你。

    my @actors = split /(?<=\)) *|, */, $line;
    

    要处理编辑过的问题中更复杂的场景,例如“Robert Downey(Jr。)(Tony Stark)”,您可以添加另一个零宽度断言:

    my $actor_regex = qr'
        (?<=     \) )  # Look-behind: close paren.
        \s*
        (?!  \s* \( )  # Negative look-ahead: opening paren.
        |
        , \s*          # Or the other delimiter.
    'x;
    
    my @items = split $actor_regex, $line;
    

答案 1 :(得分:3)

首先在每个)之后添加一个逗号,然后拆分(并丢弃)逗号:

perl -e '$_="Jon Favreau, ...";s/\)/\),/g;split ",";foreach (@_) {s/^\ //;print "$_\n"}'

收率:

Jon Favreau
Stan Lee
Justin Theroux
Robert Downey Jr. (Tony Stark)
Gwyneth Paltrow (Pepper Potts)
Don Cheadle (James Rhodes)

答案 2 :(得分:2)

归功于Randal Schwartz的一个有用的经验法则是当你知道要扔掉什么时使用splitm//当你知道要保留什么时使用括号。然而,将它应用于您的问题有点棘手,因为您想要同时执行这两项操作。那就是

  • 丢弃终止逗号或
  • 保留右括号

下面的程序使用m//并捕获,因此它根据保持的内容来定义问题。当然,最后)很容易。为了使逗号保持在捕获缓冲区之外,代码使用正look-ahead assertion:捕获应该停留在之前之前的字符上。

容易错过的可能性也应该允许名称在字符串结尾处终止。说Stan Lee是姓,而不是第二个。如果没有$替代方案,斯坦就会被排除在外。

代码使用DEFINE并命名子模式来帮助读者理解正则表达式。这种方法的缺点是它会生成额外的捕获缓冲区,因此您必须使用循环而不是@names = /$name_pattern/g

如上所述,它接受的语言略大于您在问题中指定的语言,它允许并丢弃两个同时具有字符名称的演员之间的逗号。

#! /usr/bin/env perl

use warnings;
use strict;

*ARGV = *DATA; # for demo only

my $name_pattern = qr/
  ( # capture into $1
    (?&name) (?: (?&comma_terminated) | \) | $ )
  )

  # discard trailing whitespace and optional comma
  (?: \s* (?: , \s*)? )

  (?(DEFINE)
    (?<name>             .+?    )
    (?<comma_terminated> (?= ,) )
  )
/x;

while (<>) {
  my @names;
  push @names, $1 while /$name_pattern/gx;

  print "[$_]\n" for @names;
}

__DATA__
Jon Favreau, Stan Lee, Justin Theroux, Robert Downey Jr. (Tony Stark) Gwyneth Paltrow (Pepper Potts) Don Cheadle (James Rhodes) foo

输出:

[Jon Favreau]
[Stan Lee]
[Justin Theroux]
[Robert Downey Jr. (Tony Stark)]
[Gwyneth Paltrow (Pepper Potts)]
[Don Cheadle (James Rhodes)]
[foo]

答案 3 :(得分:1)

这样做的一种方法可能是:

my @items = split(/(\)|,)/, $line);

如果你打印出那个列表,你会得到类似的东西:

Jon Favreau
,
 Stan Lee
,
 Justin Theroux
,
 Robert Downey Jr. (Tony Stark
)
 Gwyneth Paltrow (Pepper Potts
)
 Don Cheadle (James Rhodes
)

然后您需要的是重新组合单个项目,这些项目位于该列表中所有偶数位置。

答案 4 :(得分:1)

Mat已经到了现场,我刚刚在我的版本中添加了一些清洁工具:

my $names =
"Jon Favreau, Stan Lee, Justin Theroux, Robert Downey Jr. (Tony Stark) Gwyneth Paltrow (Pepper Potts) Don Cheadle (James Rhodes)";

my @names = split( /[,|\)]/, $names );
foreach my $name (@names) {
    $name = $name . ")" if $name =~ /.*\(.*/;
    $name =~ s/^ //;
}