2014-12-14 4 views
1

У меня есть файл CSV, и некоторые из полей пустые.Perl regex, значение вставки между запятыми

Примеры данных

ItemA,5,4,3,2,1 
ItemB,7,,,2,4 
ItemC,,,,2, 
ItemD,,3,,, 

Я хотел бы передать Perl-один лайнер над файлом, который будет вставить строку, скажем, NULL между каждой запятой, или после последней запятой, если значение не существует после Это.

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

ItemA,5,4,3,2,1 
ItemB,7,NULL,NULL,2,4 
ItemC,NULL,NULL,NULL,2,NULL 
ItemD,NULL,3,NULL,NULL,NULL 

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

ответ

3

Простые, используйте положительные lookbehind и lookahead утверждения, как показано ниже.

$ perl -pe 's/(?<=,)(?=,|$)/NULL/g' file 
ItemA,5,4,3,2,1 
ItemB,7,NULL,NULL,2,4 
ItemC,NULL,NULL,NULL,2,NULL 
ItemD,NULL,3,NULL,NULL,NULL 
  • (?<=,) Просто lookafter всем запятых. То есть, он утверждает, что совпадению должно предшествовать запятая.

  • (?=,|$) И сразу же после матча запятая или конец линейного якоря. Таким образом, он соответствует всем границам, которые существуют между всеми запятыми и таковыми, которые находятся рядом с запятой, которая находится в последней.

  • Замена согласованной границы на строку NULL даст вам желаемый результат.

2

Я не буду использовать регулярное выражение. Используйте модуль ядра Text::ParseWords:

perl -MText::ParseWords -lne' 
    @w = parse_line (",", 1, $_); 
    print join ",", map { (length) ? $_ : "NULL" } @w 
' file 
ItemA,5,4,3,2,1 
ItemB,7,NULL,NULL,2,4 
ItemC,NULL,NULL,NULL,2,NULL 
ItemD,NULL,3,NULL,NULL,NULL 

или в качестве альтернативы использовать необязательный третий из параметров для split.

perl -lne' 
    @F = split ",", $_, -1; 
    print join ",", map { (length) ? $_ : "NULL" } @F 
' file 
ItemA,5,4,3,2,1 
ItemB,7,NULL,NULL,2,4 
ItemC,NULL,NULL,NULL,2,NULL 
ItemD,NULL,3,NULL,NULL,NULL 
+0

OP запросил регулярное выражение perl oneliner, вы говорите, что не будете использовать регулярное выражение, по какой-либо причине? – outlyer

+2

@outlyer Это просто мое личное мнение. Разбор csv-файла с использованием regex никогда не является хорошим выбором для начала. Решение может быть коротким, но может нарушаться в нечетных вариантах использования. –

+0

Согласованные, регулярные выражения не являются хорошим выбором для CSV, хотя ваше альтернативное решение с 'split' также потерпит неудачу в некоторых случаях использования.'Text :: ParseWords' выглядит намного более надежным. – outlyer

0

Вы можете использовать это:

#!/usr/bin/perl 

use strict; 
use warnings; 

my $fpath = "./data.txt"; 

open(my $fh, '<', $fpath) or die "*** Cannot open $fpath:\n$!\n"; 

while (<$fh>) { 
    while(1) { last if ($_ !~ s/,\s*,/,NULL,/g) } 
    $_ =~ s/,\s*$/,NULL/g; 
    print $_ . "\n"; 
} 
+0

Я тестировал, и это не сработало, оно заменило некоторые, но не все экземпляры. Почему в regex есть \ s *? – tman

+0

Файл './Data.txt' является вашим CSV-файлом. –

2

Вот одно решение с регулярным выражением:

$ perl -pe 's/,(?=,)|,$/,NULL/g' file.csv 

ItemA,5,4,3,2,1 
ItemB,7,NULL,NULL,2,4 
ItemC,NULL,NULL,NULL,2,NULL 
ItemD,NULL,3,NULL,NULL,NULL 

Т.е. немедленно сопоставить запятую, за которой следует запятая (,(?=,)) ИЛИ запятая в конце строки (,$) и заменить соответствующую запятую ,NULL.


EDIT: Как было отмечено Казимир и Ипполит, s/,(?=,|$)/,NULL/g проще.

(Соответствует запятой, за которой следует другая запятая или конец линии).

+1

поместите свое чередование в lookahead. –

+0

@CasimiretHippolyte хорошая точка – outlyer