使用sed操纵巨大的CSV文件

时间:2009-07-23 19:37:24

标签: regex vim csv sed

我有一组需要修改的4个大量CSV文件。我需要做的是匹配这个表达式/^(.*),,/复制原子然后将它添加到每个后续行,直到原子再次匹配。然后我需要冲洗并重复直到文件的末尾(每个文件中有大约25k行)。最后,我需要回过头去掉第一个原子。

如果有可能,我想用sed。我尝试用vim做这个但是无法正确使用正则表达式。任何帮助将不胜感激。一个例子如下所示:

之前:

0917,,
,882-1273,1
,95F 9475,1
,276-080,1
,40K 0080,1
,275-690A,1
,TX-2311,3
,TX-3351,4
,B-07432,1
,B-6901,1
,23-753,1
,02F 4307,1
,5.1K QBK-ND,1
,0944-026,1
,0944-027,1
,0944-004,1
,0944-056,1
,0944-057,1
,0944-082,1
,0944-024,1
,0944-025,1
,0944-102,4
,LOR 102,1
0918,,
,CJ1085,1
,1352-152,4
,DMS3102A-18-,1
,6-32 KEP,7
,6-32 X 3/4,4
,6-32X1/2,4
,1251-102,8
,Oct-32,4
,10-32 SAE,8

后:

0917,882-1273,1
0917,95F 9475,1
0917,276-080,1
0917,40K 0080,1
0917,275-690A,1
0917,TX-2311,3
0917,TX-3351,4
0917,B-07432,1
0917,B-6901,1
0917,23-753,1
0917,02F 4307,1
0917,5.1K QBK-ND,1
0917,0944-026,1
0917,0944-027,1
0917,0944-004,1
0917,0944-056,1
0917,0944-057,1
0917,0944-082,1
0917,0944-024,1
0917,0944-025,1
0917,0944-102,4
0917,LOR 102,1
0918,CJ1085,1
0918,1352-152,4
0918,DMS3102A-18-,1
0918,6-32 KEP,7
0918,6-32 X 3/4,4
0918,6-32X1/2,4
0918,1251-102,8
0918,Oct-32,4
0918,10-32 SAE,8

7 个答案:

答案 0 :(得分:3)

正如我在前一个问题中指出的那样,我发现Perl更容易:

#!/usr/bin/perl

use strict;
use warnings;

my $prefix = q{};

while ( <> ) {
    last unless /\S/;
    if ( /^(.+),,$/ ) {
        $prefix = $1;
        next;
    }
    print $prefix, $_;
}

答案 1 :(得分:2)

程序(python)

import csv
infile=file("in","r")
outfile=file("out","w")
reader = csv.reader(infile , dialect='excel')
writer = csv.writer(outfile , dialect='excel')
current_header=""
for inrow in reader:
    if len(inrow[0].strip()) != 0:
        current_header = inrow[0]
        continue

    writer.writerow([current_header]+inrow[1:])

infile.close()
outfile.close()
print "done"

输入

0917,,
,882-1273,1
,95F 9475,1
,276-080,1
,40K 0080,1
,275-690A,1
,TX-2311,3
,TX-3351,4
,B-07432,1
,B-6901,1
,23-753,1
,02F 4307,1
,5.1K QBK-ND,1
,0944-026,1
,0944-027,1
,0944-004,1
,0944-056,1
,0944-057,1
,0944-082,1
,0944-024,1
,0944-025,1
,0944-102,4
,LOR 102,1
0918,,
,CJ1085,1
,1352-152,4
,DMS3102A-18-,1
,6-32 KEP,7
,6-32 X 3/4,4
,6-32X1/2,4
,1251-102,8
,Oct-32,4
,10-32 SAE,8

输出

