Разделить строку по 2 правилам, используя один и тот же синтаксис - PullRequest
3 голосов
/ 11 сентября 2011

У меня есть строка:

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)

Ответы [ 5 ]

6 голосов
/ 11 сентября 2011

При использовании split вы решаете, следует ли отбрасывать разделители или сохранять их.В вашем случае вы хотите сохранить один разделитель (закрывающая скобка) и выбросить другой (запятую).Кроме того, вы, вероятно, захотите выбросить любые пробелы после этих разделителей.

Разделители могут быть сохранены с помощью:

  1. Заключение шаблона split в круглые скобки.В этом случае сами разделители будут заканчиваться как отдельные строки, перемежающиеся с вашим результатом, что не совсем то, что вам нужно.

  2. Указание разделителя в утверждении нулевой ширины (оглядываться назад, заглядывать вперед и т. д.).Это исключает разделитель из совпадающей строки, что предотвращает его отбрасывание.

Второй подход будет вам полезен.

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

Для обработки болееСложный сценарий в вашем отредактированном вопросе, такой как «Роберт Дауни (младший) (Тони Старк)», вы можете добавить еще одно утверждение нулевой ширины:

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;
3 голосов
/ 11 сентября 2011

Сначала добавьте запятую после каждого ), затем разделите (и отбросьте) запятые:

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 голосов
/ 11 сентября 2011

Полезное эмпирическое правило, приписываемое Рэндалу Шварцу, заключается в использовании split, когда вы знаете, что вы хотите отбросить, или m// и захвате скобок, когда вы знаете, что ты хочешь сохранить. Однако применить его к вашему вопросу немного сложно, потому что вы хотите сделать и то, и другое. То есть либо

  • выбросить завершающую запятую или
  • сохранить правую скобку

Программа ниже использует m// и захват, поэтому она определяет проблему с точки зрения того, что она хочет сохранить . Конечно, ) на конце очень просто. Чтобы исключить запятую из буфера захвата, код использует положительное упреждающее утверждение : захват должен останавливаться на символе всего за до запятой.

Возможность, которую легко упустить, это имя, которое также должно заканчиваться в конце строки. Скажем, Стэн Ли был фамилией, а не второй Без альтернативы $ Стэн был бы опущен.

Код использует 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]
1 голос
/ 11 сентября 2011

Мат уже попал в точку, я только добавил немного очистки в свою версию:

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/^ //;
}
1 голос
/ 11 сентября 2011

Один из способов сделать это может быть:

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

Если вы распечатаете этот список, вы получите что-то вроде:

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

Все, что вам нужно, это повторно-собрать отдельные элементы, которые находятся на всех четных позициях в этом списке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...