2012-03-06 2 views
2

Нужна помощь с регулярным выражением. Я пытаюсь сопоставить строку с двойными кавычками текста в большой строке, которая сама может содержать пары двойных кавычек! Вот пример:Perl - регулярное выражение для совпадения с двойным кавычками

"Please can ""you"" match this" 

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

Любая помощь очень ценится.

use strict; 
use warnings; 
use Data::Dumper; 

my %hash; 

my $delimiter = '/begin CITY'; 
local $/ = $delimiter; 

my $top_of_file = <DATA>; 
my $records=0; 

while(<DATA>) { 

    my ($section_body) = m{^(.+)/end CITY}ms; 

    $section_body =~ s{/\*.*?\*/}{}gs;  # Remove any comments in string 

    $section_body =~ m{ ^\s+(.+?) ## Variable name is never whitespace seperated 
            ## Always underscored. Akin to C variable names 

         \s+(".*?") ## The long description can itself contain 
            ## pairs of double quotes ""like this"" 

         \s+(.+)  ## Everything from here can be split on 
            ## whitespace 

         \s+$ 
        }msx; 

    $hash{$records}{name} = $1; 
    $hash{$records}{description} = $2; 

    my (@data) = split ' ', $3; 

    @{ $hash{$records} }{qw/ size currency /} = @data; 

    ++$records; 
} 

print Dumper(\%hash); 


__DATA__ 
Some header information 

/begin CITY 

    london /* city name */ 
    "This is a ""difficult"" string to regex" 
    big 
    Sterling 

/end CITY 

/begin CITY paris 
     "This is a simple comment to grab." 
     big 
     euro /* the address */ 
/end CITY 


/begin CITY 

    Melbourne 
    "Another ""hard"" long description to 'match'." 
    big 
    Dollar 

/end CITY 
+0

Непонятно, на каком ожидаемом выходе или даже на вход. – yzxben

+0

@peapls и пример ввода находится в разделе '__DATA__'. – dlamblin

+0

Я думаю, вы должны тестировать это с помощью средства регулярного выражения; НАПРИМЕР. http://www.regexbuddy.com/ или что-то в этом роде. – dlamblin

ответ

4

Изменить это:

".*?" 

к этому:

"(?>(?:[^"]+|"")*)" 

Кроме того, ваше использование не жадный соответствия не очень безопасно. Что-то вроде этого:

\s+(.+?) ## Variable name is never whitespace seperated 
      ## Always underscored. Akin to C variable names 

вполне может оказаться, включая пробелы внутри имя-переменной, если Perl считает, что это единственный способ, чтобы соответствовать. (Это предпочитают остановиться, прежде чем включать пробелы, но это не дает никаких гарантий.)

И вы всегда должны проверить, чтобы убедиться, что m{} нашел что-то. Если вы уверены, что будет всегда, то вы можете просто наклеить на or die, чтобы проверить это.

+0

Отличный ответ, и спасибо, что нашли время ответить, а также ваши дополнительные комментарии. Постарайтесь немного объяснить, как работает это регулярное выражение? – Chris

+0

