Как я могу проанализировать процитированный CSV в Perl с регулярным выражением? - PullRequest
12 голосов
/ 11 марта 2009

У меня проблемы с анализом данных CSV с кавычками. Моя главная проблема с кавычками в поле. В следующем примере строки 1 - 4 работают правильно, а 5,6 и 7 - нет.

COLLOQ_TYPE,COLLOQ_NAME,COLLOQ_CODE,XDATA
S,"BELT,FAN",003541547,
S,"BELT V,FAN",000324244,
S,SHROUD SPRING SCREW,000868265,
S,"D" REL VALVE ASSY,000771881,
S,"YBELT,"V"",000323030,
S,"YBELT,'V'",000322933,

Я бы хотел избежать Text :: CSV, поскольку он не установлен на целевом сервере . Понимая, что CSV сложнее, чем кажется, я использую рецепт из Perl Cookbook.

sub parse_csv {
  my $text = shift; #record containg CSVs
  my @columns = ();
  push(@columns ,$+) while $text =~ m{
    # The first part groups the phrase inside quotes
    "([^\"\\]*(?:\\.[^\"\\]*)*)",?
      | ([^,]+),?
      | ,
    }gx;
  push(@columns ,undef) if substr($text, -1,1) eq ',';
  return @columns ; # list of vars that was comma separated.
}

У кого-нибудь есть предложения по улучшению регулярного выражения для обработки вышеуказанных случаев?

Ответы [ 7 ]

34 голосов
/ 11 марта 2009

Пожалуйста, попробуйте использовать CPAN

Нет причин, по которым вы не могли загрузить копию Text :: CSV или любую другую реализацию синтаксического анализатора CSV, не основанную на XS, и установить ее в локальный каталог или в lib / подкаталог вашего проекта, поэтому он устанавливается вместе с развертыванием ваших проектов.

Если вы не можете хранить текстовые файлы в своем проекте, то мне интересно, как это вы кодируете свой проект.

http://novosial.org/perl/life-with-cpan/non-root/

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

Неиспользование CPAN на самом деле является рецептом катастрофы.

Пожалуйста, подумайте об этом, прежде чем пытаться написать собственную реализацию CSV.

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

примечание: я научился этому нелегко. Мне потребовался целый день, чтобы получить работающий CSV-парсер в PHP, прежде чем я обнаружил, что встроенный парсер был добавлен в более позднюю версию. Это действительно что-то ужасное.

20 голосов
/ 11 марта 2009

Вы можете проанализировать CSV, используя Text :: ParseWords , который поставляется с Perl.

use Text::ParseWords;

while (<DATA>) {
    chomp;
    my @f = quotewords ',', 0, $_;
    say join ":" => @f;
}

__DATA__
COLLOQ_TYPE,COLLOQ_NAME,COLLOQ_CODE,XDATA
S,"BELT,FAN",003541547,
S,"BELT V,FAN",000324244,
S,SHROUD SPRING SCREW,000868265,
S,"D" REL VALVE ASSY,000771881,
S,"YBELT,"V"",000323030,
S,"YBELT,'V'",000322933,

, который правильно анализирует ваш CSV ....

# => COLLOQ_TYPE:COLLOQ_NAME:COLLOQ_CODE:XDATA
# => S:BELT,FAN:003541547:
# => S:BELT V,FAN:000324244:
# => S:SHROUD SPRING SCREW:000868265:
# => S:D REL VALVE ASSY:000771881:
# => S:YBELT,V:000323030:
# => S:YBELT,'V':000322933:

Единственная проблема, с которой я столкнулся при работе с Text :: ParseWords, заключается в том, что вложенные кавычки в данных не экранируются правильно. Однако это плохо скомпонованные данные CSV и могут вызвать проблемы с большинством анализаторов CSV; -)

Итак, вы можете заметить, что

# S,"YBELT,"V"",000323030,

вышло как (т. Е. Кавычки опущены вокруг "V")

# S:YBELT,V:000323030:

Однако, если он сбежал, как это

# S,"YBELT,\"V\"",000323030,

тогда цитаты будут сохранены

# S:YBELT,"V":000323030:
2 голосов
/ 12 марта 2012

Это работает как шарм

строка считается разделенной запятой и вставлена, </p> <p>my @columns = Text::ParseWords::parse_line(',', 0, $line);

1 голос
/ 28 апреля 2011

испытание; работает: -

$_.=','; # fake an ending delimiter

while($_=~/"((?:""|[^"])*)",|([^,]*),/g) {
  $cell=defined($1) ? $1:$2; $cell=~s/""/"/g; 
  print "$cell\n";
}

# The regexp strategy is as follows:
# First - we attempt a match on any quoted part starting the CSV line:-
#  "((?:""|[^"])*)",
# It must start with a quote, and end with a quote followed by a comma, and is allowed to contain either doublequotes - "" - or anything except a sinlge quote [^"] - this goes into $1
# If we can't match that, we accept anything up to the next comma instead, & put it into $2
# Lastly, we convert "" to " and print out the cell.

Имейте в виду, что файлы CSV могут содержать ячейки со встроенными символами новой строки внутри кавычек, поэтому вам нужно будет сделать это, если вы читаете данные по очереди:

if("$pre$_"=~/,"[^,]*\z/) {
  $pre.=$_; next;
}
$_="$pre$_";
0 голосов
/ 09 февраля 2010

Протестировано:


use Test::More tests => 2;

use strict;

sub splitCommaNotQuote {
    my ( $line ) = @_;

    my @fields = ();

    while ( $line =~ m/((\")([^\"]*)\"|[^,]*)(,|$)/g ) {
        if ( $2 ) {
            push( @fields, $3 );
        } else {
            push( @fields, $1 );
        }
        last if ( ! $4 );
    }

    return( @fields );
}

is_deeply(
    +[splitCommaNotQuote('S,"D" REL VALVE ASSY,000771881,')],
    +['S', '"D" REL VALVE ASSY', '000771881', ''],
    "Quote in value"
);
is_deeply(
    +[splitCommaNotQuote('S,"BELT V,FAN",000324244,')],
    +['S', 'BELT V,FAN', '000324244', ''],
    "Strip quotes from entire value"
);
0 голосов
/ 11 марта 2009

Вы можете (попытаться) использовать CPAN.pm, чтобы просто установить / обновить вашу программу Text :: CSV. Как было сказано ранее, вы даже можете «установить» его в домашний или локальный каталог и добавить этот каталог в @INC (или, если вы предпочитаете не использовать блоки BEGIN, вы можете use lib 'dir'; - это, вероятно, лучше).

0 голосов
/ 11 марта 2009

Поиск подходящих пар с использованием регулярных выражений является нетривиальной и обычно неразрешимой задачей. В книге Джеффри Фридла Овладение регулярными выражениями много примеров. У меня его сейчас нет, но я помню, что он использовал CSV и для некоторых примеров.

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