-2

我试图解决一个问题一段时间但没有成功。一开始它看起来像一个微不足道的问题,但我已经堆积了它......

无论如何,我需要解决以下问题。我有非常大的 CSV 文件,其中包含以下格式的行:

NUMBER(9);NUMBER(1);NUMBER(9-10);NUMBER(2);NUMBER(1);...;NUMBER(2);NUMBER(1);STRING;DATE(DD.MM.YYYY);NUMBER(1351)

例如:

517755369;1;0001303717;48;1;63;8;50;2;51;6;53;7;55;3;57;4;59;5;;;;;CALL;07.12.2012;1351

在第一个树字段之后的每一行中,我有 1 到 10 对NUMBER(2);NUMBER(1),然后是另外三个字段STRING;DATE(DD.MM.YYYY);NUMBER(1351)

我需要将该文件转换为具有以下结构的文件:

517755369;1;0001303717;48;1;CALL;07.12.2012;1351
517755369;1;0001303717;63;8;CALL;07.12.2012;1351
517755369;1;0001303717;50;2;CALL;07.12.2012;1351
517755369;1;0001303717;51;6;CALL;07.12.2012;1351
517755369;1;0001303717;53;7;CALL;07.12.2012;1351
517755369;1;0001303717;55;3;CALL;07.12.2012;1351
517755369;1;0001303717;57;4;CALL;07.12.2012;1351
517755369;1;0001303717;59;5;CALL;07.12.2012;1351`

因此,输入文件中的每一行都应转换为与原始行NUMBER(2);NUMBER(1)对数一样多的行。

这是输入文件的示例:

517760344;2;000601301061;31;1;;;;;;;;;;;;;;;;;;;CALL;07.12.2012;1351
518855369;1;000601303717;48;1;63;8;50;2;51;6;53;7;55;3;57;4;59;5;;;;;CALL;07.12.2012;1351
519775067;1;000601300771;4;2;6;3;19;1;;;;;;;;;;;;;;;CALL;07.12.2012;1351
617773407;1;000603252922;13;1;17;2;27;3;;;;;;;;;;;;;;;CALL;07.12.2012;1351
717764779;1;000601304021;31;1;;;;;;;;;;;;;;;;;;;CALL;07.12.2012;1351`

一般来说,我需要一些可以与sedawk一起使用的正则表达式(或者一些我可以针对输入文件运行的perl脚本)。原始输入文件大约有 1-150 万条记录。此任务应尽快完成(最多 5 分钟进行转换)。

谢谢

4

4 回答 4

2

也许以下内容会有所帮助:

use strict;
use warnings;

while (<>) {
    chomp;
    print +( join ';', ( split /;/ )[ 0 .. 4, -3 .. -1 ] ) . "\n";

}

输出数据:

517760344;2;000601301061;31;1;CALL;07.12.2012;1351
518855369;1;000601303717;48;1;CALL;07.12.2012;1351
519775067;1;000601300771;4;2;CALL;07.12.2012;1351
617773407;1;000603252922;13;1;CALL;07.12.2012;1351
717764779;1;000601304021;31;1;CALL;07.12.2012;1351

用法:perl file.csv >out.csv

您似乎需要前五个字段和后三个字段。上面split的 s on ;, re joins ,;然后打印修改后的记录。

于 2013-01-25T19:08:45.367 回答
2

来自@Kenosis 的想法,但对规格的不同解释:

use strict;
use warnings;

while (<DATA>) {
    chomp;
    my @fields = split /;/;
    my $f = 3;
    while ($fields[$f]) {
      print join( ';', @fields[0 .. 2, $f, $f + 1, -3 .. -1]), "\n";
      $f += 2;
    }
}

__DATA__
517760344;2;000601301061;31;1;;;;;;;;;;;;;;;;;;;CALL;07.12.2012;1351
518855369;1;000601303717;48;1;63;8;50;2;51;6;53;7;55;3;57;4;59;5;;;;;CALL;07.12.2012;1351
519775067;1;000601300771;4;2;6;3;19;1;;;;;;;;;;;;;;;CALL;07.12.2012;1351
617773407;1;000603252922;13;1;17;2;27;3;;;;;;;;;;;;;;;CALL;07.12.2012;1351
717764779;1;000601304021;31;1;;;;;;;;;;;;;;;;;;;CALL;07.12.2012;1351

