用于编辑csv文件或Python的sed脚本

时间:2010-03-02 22:28:06

标签: python sed awk text-processing

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

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

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

我有两种方法可以预处理文件 - 使用python或使用Sed。

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

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

6 个答案:

答案 0 :(得分:3)

您是否意识到COPY FROM允许您指定要导入哪些列(以及它们的顺序)?

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

在Postgres级别直接指定要导入的列以及按什么顺序排列,通常是最快且最有效的导入方法。

有人说过,使用sed(比其他帖子中提到的内容)更简单(和便携)的方式,replace an n th occurrence,例如用双逗号替换逗号的第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选项进行autosplit,因此我有一个较短的命令字符串;但是,这会将换行符嵌入到最后一个字段($F[5])中,只要该字段不必在其他地方重新排序即可。如果出现这种情况,则需要稍微更多的输入才能通过chomp,然后split手动删除换行符,最后打印我们自己的换行符\n({{1}上面的例子没有这个问题):

awk

EDIT(受Vivin启发的想法):

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

抱歉,忍不住了。 :)

答案 1 :(得分:2)

要回答您的第一个问题,sed会减少开销,但可能会很痛苦。 awk会更好一点(它更强大)。 Perl或Python有更多的开销,但更容易使用(关于Perl,这可能有点主观;)。就个人而言,我会使用Perl)。

就第二个问题而言,我认为这个问题可能会更复杂一些。例如,您是否需要检查字符串以确定哪些字段实际丢失?还是保证它永远是第4和第5?如果是第一种情况,那么在Python或Perl中使用 way 更容易,而不是在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,/'

答案 2 :(得分: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

答案 3 :(得分:0)

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

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

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

答案 4 :(得分:0)

不知道关于速度,但这里是应该做的工作的sed expr:

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

只需用精确的列数替换4

答案 5 :(得分:0)

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