5

在我们的项目中,我们需要将 csv 文件导入 postgres。有多种类型的文件,这意味着文件的长度会发生变化,因为有些文件的列较少,有些文件的列全部。

我们需要一种快速将此文件导入 postgres 的方法。我想使用 postgres 的 COPY FROM 因为处理的速度要求非常高(每分钟几乎 150 个文件,每个文件大小为 20K)。

由于文件列编号不固定,因此我需要在将文件传递给 postgres 过程之前对其进行预处理。预处理只是在 csv 中为列添加额外的逗号,这些列在文件中不存在。

我有两个选项来预处理文件 - 使用 python 或使用 Sed。

我的第一个问题是,预处理文件的最快方法是什么?

第二个问题是,如果我使用 sed,我将如何在第 4、5 个逗号字段之后插入逗号?
例如,如果文件有 1,23,56,we,89,2009-12-06 之类的条目,我需要编辑文件,最终输出如下:1,23,56,we,,89,,2009-12-06

4

6 回答 6

3

您是否知道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-related,尽管您在问题正文中没有明确提及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 示例的小注释:我使用-aand-F选项来自动拆分,所以我有一个更短的命令字符串;但是,这会将换行符嵌入在最后一个字段 ( $F[5]) 中,只要该字段不必在其他地方重新排序就可以了。如果出现这种情况,需要稍微多输入一点,以便通过 删除换行符chomp,然后split手动并最终打印我们自己的换行符\nawk上面的示例没有这个问题):

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

编辑(受 Vivin 启发的想法):

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

对不起,无法抗拒。:)

于 2010-03-02T23:03:10.590 回答
2

要回答您的第一个问题,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 列之后添加一个逗号。

或者您可以使用两个seds 来表示不那么难看的东西(尽管只是稍微):

echo "1,23,56,we,89,2009-12-06" | sed -e 's/\(\([^,]*,\)\{4\}\)/\1,/' | sed -e 's/\(\([^,]*,\)\{6\}\)/\1,/'
于 2010-03-02T22:30:45.527 回答
2

@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
于 2010-03-02T23:56:21.263 回答
0
sed 's/^([^,]*,){4}/&,/' <original.csv >output.csv

将在第 4 个逗号分隔字段之后添加一个逗号(通过匹配 4 个重复<anything>,然后在之后添加一个逗号)。请注意,有一个问题;确保这些值都不是带逗号的引号字符串。

如有必要,您可以通过管道链接多个替换,或者修改正则表达式以同时添加任何需要的逗号(尽管这变得更加复杂;您需要在替换文本中使用子组捕获)。

于 2010-03-02T22:34:57.600 回答
0

不知道速度,但这里 sed expr 应该可以完成这项工作:

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

只需将 4 替换为确定的列数

于 2010-03-02T22:35:52.410 回答
0

根据您的要求,考虑将ETL软件用于此和未来的任务。PentahoTalend等工具为您提供了极大的灵活性,您无需编写任何代码。

于 2010-03-02T22:37:36.180 回答