Для обмена данными многие приложения используют формат 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.