Удалить ложные запятые
Идиотский клиент генерирует 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.