Преобразование файла CSV с помощью sed

Чтобы смочь импортировать некоторые данные в определенный инструмент, я должен преобразовать файл CSV от этого формата

"data","data","data data","data","123"

в этот формат

data;data;data data;data;123

Столбцы никогда не содержат никого ", ; или , но могут быть пробелы. В настоящее время я использую следующее

sed -e 's/","/;/g' -e 's/"//g' input.csv > output.csv

Хотя это хорошо работает, интересно, может ли это быть сделано более изящно, т.е.

  • Действительно ли sed является правом (стандартный Unix) инструмент для задания?
  • Было бы возможно объединить оба выражения в одно?

Спасибо за Ваш вход!

7
задан 10.10.2009, 17:46

3 ответа

( tr , ';' | tr -d '"' ) < input.csv > output.csv

Я использовал бы Perl

perl -pe 'tr/,"/;/d' input.csv > output.csv

- но эта определенная задача не вне sed. Вы не можете объединить эти два выражения.

6
ответ дан 07.12.2019, 14:38
  • 1
    Спасибо за Ваш ответ, по моему скромному мнению, два хороших решения. Вы могли объяснить. в одном TR использования? Это не то же как [: punct:], правильно? TR человека не помогает мне. Это, кажется, что-то вроде вопроса вкуса, какой ответ является лучшим. Если авторы других ответов не возразят, то я установлю это как принятый ответ, потому что это выглядит очень изящным мне, и сообщество оценило его самое высокое до сих пор. – middus 11.10.2009, 13:35
  • 2
    я не возражаю. я неравнодушен к версии жемчуга сам. скалы TR perl. – quack quixote 11.10.2009, 13:56

Который Вы предпочитаете (жемчуг, sed, awk) ваше дело; они все сделают задание. Так как Вы попросили sed, и другие отправляются, здесь Вы идете. Это - более простая форма Вашего regex's и работает с Вашей строкой в качестве примера:

$ sed -e 's/"//g; s/,/;/g' infile.csv > outfile.csv

Обратите внимание, что можно присоединиться к этим двум выражениям с точкой с запятой после каждой замены. Протестированный с GNU sed v4.1.5.

Вот Ваши исходные выражения, к которым присоединяются:

$ sed -e 's/","/;/g; s/"//g' infile.csv > outfile.csv

Я довольно уверен, что возможно объединить эти две замены. Не уверенный, что это было бы пренебрежительно, и я вполне уверен, результат будет намного менее читаем, чем сценарий наверху. Если я придумаю что-то (или кто-то еще взвешивается в комментариях), то я добавлю его здесь.

5
ответ дан 07.12.2019, 14:38
  • 1
    "Можно присоединиться к двум substitions" - Вы, Вы не можете. Вы взяли два выражения и заменили их два выражения. – ayrnieu 10.10.2009, 21:00
  • 2
    его оригинал был '-e "нечто"-e "панель"', я присоединился к ним в '-e "нечто; панель"'.-e является выражением, которое я отсылаю к и предположил, что он обращался к. Вы могли бы быть правы - я неправильно истолковал то, что он просит - но Вы также неправильно читаете мой оператор. – quack quixote 10.10.2009, 21:13
  • 3
    разъяснен. я надеюсь. :) – quack quixote 10.10.2009, 21:20
  • 4
    Это прохладно, я не знал, что Вы могли просто присоединиться к выражениям как этот. Спасибо за Ваш ответ! – middus 11.10.2009, 13:16

Так как Вы имеете дело с записями, awk имеет больше смысла. Тем не менее это не действительно хорошо в CSV, так как разделители полей являются несколько переменными. Но если Вы будете уверены, что все поля окружаются doublequotes, то это будет работать:

awk -F'","' 'BEGIN {OFS=";"} { gsub(/(^")|("$)/, ""); $1=$1; print }'

Это устанавливает разделитель поля ввода awk на"",""(включая внутренний набор doublequotes). Это почти работает, кроме Вас должны иметь дело с продвижением и запаздыванием doublequotes, которые разделяются с gsub функция. $1=$1 силы это для перекомпиляции записи с новым выходным разделителем полей, который был определен как ; в НАЧАТЬ блоке. Затем print распечатывает целую запись.

Это немного более опрятно:

awk -F '(^")|(",")|("$)' 'BEGIN {OFS=";"} { $1=$1; print }'

Это устанавливает разделитель поля ввода на регулярное выражение, которое включает doublequotes вначале и конец записи, но это также заставляет это распечатывать пустое начало и запаздывающее поле. Можно легко избавиться от запаздывающего поля:

awk -F '(^")|(",")|("$)' 'BEGIN {OFS=";"} { NF=NF-1; $1=$1; print }'

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

Если Вы знаете, что вход всегда имеет пять полей, тем не менее, Вы могли сделать это:

awk -F '(^")|(",")|("$)' 'BEGIN {OFS=";"} { print $2,$3,$4,$5,$6 }'

Заметьте, что это избавляется от $1=$1 создайте, в котором мы только нуждаемся, если мы печатаем (подразумеваемые) 0$.

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

4
ответ дан 07.12.2019, 14:38
  • 1
    Хорошо, это выглядит немного более сложным, чем другие решения и не слишком читаемо. Если бы я столкнулся с этим за один год, то я, вероятно, должен был бы задаться вопросом, что это делает. Однако хорошо видеть, что несколько различных инструментов (awk, sed...) подходят для задачи. Спасибо за Ваш подробный ответ. Я возьму его в качестве точки входа для изучения awk для других проблем. – middus 11.10.2009, 13:22
  • 2
    это выглядеть хуже, чем он, должен быть. после того как Вы начинаете изучаете немного awk находится легче не читается. :) – quack quixote 11.10.2009, 16:27

Теги

Похожие вопросы