Perl - регулярное выражение для соответствия двойным кавычкам - PullRequest
2 голосов
/ 07 марта 2012

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

"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

Ответы [ 3 ]

4 голосов
/ 07 марта 2012

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

".*?"

на это:

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

Кроме того, использование сопоставления без жадности не очень безопасно.Примерно так:

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

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

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

2 голосов
/ 07 марта 2012

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

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

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

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

0 голосов
/ 07 марта 2012

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

#!/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

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

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