我试图暂时解决一个问题,但没有成功。一开始它看起来像一个微不足道的问题,但我已经堆积了它......
无论如何,我需要解决以下问题。我有一个非常大的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`
一般情况下,我需要一些 regexp ,我可以使用 sed 或 awk (或一些 perl 脚本我可以对输入文件运行)。原始输入文件大约有1-1.5M条记录。此任务应尽快完成(转换最多5分钟)。
由于
答案 0 :(得分: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
位于;
上,join
位于;
,然后打印修改后的记录。
答案 1 :(得分: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
答案 2 :(得分: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
命令用于调用循环,该循环打印构造的字符串,直到替换命令失败然后结束循环。剩余的字符串将被完全删除,并开始下一行。
答案 3 :(得分: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个最后元素并存储在单独的数组中。删除空元素。绕过剩余的元素并根据需要进行打印。