Скрипт Sed для редактирования CSV-файла или Python

В нашем проекте нам нужно импортировать файл csv в postgres. Существует несколько типов файлов, что означает, что длина файла изменяется, так как некоторые файлы имеют меньшее количество столбцов, а некоторые — все столбцы.

Нам нужен быстрый способ импортировать этот файл в postgres. Я хочу использовать COPY FROM из postgres, так как требования к скорости обработки очень высоки (почти 150 файлов в минуту с размером файла 20 КБ каждый).

Поскольку номера столбцов файла не фиксированы, мне нужно предварительно обработать файл, прежде чем передать его процедуре postgres. Предварительная обработка заключается в добавлении дополнительных запятых в csv для столбцов, которых нет в файле.

У меня есть два варианта предварительной обработки файла: использовать python или использовать Sed.

Мой первый вопрос: какой самый быстрый способ предварительной обработки файла?

Второй вопрос: если я использую sed, как мне вставить запятую после, скажем, 4-го, 5-го поля запятой?
например. если в файле есть такие записи, как 1,23,56,мы,89,2009-12-06, и мне нужно отредактировать файл с окончательным выводом, например: 1,23,56,мы,,89,,2009-12-06


person Sujit    schedule 02.03.2010    source источник


Ответы (6)


Знаете ли вы, что COPY FROM позволяет указать, какой столбцы (а также в каком порядке) должны быть импортированы?

COPY tablename ( column1, column2, ... ) FROM ...

Прямое указание на уровне Postgres, какие столбцы импортировать и в каком порядке, как правило, будет самым быстрым и эффективным методом импорта.

При этом существует гораздо более простой (и портативный) способ использования sed (чем то, что было представлено в других сообщениях) для заменить n-е вхождение, например. замените 4-е и 5-е вхождения запятой двойными запятыми:

echo '1,23,56,we,89,2009-12-06' | sed -e 's/,/,,/5;s/,/,,/4'

производит:

1,23,56,we,,89,,2009-12-06

Обратите внимание, что я сначала заменил крайние правые поля (#5).

Я вижу, что вы также пометили свой вопрос как связанный с perl, хотя вы не делаете явной ссылки на perl в теле вопроса; вот одна из возможных реализаций, которая дает вам возможность также переупорядочивать или иным образом обрабатывать поля:

echo '1,23,56,we,89,2009-12-06' |
  perl -F/,/ -nae 'print "$F[0],$F[1],$F[2],$F[3],,$F[4],,$F[5]"'

также производит:

1,23,56,we,,89,,2009-12-06

Аналогично с awk, для протокола:

echo '1,23,56,we,89,2009-12-06' |
  awk -F, '{print $1","$2","$3","$4",,"$5",,"$6}'

Я оставлю Python кому-то другому. :)

Небольшое примечание к примеру Perl: я использую параметры -a и -F для авторазбиения, поэтому у меня более короткая командная строка; однако это оставляет новую строку встроенной в последнее поле ($F[5]), что нормально, если это поле не нужно переупорядочивать где-то еще. Если возникнет такая ситуация, потребуется немного больше ввода, чтобы заменить новую строку через chomp, затем вручную split и, наконец, напечатать наш собственный символ новой строки \n (пример awk выше не имеет этой проблемы):

perl -ne 'chomp;@F=split/,/;print "$F[0],$F[1],$F[2],$F[3],,$F[4],,$F[5]\n"'

РЕДАКТИРОВАТЬ (идея, вдохновленная Вивином):

COMMAS_TO_DOUBLE="1 4 5"
echo '1,23,56,we,89,2009-12-06' |
  sed -e `for f in $COMMAS_TO_DOUBLE ; do echo "s/,/,,/$f" ; done |
    sort -t/ -k4,4nr | paste -s -d ';'`

1,,23,56,we,,89,,2009-12-06

Извините, не удержался. :)

person vladr    schedule 02.03.2010
comment
Мне это нравится! Я и не знал, что можно вот так передать число! Сегодня я узнал кое-что новое :) - person Vivin Paliath; 03.03.2010
comment
есть ли способ заменить sed -e 's/,/,,/5;s/,/,,/4' из вашего ответа, чтобы мне не пришлось повторять 's/,/,,/' и просто дайте список номеров, например {5,4}, упомянув его один раз. - person Sujit; 03.03.2010
comment
@SystemMatrix, не то, чтобы я знал об этом - и, вероятно, не в переносимом виде (не то, чтобы вас, вероятно, заботила :) о переносимости в вашем конкретном случае.) @vivin, конечно, почему бы и нет :) sed -e `for f in 4 5 ; do echo "s/,/,,/$f" ; done | sort -t/ -k4,4nr | paste -s -d ';'` - person vladr; 03.03.2010
comment
@ Влад, хе-хе, я знал, что будет задействован for..in. Прошло некоторое время с тех пор, как я писал сценарии оболочки. Я обычно использую Perl в эти дни. Кроме того, я никогда не пытался помещать операторы for в файл sed. Я maned увидел, что флаг -e предназначен для оценки его как скрипта. Аккуратный! :) - person Vivin Paliath; 03.03.2010
comment
@vivin, на самом деле оболочка уже выполнила цикл for и все остальное между обратными галочками, прежде чем взять весь этот вывод и передать его в sed (как строку регулярного выражения) через argv[2]. - person vladr; 03.03.2010
comment
Вау, это аккуратно! Я должен буду использовать это когда-нибудь. - person Vivin Paliath; 03.03.2010

