Как использовать команду “cut” для вырезания полей в CSV-файле, когда поля содержат запятые?

Вопрос или проблема

Формат файлов CSV выглядит следующим образом

"Пицца,крылья,мороженое","хоппер","0",,"Масдфасдф","США","381","10 УТ","кап",,,"монстр"

Использование запятой в качестве разделителя, как показано ниже, приведет к тому, что поля с запятыми будут считаться разделителями, чего я не хочу.

cat /path/to/file | cut -d , -f2

вернет

крылья

когда я хочу

хоппер

Я думаю, что логически нашел решение, но не уверен, как его реализовать.

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


Пример

[tksmith@rifle ~]$ cat deleteme 

"Пицца,крылья,мороженое","хоппер","0",,"Масдфасдф","США","381","10 УТ","кап",,,"монстр"

[tksmith@rifle ~]$ cat deleteme | cut -d , -f2

крылья

Формат CSV имеет достаточно сложных случаев, чтобы вы не хотели заново изобретать его разбор. Используйте правильный парсер CSV. Например, с помощью Ruby

ruby -rcsv -e 'CSV.foreach(ARGV.shift) {|row| puts row[1]}' файл

Команда cut разделяет только один символ за раз. Так что вы можете преобразовать кавычки и запятые в другой разделитель с помощью sed и регулярного выражения. Например:

sed -e 's/","\|",\|,"\|,,/@/g' /path/to/file | cut -d@ -f2

Вернет

хоппер

Другой способ – использовать AWK. Например, следующее даст вам 2-е поле.

awk -F ',,|^"|","|",|,"|"$' '{print $3}' /path/to/file

вернет

хоппер

Приведенные выше примеры не идеальны, так как они не обрабатывают корректно все случаи с несколькими пустыми полями и внешними кавычками. Лучше использовать язык сценариев более высокого уровня с модулем для разбора CSV.

Вы должны использовать библиотеку для обработки CSV (как упоминали другие), например в Ruby:

ruby -rcsv -e 'CSV.foreach(ARGV.shift) {|row| puts row[1]}' файл

Но мой предпочтительный метод – использовать csvkit, установите его с помощью pip install csvkit (если у вас уже есть рабочая среда Python).

А затем после добавления заголовков к файлу запустите его с помощью

csvcut -f second_column deleteme

Ответ или решение

Чтобы извлечь поля из CSV-файла, содержащих запятые, необходимо учитывать наличие кавычек (") вокруг некоторых значений. Использование стандартной команды cut с разделителем в виде запятой не подходит, так как она не учитывает контекст, в котором могут находиться запятые – то есть, внутри кавычек. Рассмотрим несколько методов решения этой задачи.

1. Использование sed для замены разделителей

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

sed -e 's/","\|",\|,"\|,,/@/g' /path/to/file | cut -d@ -f2

Данный код работает следующим образом:

  • sed заменяет запятые, которые находятся между кавычками или рядом с пустыми полями, на символ @. Это позволяет изолировать нужные данные, чтобы затем использовать cut.
  • После этого cut может корректно извлечь второе поле на основе нового разделителя.

2. Использование awk

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

awk -F ',,|^"|","|",|,"|"$' '{print $3}' /path/to/file

Здесь:

  • -F задаёт несколько разделителей, включая запятые, которые могут находиться в разнообразных контекстах.
  • $3 указывает на вывод третьего поля, что соответствует вашему примеру, если учитывать запятые и кавычки.

3. Использование языков программирования с поддержкой CSV

Как наиболее оптимальное решение, рекомендуется использовать подходящие языки программирования с готовыми библиотеками для обработки CSV. В Python, например, можно воспользоваться csvkit, который упрощает работу с CSV-файлами:

  1. Установите csvkit:

    pip install csvkit
  2. После этого можно использовать команду csvcut для выбора нужного столбца, предварительно добавив заголовки в ваш CSV-файл:

    csvcut -f 2 deleteme

Также можно использовать язык Ruby с встроенной библиотекой CSV:

ruby -rcsv -e 'CSV.foreach(ARGV.shift) {|row| puts row[1]}' deleteme

Заключение

Использование простых Unix-утилит для обработки CSV может оказаться неэффективным, особенно если в файле есть много нюансов, таких как запятые внутри кавычек или пустые поля. Поэтому, для более комплексных задач обработки CSV, лучше всего применять специализированные библиотеки и языки, которые обеспечивают более точное разбиение данных без необходимости самостоятельно разбираться с нюансами формата.

Оцените материал
Добавить комментарий

Капча загружается...