Как сделать CSV файл совместимым с Excel

Для обмена данными многие приложения используют формат CSV. Этот формат стал своего рода стандартом и к нему выработаны требования. Перечень требований на английском (CSV well formedness): Требования к CSV

В данной заметке я расскажу, как исправить нарушение требования к экранированию двойных кавычек, которое может повлечь лишние колонки при импорте данных в Excel. Будет использован небольшой скрипт на perl.
Предусловием является то, что разделитель в CSV вы можете настроить в отличие от экранирования кавычек. Пусть это будет символ \x01, который обычно в текстовых файлах не встречается. Еще одно допущение: считается что кавычки не экранированы во всем документе.

Корректно сгенерированная строка:

Jack;McGinnis;220 hobo Av.;Phila; PA;09119

Для наглядности символ \x01 (наш сконфигурированный разделитель) заменен на точку с запятой.

Пример неправильной строки (вам понадобилось использовать запятую не как разделитель, а как символ в одном из полей с данными):

John "Da, Man" ;Repici;120 Jefferson St.;Riverside; NJ;08075

Для наглядности символ \x01 (наш сконфигурированный разделитель) заменен на точку с запятой.

Эксель при импорте этого файла разделит первое поле по запятой и по сравнению с правильно сформированной строкой неожиданно появится дополнительная колонка.

Вот во что должна превратиться строка в итоге:

"John ""Da, Man""",Repici,120 Jefferson St.,Riverside, NJ,08075

Такую строку Exсel поймет и не создаст лишнюю колонку.

Выполняемая команда:

cat badly_formed.csv | perl -ne 'chomp; @a = split chr(1); print join( ",", map { s/"/""/g; qq["$_"] } @a), "\n"'

Что происходит при вызове этой длинной команды?

Команда cat печатает ваш файл. Это заглушка, которая имитирует то самое приложение (выводящее в поток CSV), в котором вы настраиваете разделитель, но не можете настроить экранирование кавычек.

Вторая команда в конвейере принимает строки по одной.
chomp убирает ‘\n’ — перевод строки
Далее строка разбивается по нашему разделителю \x01 в массив (split).

Теперь надо раскручивание стека начать из глубины 🙂

map() перебирает элементы массива/списка, заменяет в нем одинарные кавычки на двойные, и прячет весь элемент в двойные кавычки.
join() объединяет их в строку, используя запятую как разделитель.
В конце строки ставим символ \n.

Вывод результата происходит в stdout.

You can leave a response, or trackback from your own site.

Leave a Reply