@Chris: '[^"] + 'означает" один или несколько символов, которые не являются двойными кавычками ".' "" "Означает две последовательные двойные кавычки". Так '(?: [^"] + | "") * 'означает" последовательность символов без двойных кавычек, необязательно смешанных с парами последовательных двойных кавычек ". '(?> ...)' - специальная нотация Perl, означающая «после того, как вы сопоставили» ...', не возвращайтесь обратно в нее", здесь здесь не 100%, но это немного проверка безопасности, это гарантирует, что, учитывая что-то вроде "x" "y" "z' (с отсутствующим двойным -quote в конце), регулярное выражение не будет отступать и соответствовать «x» «y». (Т. Е. Он гарантирует, что * [продолжение] * – ruakh

+0

* [продолжение] * '' "' внутри строки с кавычками будет * никогда * считаться «разделителем конца строки», за которой следует дополнительная '' '', даже если чтение его таким образом может позволить регулярному выражению соответствовать.) В этом случае, подумайте об этом, он фактически не имеет никакого эффекта, кроме потенциально небольшого повышения производительности, потому что вы уже указываете, что конец -строчный разделитель сопровождается '\ s +', но я всегда привык использовать '(?> ...)' в этих выражениях, на всякий случай. – ruakh

2

Я не знаю, сколько удачи вам придется иметь с синтаксическим оформлением цитированного текста с вашими регулярными выражениями, это может быть довольно рискованный бизнес. Я бы посмотрел на модуль вроде Text :: Balanced.

https://metacpan.org/pod/Text::Balanced

Это должно делать то, что вам это нужно тоже, и хороший немного менее болезненно.

Я знаю, что я должен отвечать на вопрос, как было задано, но регулярные выражения действительно не так, как вы хотите это сделать.

+0

Не знаю, согласен ли я. 'Text :: Balanced :: gen_delimited_pat ('' ',' '')' можно использовать для создания регулярного выражения для этого, но в этой ситуации кажется, что это ничего не помогает. – ruakh

+0

Я думал по строки использования extract_delimited() или extract_multiple() и не беспокоиться о регулярном выражении (или другом методе синтаксического анализа), фактически используемом. –

+0

Ah. I di Не думаю, что 'extract_delimited' поддерживал случай, когда литерал' '' написан как '' '', но теперь я вижу, что это так. Тогда это имеет смысл. :-) – ruakh

0

Я неуверен если это всего лишь пример, чтобы продемонстрировать свою проблему, но это может быть решена путем чтения построчно:

#!/usr/bin/perl 
use strict; 
use warnings; 
use Data::Dumper; 
my %hash; 
my $delimiter = '/begin CITY'; 
local $/ = $delimiter; 
my $top_of_file = <DATA>; 
my $records=0; 
my @lines; 
sub trim 
{ 
     my $string = shift; 
     $string =~ s/^\s+//; 
     $string =~ s/\s+$//; 
     return $string; 
} 
while(<DATA>) { 
    my ($section_body) = m{^(.+)/end CITY}ms; 
    $section_body =~ s{/\*.*?\*/}{}gs; # Remove any comments in string 
    $section_body =~ s{^\s*\n}{}gs; # Remove empty lines 
################# 
    if ($section_body =~ m{".*"}) { # Or a normal greedy match 
    $hash{$records}{quoted} = $&; 
    } 
################# 
    @lines = split "\n", $section_body, 5; 
    $hash{$records}{name} = trim($lines[0]); 
    $hash{$records}{description} = trim($lines[1]); 
    $hash{$records}{size} = trim($lines[2]); 
    $hash{$records}{currency} = trim($lines[3]); 
    ++$records; 
} 
print Dumper(\%hash); 

__DATA__ 
Some header information 

/begin CITY 

    london /* city name */ 
    "This is a ""difficult"" string to regex" 
    big 
    Sterling 

/end CITY 

/begin CITY paris 
     "This is a simple comment to grab." 
     big 
     euro /* the address */ 
/end CITY 


/begin CITY 

    Melbourne 
    "Another ""hard"" long description to 'match'." 
    big 
    Dollar 


/end CITY 

Также обратите внимание, что я указал, что единственная проблема, которую вы что ".*?", вероятно, должен быть ".*".

+0

Спасибо, что нашли время ответить, но пока мои примеры имели разделители строк, я не хочу полагаться на это. Например, '/ begin CITY Melbourne « Еще одно «тяжелое» «длинное описание для« матча ». большой доллар /конец CITY' ... не работает. – Chris

+0

@ Крис Я вижу; ну тогда проблем нет. Предполагая, что есть только одно поле, которое цитируется, вам нужно только изменить неалвидную цитату в жадном матче. – dlamblin