输出:

perl 14528210.pl
517760344;2;000601301061;31;1;CALL;07.12.2012;1351
518855369;1;000601303717;48;1;CALL;07.12.2012;1351
518855369;1;000601303717;63;8;CALL;07.12.2012;1351
518855369;1;000601303717;50;2;CALL;07.12.2012;1351
518855369;1;000601303717;51;6;CALL;07.12.2012;1351
518855369;1;000601303717;53;7;CALL;07.12.2012;1351
518855369;1;000601303717;55;3;CALL;07.12.2012;1351
518855369;1;000601303717;57;4;CALL;07.12.2012;1351
518855369;1;000601303717;59;5;CALL;07.12.2012;1351
519775067;1;000601300771;4;2;CALL;07.12.2012;1351
519775067;1;000601300771;6;3;CALL;07.12.2012;1351
519775067;1;000601300771;19;1;CALL;07.12.2012;1351
617773407;1;000603252922;13;1;CALL;07.12.2012;1351
617773407;1;000603252922;17;2;CALL;07.12.2012;1351
617773407;1;000603252922;27;3;CALL;07.12.2012;1351
717764779;1;000601304021;31;1;CALL;07.12.2012;1351
于 2013-01-25T19:37:51.547 回答
1

这可能对您有用(GNU sed):

sed -r 's/^(([^;]*;){3})(([0-9]+;){2})(([0-9]*;)*)(([^;]*;?){3})$/\1\3\7\n\1\5\7/;Ta;P;:a;D' file
  • s/^(([^;]*;){3})(([0-9]+;){2})(([0-9]*;)*)(([^;]*;?){3})$/\1\3\7\n\1\5\7/这个替换命令构造了两个字符串。第一个是预期的字符串,后跟换行符,第二个是原始字符串减去第一对数字。所以^(([^;]*;){3})表示前三个字段,(([0-9]+;){2})第一对数字,(([0-9]*;)*)剩余的数字对和(([^;]*;?){3})$最后三个字段。
  • Ta如果替换命令失败跳转到标签a
  • P打印到模式空间中的第一个换行符。
  • :a标签a
  • D删除直到并包括第一个换行符并开始下一个循环。在模式空间为空之前,不要读入另一行。

因此,本质上,s/.../.../D命令用于调用循环,该循环打印构造的字符串,直到替换命令失败,然后结束循环。剩余的字符串被完全删除并开始下一行。

于 2013-01-25T21:43:14.557 回答
1

这是使用Text::CSV模块的单行形式的解决方案。

perl -MText::CSV -lwe '$c = Text::CSV->new({
    sep_char=>';',
    eol=>$/
});                  
while($r = $c->getline(*STDIN)) { 
    my @a = splice @$r,0,3;            # remove 3 first elements
    my @c = splice @$r,-3;             # remove 3 last elements
    @$r = grep $_ ne '', @$r;          # remove empty elements
    while(@$r) {                       # while array is not empty
        $c->print(*STDOUT, [@a, splice(@$r,0,2),@c]);    # print all elements
    } }"

输出:

517755369;1;0001303717;48;1;CALL;07.12.2012;1351
517755369;1;0001303717;63;8;CALL;07.12.2012;1351
517755369;1;0001303717;50;2;CALL;07.12.2012;1351
517755369;1;0001303717;51;6;CALL;07.12.2012;1351
517755369;1;0001303717;53;7;CALL;07.12.2012;1351
517755369;1;0001303717;55;3;CALL;07.12.2012;1351
517755369;1;0001303717;57;4;CALL;07.12.2012;1351
517755369;1;0001303717;59;5;CALL;07.12.2012;1351

所以基本上,就像我在评论中所说,删除前 3 个元素和最后 3 个元素并存储在单独的数组中。删除空元素。循环剩余元素并根据需要打印。

于 2013-01-25T22:52:19.703 回答