Чтобы ответить на ваш первый вопрос, sed будет меньше накладных расходов, но может быть болезненным. awk было бы немного лучше (он мощнее). Perl или Python имеют больше накладных расходов, но с ними проще работать (что касается Perl, это может быть немного субъективно;). Лично я бы использовал Perl).

Что касается второго вопроса, я думаю, что проблема может быть немного сложнее. Например, разве вам не нужно проверять строку, чтобы выяснить, каких полей на самом деле не хватает? Или гарантировано, что всегда будет 4-й и 5-й? Если это первый случай, было бы намного проще сделать это в Python или Perl, чем в sed. В противном случае:

echo "1,23,56,we,89,2009-12-06" | sed -e 's/\([^,]\+\),\([^,]\+\),\([^,]\+\),\([^,]\+\),\([^,]\+\),/\1,\2,\3,\4,,\5,,/'

или (проще для глаз):

echo "1,23,56,we,89,2009-12-06" | sed -e 's/\(\([^,]\+,\)\{3\}\)\([^,]\+\),\([^,]\+\),/\1,\3,,\4,,/'

Это добавит запятую после 5-го и 4-го столбцов, если в тексте нет других запятых.

Или вы можете использовать два sed для чего-то менее уродливого (хотя и ненамного):

echo "1,23,56,we,89,2009-12-06" | sed -e 's/\(\([^,]*,\)\{4\}\)/\1,/' | sed -e 's/\(\([^,]*,\)\{6\}\)/\1,/'
person Vivin Paliath    schedule 02.03.2010
comment
чрезмерно сложный синтаксис sed и непереносимость (не работает с POSIX sed, т.е. без поддержки регулярных выражений-расширителей) - person vladr; 03.03.2010
comment
Вот почему я сказал, что sed, вероятно, плохое решение для этого. Perl или Python были бы намного лучше, проще и, как вы упомянули, переносимы. - person Vivin Paliath; 03.03.2010
comment
нет, см. ниже простой и портативный способ использования sed. Кроме того, perl и python вводят накладные расходы на файл (время установки/загрузки), но в долгосрочной перспективе их механизмы регулярных выражений могут быть более эффективными, чем sed. - person vladr; 03.03.2010
comment
Я говорил о случае, когда нельзя быть уверенным, что позиции находятся на 4-й и 5-й. Если они произвольны, sed не будет подходящим инструментом. - person Vivin Paliath; 03.03.2010

@OP, вы обрабатываете CSV-файл с отдельными полями и разделителями. Используйте инструмент, который может разбиваться на разделители и дает вам возможность легко работать с полями. sed не является одним из них, хотя это можно сделать, как предлагалось в некоторых ответах, но вы получите регулярное выражение sed, которое трудно читать, когда оно усложняется. Используйте такие инструменты, как awk/Python/Perl, где они легко работают с полями и разделителями, лучше всего доступны модули, специально предназначенные для обработки csv. Для вашего примера простой подход Python (без использования модуля csv, который в идеале вы должны попытаться использовать)

for line in open("file"):
    line=line.rstrip() #strip new lines
    sline=line.split(",")
    if len(sline) < 8: # you want exact 8 fields
        sline.insert(4,"")
        sline.insert(6,"")
        line=','.join(sline)
    print line

выход

$ more file
1,23,56,we,89,2009-12-06

$ ./python.py
1,23,56,we,,89,,2009-12-06
person ghostdog74    schedule 02.03.2010
comment
Я согласен, это более чистый способ выполнить задачу. Но из-за требования скорости я должен пойти с Седом. - person Sujit; 05.03.2010

sed 's/^([^,]*,){4}/&,/' <original.csv >output.csv

Добавит запятую после 4-го поля, разделенного запятой (сопоставив 4 повторения <anything>,, а затем добавив запятую после этого). Обратите внимание, что есть улов; убедитесь, что ни одно из этих значений не является строкой в ​​кавычках с запятыми.

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

person Amber    schedule 02.03.2010

Не знаю насчет скорости, но вот sed expr, который должен выполнить эту работу:

sed -i 's/\(\([^,]*,\)\{4\}\)/\1,/' file_name

Просто замените 4 на требуемое количество столбцов

person dimba    schedule 02.03.2010

В зависимости от ваших требований рассмотрите возможность использования программного обеспечения ETL для этой и будущих задач. Такие инструменты, как Pentaho и Talend предлагает вам большую гибкость, и вам не нужно писать ни одной строки кода.

person paprika    schedule 02.03.2010