Sed скрипт для редактирования CSV-файла или Python - PullRequest
5 голосов
/ 03 марта 2010

В нашем проекте нам нужно импортировать 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

Ответы [ 6 ]

3 голосов
/ 03 марта 2010

Вам известно о том, что 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

Извините, не смог устоять. :)

2 голосов
/ 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
2 голосов
/ 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,/'
0 голосов
/ 03 марта 2010

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

0 голосов
/ 03 марта 2010

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

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

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

0 голосов
/ 03 марта 2010
sed 's/^([^,]*,){4}/&,/' <original.csv >output.csv

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

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

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