0917,882-1273,1
0917,95F 9475,1
0917,276-080,1
0917,40K 0080,1
0917,275-690A,1
0917,TX-2311,3
0917,TX-3351,4
0917,B-07432,1
0917,B-6901,1
0917,23-753,1
0917,02F 4307,1
0917,5.1K QBK-ND,1
0917,0944-026,1
0917,0944-027,1
0917,0944-004,1
0917,0944-056,1
0917,0944-057,1
0917,0944-082,1
0917,0944-024,1
0917,0944-025,1
0917,0944-102,4
0917,LOR 102,1
0918,CJ1085,1
0918,1352-152,4
0918,DMS3102A-18-,1
0918,6-32 KEP,7
0918,6-32 X 3/4,4
0918,6-32X1/2,4
0918,1251-102,8
0918,Oct-32,4
0918,10-32 SAE,8

玩得开心

答案 2 :(得分:1)

由于sed的语法含糊不清,你不是日复一日地使用,而不是谈论你的同事,这段代码很难维护。 Perl / awk解决方案是首选。

无论如何,here是我见过的最好的手册。

祝你好运 迪马

答案 3 :(得分:1)

Perl可能更容易:

#!/usr/bin/perl -w

$filename = $ARGV[0];
open FILE, "<", $filename or die $!;

while (<FILE>) {
    if(/^(.*),,/) {
        $prefix = $_;
        $prefix =~ s/,//g;
        $prefix =~ s/\s+//g;
        next;   
    }
    s/^,/$prefix,/g;
    print $_;
}

close FILE;

答案 4 :(得分:0)

以下是awk的解决方案:

awk -F, '{ if ($1 != "") prefix=$1; else printf "%s%s\n", prefix,$0 }' myfile.csv

答案 5 :(得分:0)

这是一个用Perl编写的全功能示例,它使用了Perl 5.10的新功能;

#!/usr/bin/perl
use strict;
use warnings;

use feature qw'switch say';

my $append;

while( <> ){
  given( $_ ){

    when( /^$/ ){
      # handle empty line
      say STDERR '#';
    }

    # handle lines that start with "#"
    when( /^\s*[#](.*)/s ){
      print STDERR '# comment:', $1;
    }

    # handle lines that end with two commas
    when( /(.+),,\s*$/ ){
      $append = $1;
    }

    # handle lines that start with a comma
    when( /^,/ ){
      die unless defined $append;
      print $append, $_;
    }
  }
}

输入

0917,,
,882-1273,1
,95F 9475,1
,276-080,1

,40K 0080,1
,275-690A,1
,TX-2311,3
# ignore
 # ignore this too
,TX-3351,4
,B-07432,1
,B-6901,1
,23-753,1
,02F 4307,1
,5.1K QBK-ND,1
,0944-026,1
,0944-027,1
,0944-004,1
,0944-056,1
,0944-057,1
,0944-082,1
,0944-024,1
,0944-025,1
,0944-102,4
,LOR 102,1
0918,,
,CJ1085,1
,1352-152,4
,DMS3102A-18-,1
,6-32 KEP,7
,6-32 X 3/4,4
,6-32X1/2,4
,1251-102,8
,Oct-32,4
,10-32 SAE,8

输出

#
# comment: ignore
# comment: ignore this too
0917,882-1273,1
0917,95F 9475,1
0917,276-080,1
0917,40K 0080,1
0917,275-690A,1
0917,TX-2311,3
0917,TX-3351,4
0917,B-07432,1
0917,B-6901,1
0917,23-753,1
0917,02F 4307,1
0917,5.1K QBK-ND,1
0917,0944-026,1
0917,0944-027,1
0917,0944-004,1
0917,0944-056,1
0917,0944-057,1
0917,0944-082,1
0917,0944-024,1
0917,0944-025,1
0917,0944-102,4
0917,LOR 102,1
0918,CJ1085,1
0918,1352-152,4
0918,DMS3102A-18-,1
0918,6-32 KEP,7
0918,6-32 X 3/4,4
0918,6-32X1/2,4
0918,1251-102,8
0918,Oct-32,4
0918,10-32 SAE,8

答案 6 :(得分:0)

这是一个sed解决方案。这不是最干净的,我确信有更好的音译这两行的方法,但我觉得这很有趣。 (实际上,我会使用Perl解决方案,但是我发布了这个新颖的内容,并希望看到改进。)

sed -e '/,,/{s/,,//; h; d;}' -e 'G' -e 's/\(.*\)\n\(.*\)/\2\1/'