2015-10-30 3 views
1

Я пишу парсер на Perl, и вот проблема с split. Вот мой код:Perl split by regexp issue

my $str = 'a,b,"c,d",e'; 
my @arr = split(/,(?=([^\"]*\"[^\"]*\")*[^\"]*$)/, $str); 
# try to split the string by comma delimiter, but only if comma is followed by the even or zero number of quotes 

foreach my $val (@arr) { 
    print "$val\n" 
} 

Я ожидал следующее:

a 
b 
"c,d" 
e 

Но это то, что я действительно получил:

a 
b,"c,d" 
b 
"c,d" 
"c,d" 

e 

Я вижу, мои части строк в массиве , их индексы равны 0, 2, 4, 6. Но как избежать этих нечетных b,"c,d" и других частей строки ожидания в результирующем массиве? Есть ли какая-либо ошибка в моем разделителе регулярных выражений или есть некоторые специальные параметры split?

+0

сделать соответствующие вместо расщепления ' "[^"] *" | [ ^,] + ' –

+0

Вы используете _split_ и причудливое регулярное выражение в Perl и никогда не слышали, что сплит создает элементы из групп захвата? – sln

ответ

4

Вы должны использовать не-захвата группы:

my @arr = split(/,(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/, $str); 
         ^^ 

См IDEONE demo

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

См perldoc reference:

Если регулярное выражение имеет группировки, то список выпускаемого совпавшие подстроки из группировок, а

+1

Спасибо! Мне нравится stackoverflow :) – yakov

+1

Я только показал, откуда берутся эти« странные »элементы. проверить Собр Ответ ique. Если вы действительно работаете с CSV, лучше использовать существующие проверенные инструменты для разбора текста с разделителями. –

0

ли соответствие вместо расщепления.

use strict; use warnings; 

my $str = 'a,b,"c,d",e'; 
my @matches = $str =~ /"[^"]*"|[^,]+/g; 
foreach my $val (@matches) { 
    print "$val\n" 
} 
4

Что расцепления вас особенность в split в том, что если вы используете группу, и он установлен, чтобы захватить - он возвращает захваченный «немного» а.

Но вместо того, чтобы использовать split я хотел бы предложить модуль Text::CSV, который уже обрабатывает квотирование для вас:

#!/usr/bin/env perl 
use strict; 
use warnings; 
use Text::CSV; 

my $csv = Text::CSV->new(); 
my $fields = $csv->getline(\*DATA); 

print join "\n", @$fields; 

__DATA__ 
a,b,"c,d",e 

распечаток:

a 
b 
c,d 
e 

Мои рассуждения довольно просто - вы делаете цитаты совпадение и может иметь такие вещи, как цитаты/экранированные цитаты и т. д., означает, что вы делаете рекурсивный синтаксический анализ, что-то regex, просто не подходит для выполнения.

+0

Зачем защищать 'Text :: CSV', как это работает?Может ли простое регулярное выражение работать лучше? Я не понимаю, почему вы упоминаете рекурсивный синтаксический анализ, который Perl очень хорош. – sln

+0

Я защищаю его, потому что он работает, и делает код для синтаксического анализа вашей вещи очень простым. Регулярные выражения отлично подходят для разделения строки разделителем или поиска шаблона для поиска и замены. Но сопоставленные разделители - например, цитаты, скобки или начальные/конечные теги - это хуже, потому что это рекурсивная проблема. Вы можете это сделать, но вы найдете множество случаев, когда он не работает. Гораздо лучше использовать парсер, который может справиться с этим. – Sobrique

+0

'perl' может выполнять рекурсивный синтаксический анализ -' Text :: CSV' или 'XML :: Twig' делает это очень хорошо. 'regex' не может (очень хорошо - технически это возможно, но вы получаете очень большое и трудно понятное регулярное выражение). – Sobrique

2

Вы можете использовать parse_line() из Text::ParseWords, если вы на самом деле не ограничены для регулярных выражений:

use Text::ParseWords; 

my $str = 'a,b,"c,d",e'; 

my @arr = parse_line(',', 1, $str); 

foreach (@arr) 
{ 
    print "$_\n"; 
} 

Выход:

a 
b 
"c,d" 
e 
+0

Можете ли вы объяснить, как работает «Text :: ParseWords», а не просто использовать его? – sln

+0

@sln: Связанная документация Perl для «Text :: ParseWords» не дает хорошего объяснения? – serenesat

+0

Ну, может быть, если я захочу увидеть только ссылки, когда я ищу решения. В конце концов выяснение, что это не то, что мне нужно. – sln