В настоящее время у меня есть несколько файлов csv, и я не могу контролировать их создание. Излишне говорить ... они имеют неправильный формат и не соответствуют RFC 4180.

Пример ввода: gist

",0000000000000000";"0";"1115S021121-12-1/2"M"
",0000000000000000";"0";"1115S021122-12-1/2"M"
",0000000000000000";"0";"1115S021123-12-1/2"M"
",0000000000000000";"0";"1115S021124-12-1/2"M"
"1";"1";"EXAMPLE_RANDOM" .    STRING"
"2,0000000000000000";"2";"this;can"also happen"

Желаемое:

",0000000000000000";"0";"1115S021121-12-1/2""M"

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

Может ли кто-нибудь помочь мне избежать «дюймовой кавычки» внутри двойных кавычек? Я знаю, что такие решения только на 99%, я могу полагаться только на следующие факты.

  • Разделитель есть;
  • Корпус "
  • "может встречаться несколько раз в текстовом поле с кавычками.

Это означает: или "может появиться в цитируемых полях. Может ли кто-нибудь помочь мне заменить" на ""?

Моя попытка регулярного выражения, объединяющего несколько сообщений stackoverflow.

 sed -E "s/[^\"](?<!;)\"(?!;|$)/\1"/g" $filename.test2   -> error
 sed "s/[^\"](?<!;)(\")(?!;|$)/\1/g" $filename.test2    -> error 
 ... about 10 more variations, some even without errors but no replaced strings.

Если у кого-то есть другое решение, кроме регулярного выражения, любая помощь очень ценится!

Изменить: благодаря @choroba, мастеру perl. Следующее исправляет файл.

 cat $filename.test | perl -pe 's/(?<=[^;])"(?=[^;])/""/g' >  $filename.test2
2
Sam 5 Дек 2018 в 11:20

2 ответа

Лучший ответ

На помощь приходят осмотрительные утверждения Perl!

perl -pe 's/(?<=[^;])"(?=[^;\n])/""/g' 

То есть если есть ", перед которым не стоит ; и за которым не следует ;, замените его на "".

4
choroba 11 Дек 2018 в 17:01
$ perl -MText::CSV_XS=csv -wE'csv(in=>csv(in=>"test.csv",sep=>";",allow_loose_quotes=>1,allow_loose_escapes=>1),always_quote=>1)'
",0000000000000000","0","1115S021121-12-1/2""M"
",0000000000000000","0","1115S021122-12-1/2""M"
",0000000000000000","0","1115S021123-12-1/2""M"
",0000000000000000","0","1115S021124-12-1/2""M"
"1","1","EXAMPLE_RANDOM"" .    STRING"
"2,0000000000000000","2","this;can""also happen"

Как заметил чороба, с ";" как выход sep тоже:

$ perl -MText::CSV_XS=csv -wE'csv(in=>csv(in=>"test.csv",sep=>";",allow_loose_quotes=>1,allow_loose_escapes=>1),always_quote=>1,sep=>";")'
",0000000000000000";"0";"1115S021121-12-1/2""M"
",0000000000000000";"0";"1115S021122-12-1/2""M"
",0000000000000000";"0";"1115S021123-12-1/2""M"
",0000000000000000";"0";"1115S021124-12-1/2""M"
"1";"1";"EXAMPLE_RANDOM"" .    STRING"
"2,0000000000000000";"2";"this;can""also happen"

Пояснение добавлено по запросу:

Text :: CSV_XS - это модуль Perl, который может очень гибко анализировать и генерировать CSV. Использование параметров / атрибутов, чтобы разрешить плохо отформатированный CSV.

  1. csv (in => "file.csv", ...) считывает файл во внутреннюю структуру

  2. sep => ";" устанавливает символ-разделитель на ";" вместо стандартного ","

  3. allow_loose_quotes => 1 и allow_loose_escapes => 1 позволяют читать неверный CSV и принимать неэкранированные вложенные кавычки

  4. csv () возвращает ссылку на внутреннюю структуру, которая действительна для внешнего вызова csv, который генерирует выходной csv (in => csv (in => "file.csv")

  5. Последние два аргумента устанавливают sep равным ";" для вывода тоже и привести все поля в кавычки, поскольку OP требуется

См. https://metacpan.org/module/Text::CSV_XS для всех параметров и Примеры

Отформатированный в скрипте, он может выглядеть так:

use Text::CSV_XS qw( csv );

csv (                    # Outer function
    always_quote => 1,   # Quote all field
    sep          => ";", # Use ; instead of ,
    in           =>      # Input
        csv (            #   comes from inner function
            in                  => "test.csv", # a file
            sep                 => ";",        # ; instead of ,
            allow_loose_quotes  => 1,          # allow ,"foo"bar",
            allow_loose_escapes => 1,          # idem
            )
    );
2
Tux 5 Дек 2018 в 11:14