2010-06-17 3 views
0

У меня есть некоторые данные, которые я обрабатываю в Perl, и в ближайшее время будут добавлять все более и более отформатированные данные. То, что я хотел бы сделать, это написать простую в использовании функцию, чтобы я мог передать строку и регулярное выражение, и оно вернет что-нибудь в круглых скобках. Это было что-то вроде этого (псевдокод) работы:Соответствующие n круглых скобок в perl regex

sub parse { 
    $data = shift; 
    $regex = shift; 

    $data =~ eval ("m/$regex/") 
    foreach $x ($1...$n) 
    { 
    push (@ra, $x); 
    } 
    return \@ra; 
} 

Тогда я мог бы назвать это так:

@subs = parse ($data, '^"([0-9]+)",([^:]*):(\W+):([A-Z]{3}[0-9]{5}),ID=([0-9]+)'); 

Как вы можете видеть, есть несколько проблем с этим кодом. Я не знаю, будет ли eval работать, «foreach» определенно не сработает, и, не зная, сколько скобок есть, я не знаю, сколько раз цикл.

Это слишком сложно для разделения, поэтому, если есть еще одна функция или возможность, которую я пропускаю, дайте мне знать.

Благодарим за помощь!

ответ

6

В контексте списка регулярное выражение вернет список всех совпадений в скобках.

Так все, что вам нужно сделать, это:

my @matches = $string =~ /regex (with) (parens)/; 

И если предположить, что оно соответствует, @matches будет массив из двух групп захвата.

Таким образом, используя ваши регулярные выражения:

my @subs = $data =~ /^"([0-9]+)",([^:]*):(\W+):([A-Z]{3}[0-9]{5}),ID=([0-9]+)/; 

Кроме того, если у вас есть длинные регулярные выражения, Perl имеет x модификатор, который идет после закрытия регулярных выражений разделителя. Модификатор x позволяет помещать пробелы и символы новой строки внутри регулярного выражения для повышения удобочитаемости.

Если вы беспокоитесь о группах захвата, которые могут иметь нулевую длину, вы можете передать спички через @subs = grep {length} @subs, чтобы отфильтровать их.

+0

Если вы не знаете, имеет ли регулярное выражение парс или нет, и не хотите возвращать ничего, если это не так (вместо стандартной строки по умолчанию), добавьте дополнительный набор: '$ string = ~/(regex) /' и отбросить его от результатов. – ysth

+0

Этот grep будет фильтровать парны, которые фактически не используются в матче, но не нулевые (которые будут определены и «») – ysth

+0

@ysth => вы правы, исправлены. –

0

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

+0

Perl являются нерегулярными. вы можете использовать '(?? {blah})', хотя это не совсем рекомендуемая практика. – muhmuhten

+0

regex engine perl также поддерживает рекурсию, что позволяет легко совместить вложенные конструкции –

+0

True - многие реализации регулярных выражений могут фактически анализировать больше, чем набор обычных языков, но это несовместимо. Если вам нужно проанализировать грамматику - используйте правильный грамматический синтаксический анализатор. –

0

Если вы хотите найти текст внутри пар круглых скобок, вы хотите использовать Text::Balanced.

Но это не то, что вы хотите сделать, так что это вам не поможет.

+1

, несмотря на название вопроса, похоже, что OP фактически не ищет совпадающих вложенных парсеров, просто для использования регулярного выражения, которое может иметь любое количество последовательных групп захвата. –

+0

Извините, я должен был сказать «скобки» вместо «круглых скобок». –

1

Тогда я мог бы назвать это так:

@subs = parse($data, 
      '^"([0-9]+)",([^:]*):(\W+):([A-Z]{3}[0-9]{5}),ID=([0-9]+)'); 

Вместо этого, называют это нравится:

parse($data, 
    qr/^"([0-9]+)",([^:]*):(\W+):([A-Z]{3}[0-9]{5}),ID=([0-9]+)/); 

Кроме того, ваша задача будет сделать проще, если вы можете использовать named captures (т.е. Perl 5.10 и более поздние).Вот пример:

#!/usr/bin/perl 

use strict; use warnings; 

my %re = (
    id => '(?<id> [0-9]+)', 
    name => '(?<name> \w+)', 
    value => '(?<value> [0-9]+)', 
); 

my @this = (
    '123,one:12', 
    '456,two:21', 
); 

my @that = (
    'one:[12],123', 
    'two:[21],456', 
); 

my $this_re = qr/$re{id} , $re{name} : $re{value}/x; 
my $that_re = qr/$re{name} : \[$re{value}\] , $re{id} /x; 

use YAML; 

for my $d (@this) { 
    print Dump [ parse($d, $this_re) ]; 
} 

for my $d (@that) { 
    print Dump [ parse($d, $that_re) ]; 
} 

sub parse { 
    my ($d, $re) = @_; 
    return unless $d =~ $re; 
    return my @result = @+{qw(id name value)}; 
} 

Выход: regexen

--- 
- 123 
- one 
- 12 
--- 
- 456 
- two 
- 21 
--- 
- 123 
- one 
- 12 
--- 
- 456 
- two 
- 21
+0

Благодарим вас за это, это хорошо знать! –