Удалить ложные запятые

Идиотский клиент генерирует CSV-файлы, но в одном поле иногда есть дополнительные запятые (поле описания).

Есть ли регулярное выражение, чтобы найти эти плохие записи и заменить дополнительные запятые на что-то еще. Командная строка SED будет в порядке.

Пример:

A,B,C,This is a description,D,E
F,G,H,This is a description with a comma (,) in it,D,E

Мне нужен САС, который может сказать, что в строке слишком много запятых, и удалить лишнюю запятую из поля 4.

Мы не можем позволить глупому клиенту изменить свой код.

добавленной

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

4 ответа

Решение

Решение 1: однострочное, удаление ,

Вот и вы с однострочником SED:

sed -r 's/([^,],[^,],[^,],)(.*)(,.+,.+)/\1'"$(sed -r 's/([^,],[^,],[^,],)(.*)(,.+,.+)/\2/' <<< $myInput | sed 's/,//g')"'\3/' <<< $myInput

Вы должны заменить <<< $myInput с любым вашим фактическим вкладом.
Поскольку вы работаете с CSV, вам, возможно, придется настроить (в обоих случаях) регулярное выражение, чтобы оно соответствовало каждой строке таблицы CSV.
Если ваши первые три и два последних поля больше чем один символ, замените [^,] с [^,]*,

Пояснение:
Мы используем это регулярное выражение

/([^,],[^,],[^,],)(.*)(,.+,.+)/

который захватывает первое (F,G,H,) второй .*) и последняя часть (,D,E) строки для нас.
Первая и третья группы захвата останутся неизменными, а вторая будет заменена.
Для замены мы называем sed второй (и фактически третий) раз. Сначала мы фиксируем только вторую группу, затем мы заменяем каждую , ни с чем (только в группе захвата!).

Доказательство: введите описание изображения здесь

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


Решение 2: весь файл, построчно, удалить ,

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

while read line; do sed -r 's/([^,],[^,],[^,],)(.*)(,.+,.+)/\1'"$(sed -r 's/([^,],[^,],[^,],)(.*)(,.+,.+)/\2/' <<< $line | sed 's/,//g')"'\3/' <<< $line; done < input.txt

где input.txt в конце - очевидно - ваш файл.
Я просто использую SED-команду сверху внутри while -loop, который читает каждую строку текста. Это необходимо, потому что вы должны следить за строкой, которую вы читаете, когда вы звоните sed два раза на одном входе.

введите описание изображения здесь


Решение 3: весь файл, вложить поле в "

Как отметил Danubian Sailor. в комментариях к OP, согласно RFC1480, который описывает формат CSV-файлов, было бы лучше заключить поля, содержащие запятую, в ",
Это проще, чем другие решения:

sed -r 's/([^,],[^,],[^,],)(.*)(,.*,.*)/\1"\2"\3/' input.txt

Снова у нас есть три группы захвата. Это позволяет нам просто обернуть вторую группу в "!

введите описание изображения здесь

Если количество столбцов фиксировано, мы можем попробовать вырезать первые три и два последних столбца с заглядыванием ?: и сопоставьте запятые внутри остальной части строки (это описание). У меня есть что-то вроде этого:

(?:^(?:[^,]*,){3})(?:(?:[^,]*(,))*[^,]*)(?:(?:,[^,]*){2}$)

[^,]* это поле (без запятой) так (?:^(?:[^,]*,){3}) обрежет первые 3 столбца (включая следующую запятую). (?:(?:,[^,]*){2}$) удалит последние 2 столбца, включая запятую. (?:(?:[^,]*(,))*[^,]*) соответствует изнутри.

В JavaScript полное выражение возвращает полное описание (с запятыми) в качестве первого совпадения, а запятые внутри него - в качестве второго. Это дает возможность, в зависимости от движка Regex, либо пробел и заменить описание (если движок дает диапазоны совпадений выражений), либо нацелиться на (,) запятая выражения соответствия с синтаксисом замены.

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

Мой подход заключается в том, чтобы рассчитать, какие запятые нужно удалить, основываясь на общем количестве столбцов и какие столбцы вы должны изменить. Он принимает три аргумента: входной файл, общее количество столбцов и странный столбец с дополнительными запятыми.

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

#!/usr/bin/env perl 

use warnings;
use strict;
use Text::CSV_XS;

my (@columns);

open my $fh, '<', shift or die;
my ($total_columns, $weird_column) = (shift, shift);

my $csv = Text::CSV_XS->new or die;
while ( my $row = $csv->getline( $fh ) ) { 
    undef @columns;
    if ( @$row == $total_columns ) { 
        @columns = @$row;
        next;
    }   

    my $extra_columns = @$row - $total_columns;
    my $post_columns_index = $weird_column + $extra_columns;
    @columns = ( 
        @$row[0..($weird_column-2)], 
        join( '', @$row[($weird_column-1)..($post_columns_index-1)]),  
        @$row[$post_columns_index..$#$row] 
    );  
}
continue {
    $csv->print( \*STDOUT, \@columns );
    printf "\n";
}

Предполагая, что входной файл, как:

A,B,C,This is a description,D,E
F,G,H,This is a description with a comma (,) in it,D,E
F,G,H,This is, a description with two commas (,) in it,D,E
F,G,H,This is, a description with, three commas (,) in it,D,E

Запустите это как:

perl script.pl infile 6 4

Это дает:

A,B,C,"This is a description",D,E
F,G,H,"This is a description with a comma () in it",D,E
F,G,H,"This is a description with two commas () in it",D,E
F,G,H,"This is a description with three commas () in it",D,E       

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

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

cat your_file | ruby -ne '$_.scan(/^(\w+,\w+,\w+,)([^$]+)(,\w,\w)$/).each{|m|puts m[0]+m[1].gsub(",","")+m[2]}'

Это предполагает, что всегда есть 6 столбцов и что 4-й является тем, который может содержать запятые.

Код был протестирован с ruby ​​1.8.7, 1.9.1 и 2.1.0.

Другие вопросы по тегам