Perl разделить список на запятые, кроме случаев, когда они заключены в квадратные скобки? - PullRequest
8 голосов
/ 24 февраля 2012

У меня есть база данных с несколькими полями, содержащими значения, разделенные запятыми. Мне нужно разделить эти поля в Perl, что достаточно просто, за исключением того, что за некоторыми значениями следуют вложенные CSV, содержащиеся в скобках, которые я не хочу разделять.

Пример:

recycling, environmental science, interdisciplinary (e.g., consumerism, waste management, chemistry, toxicology, government policy, and ethics), consumer education

Разделение на "," дает мне:

recycling
environmental science
interdisciplinary (e.g.
consumerism
waste management
chemistry
toxicology
government policy
and ethics)
consumer education

То, что я хочу, это:

recycling
environmental science
interdisciplinary (e.g., consumerism, waste management, chemistry, toxicology, government policy, and ethics)
consumer education

Может ли какое-нибудь Perl-выражение (perts) протянуть руку?

Я попытался изменить строку регулярного выражения, найденную в похожем сообщении SO , которое не дает результатов:

#!/usr/bin/perl

use strict;
use warnings;

my $s = q{recycling, environmental science, interdisciplinary (e.g., consumerism, waste management, chemistry, toxicology, government policy, and ethics), consumer education};

my @parts = $s =~ m{\A(\w+) ([0-9]) (\([^\(]+\)) (\w+) ([0-9]) ([0-9]{2})};

use Data::Dumper;
print Dumper \@parts;

Ответы [ 4 ]

9 голосов
/ 24 февраля 2012

Попробуйте это:

my $s = q{recycling, environmental science, interdisciplinary (e.g., consumerism, waste management, chemistry, toxicology, government policy, and ethics), consumer education};

my @parts = split /(?![^(]+\)), /, $s;
3 голосов
/ 24 февраля 2012

Решение, которое вы выбрали, лучше, но для тех, кто сказал бы иначе, регулярные выражения имеют элемент рекурсии, который будет соответствовать вложенным скобкам.Следующее работает отлично

use strict;
use warnings;

my $s = q{recycling, environmental science, interdisciplinary (e.g., consumerism, waste management, chemistry, toxicology, government policy, and ethics), consumer education};

my @parts;

push @parts, $1 while $s =~ /
((?:
  [^(),]+ |
  ( \(
    (?: [^()]+ | (?2) )*
  \) )
)*)
(?: ,\s* | $)
/xg;


print "$_\n" for @parts;

, даже если круглые скобки вложены дальше.Нет, это не красиво, но работает!

0 голосов
/ 24 февраля 2012

Другой подход, который использует циклы и split.Я не тестировал производительность, но разве это не должно быть быстрее, чем прогнозные решения регулярных выражений (при увеличении длины $str)?

my @elems = split ",", $str;
my @answer;
my @parens;
while(scalar @elems) {
    push @answer,(shift @elems) while($elems[0] !~ /\(/);
    push @parens, (shift @elems) while($elems[0] !~ /\)/);
    push @answer, join ",", (@parens, shift @elems);
    @parens = ();
}
0 голосов
/ 24 февраля 2012

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

use strict;
use warnings;
use 5.010;

my $s = q{recycling, environmental science, interdisciplinary (e.g., consumerism, waste management, chemistry, toxicology, government policy, and ethics), consumer education};

my @parts;
while(1){

        my ($elem, $rest) = $s =~ m/^((?:\w|\s)+)(?:,\s*([^\(]*.*))?$/;
        if (not $elem) {
                say "second approach";
                ($elem, $rest) = $s =~ m/^(?:((?:\w|\s)+\s*\([^\)]+\)),\s*(.*))$/;
        }
        $s = $rest;
        push @parts, $elem;
        last if not $s;

}

use Data::Dumper;
print Dumper \@parts;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...