Как я могу захватить сбежавшего, но не спасшегося? - PullRequest
2 голосов
/ 01 февраля 2010

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

,"PORTION","","a",["some_string"]  

Примеры порции:

  • \ "абв123
  • abc123 \ "
  • \ "абв123 \"
  • а \ "123 \"
  • абв123

так что строки на самом деле выглядят как

  • , "\" abc123 "," "," a ", [" some_string "]
  • , "abc123 \" "," "," a ", [" some_string "]
  • "\" abc123 \ "", "", "a", ["some_string"]
  • "abc \" 123 \ "", "", "a", ["some_string"]
  • "abc123", "", "a", ["some_string"]

ЧАСТЬ окружена двойными кавычками. Двойные кавычки внутри PORTION экранируются обратной косой чертой. Моя текущая модель

my $pattern = '(.?([\\"]|[^"][^,][^"])*)';

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

  • \ "abc123", "", "а"
  • абв123
  • \ "abc12
  • а \ "123 \" "
  • абв123"

Шаблон пытается сопоставить все перед последовательностью, которая не является ","
а также разрешить захват \ "
Но это не работает, как задумано. Как я могу заставить это работать?

Ответы [ 6 ]

5 голосов
/ 01 февраля 2010

Ты делаешь это слишком сложным; нет правила, которое говорит, что вы должны выполнять все ваши разборы в одном монолитном регулярном выражении. Поскольку ваша строка выглядит как последовательность, разделенная запятыми, сначала проанализируйте ее следующим образом:

my @fields = split /(?<!\\),/, $string;   # use comma as a delimiter (except when escaped)

... А затем соответствующим образом проанализируйте ваше первое поле:

shift @fields unless $fields[0];     # pull off the potentially null first field
$fields[0] =~ s/^"//g;               # remove the leading "
$fields[0] =~ s/(?<!\\)"$//g;        # remove the trailing " that isn't preceded by a \

Вы можете проанализировать все ваши поля таким образом, обернув код выше в цикл for или map ().

Обратите внимание, что этот код не учитывает такие случаи, как \\, (запятая здесь является допустимым разделителем, даже если он будет проходить через регулярное выражение неправильно). Поэтому, было бы намного предпочтительнее использовать правильный синтаксический анализатор для вашего формата (что бы это ни было). Возможно, вы захотите взглянуть на Text :: CSV .

3 голосов
/ 01 февраля 2010

Просто используйте Текст :: CSV

1 голос
/ 01 февраля 2010

Не забудьте разрешить экранированные обратные слэши и экранированные кавычки. Использование RE для согласованного сбалансированного чего-либо становится ужасно быстрым:

/(?<=")((?:[^"\\]+|\\+[^"\\]|(?:\\\\)+|(?<!\\)\\(?:\\\\)*")*)(?=")/

Сделайте себе одолжение и используйте парсер, как подсказывает Эфир.

1 голос
/ 01 февраля 2010

Ваша проблема требует печально известного отрицательного утверждения нулевой ширины

... который позволяет вам соответствовать foo, что не следует за bar.

Документ здесь: http://perldoc.perl.org/perlre.html#Extended-Patterns

и вы хотите что-то подобное в своем регулярном выражении:

"(.+?)(?<!\\)"

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

Редактировать: Между тем проверено с использованием http://www.internetofficer.com/seo-tool/regex-tester/ и, кажется, работает нормально.

Редактировать: Как уже указывалось, это выражение не будет правильно совпадать с порцией, в которой последний символ перед закрывающей кавычкой является экранированной обратной косой чертой. Если вы не ожидаете обратной косой черты в вашем тексте, все будет в порядке.

0 голосов
/ 01 февраля 2010

Если вам нужно учитывать экранированные обратные слеши, как указано в outis, вы можете использовать это:

m/"((\\\\|\\"|[^"])+)"/

(Кажется, я не могу оставить комментарий к ответу outis, но наше решение не работает с этим:

"abc\\\"123"

будет производить

abc\\\

)

Введите:

,"\"abc123","","a",["some_string"]
,"abc123\" ","","a",["some_string"]
"\"abc123\"","","a",["some_string"]
"abc\"123\"","","a",["some_string"]
"abc123","","a",["some_string"]
"ab\\c123","","a",["some_string"]
"abc123\\","","a",["some_string"]
"abc123\\\"","","a",["some_string"]
"abc\\\"123\"","","a",["some_string"]
"abc123\\\\\"","","a",["some_string"]

Выход:

\"abc123
abc123\" 
\"abc123\"
abc\"123\"
abc123
ab\\c123
abc123\\
abc123\\\"
abc\\\"123\"
abc123\\\\\"
0 голосов
/ 01 февраля 2010

если ваши данные разделены запятыми и не имеют встроенных запятых, просто разбейте на "," и получите соответствующие поля

while(<>){
    chomp;
    @s = split /,/;
    if ($s[0] eq ""){
        print "$s[1]\n";
    }else{
        print $s[0]."\n";
    }
}

выход

$ perl perl.pl file
"\"abc123"
"abc123\" "
"\"abc123\""
"abc\"123\""
"abc123"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...