我有一个大约有3,000行的文本文件。 99%的时间我需要全部3,000行。但是,我会定期查看我需要的行并将输出定向到另一个要使用的文本文件。
我这样做的唯一问题是:文本文件中嵌入的是一个6个字符的数字字符串,表示行号。为了使用该文件,该区域需要正确重新编号...(我不需要重新排序数据,但我需要用新的行号替换当前的六个字符。它必须填充用零!不幸的是,整行是一行长的数据,没有字段分隔符!
例如,我的前三行可能类似于:
20130918082020ZZ000001RANDOMDATAFOLLOWSAFTERTHISABCDEFGH
20130810112000ZZ000999MORERANDOMDATAFOLLOWSAFTERTHISABCD
20130810112000ZZ000027SILLMORERANDOMDATAFOLLOWSAFTERTHIS
位置17-22(紧跟“ZZ”之后)的六个字符需要根据当前行号重新编号...所以上面需要看起来像:
20130918082020ZZ000001RANDOMDATAFOLLOWSAFTERTHISABCDEFGH
20130810112000ZZ000002MORERANDOMDATAFOLLOWSAFTERTHISABCD
20130810112000ZZ000003SILLMORERANDOMDATAFOLLOWSAFTERTHIS
任何想法都将不胜感激!
谢谢, KSL。
答案 0 :(得分:3)
这是我提出的Perl解决方案。它假定编号在ZZ
序列之后总是6位数。
在convert.pl中:
use strict;
use warnings;
my $i = 1; # or the value you want to start numbering
while (<STDIN>) {
my $replace = sprintf("%06d", $i++);
$_ =~ s/ZZ\d{6}/ZZ$replace/g;
print $_;
}
在data.dat中:
20130918082020ZZ000001RANDOMDATAFOLLOWSAFTERTHISABCDEFGH
20130810112000ZZ000999MORERANDOMDATAFOLLOWSAFTERTHISABCD
20130810112000ZZ000027SILLMORERANDOMDATAFOLLOWSAFTERTHIS
运行:
cat data.dat | perl convert.pl
输出
20130918082020ZZ000001RANDOMDATAFOLLOWSAFTERTHISABCDEFGH
20130810112000ZZ000002MORERANDOMDATAFOLLOWSAFTERTHISABCD
20130810112000ZZ000003SILLMORERANDOMDATAFOLLOWSAFTERTHIS
答案 1 :(得分:1)
如果我要解决这个问题,我会创建一个简单的python脚本,通过grep过滤并使用python脚本内部的内部计数器来读取这些行。
作为简单的提示,您可以读取字符串中的每一行并使用variablename [17:22]访问它们(17:22是您尝试使用的字符串的位置)。
现在,python中的字符串中有一个方法可以替换,只需用你创建的计数器替换值。
我希望这会有所帮助。
答案 2 :(得分:1)
要在awk中执行此操作:
awk '{print substr($0,1,16) sprintf("%06d", NR) substr($0,23)}'
或
gawk 'match($0,/^(.*ZZ)[0-9]{6}(.*)/,a) {print a[1] sprintf("%06d",NR) a[2]}'
答案 3 :(得分:0)
这正是unpack
有用的东西。
#!/usr/bin/env perl
use v5.10.0;
use strict;
use warnings;
while( my $line = <> ){
chomp $line;
my @elem = unpack 'A16 A6 A*', $line;
$elem[1] = sprintf '%06d', $.;
# $. is the line number for the last used file handle
say @elem;
}
实际上看一下这些线条,看起来前14个字符中都存有日期信息
假设在某些时候您可能出于某种原因需要解析这些行,您可以使用以下内容作为如何使用unpack
拆分行的示例。
#!/usr/bin/env perl
use v5.10.0; # say()
use strict;
use warnings;
use DateTime;
my @date_elem = qw'
year month day
hour minute second
';
my @elem_names = ( @date_elem, qw'
ZZ
line_number
random_data
');
while( my $line = <> ){
chomp $line;
my %data;
@data{ @elem_names } = unpack 'A4 (A2)6 A6 A*', $line;
# choose either this:
$data{line_number} = sprintf '%06d', $.;
say @data{@elem_names};
# or this:
$data{line_number} = $.;
printf '%04d' . ('%02d'x5) . "%2s%06d%s\n", @data{ @elem_names };
# the choice will affect the contents of %data
# this just shows the contents of %data
for( @elem_names ){
printf qq'%12s: "%s"\n', $_, $data{$_};
}
# you can create a DateTime object with the date elements
my $dt = DateTime->new(
(map{ $_, $data{$_} } @date_elem),
time_zone => 'floating',
);
say $dt;
print "\n";
}
虽然使用正则表达式会更好,但是你可能会抛出伪造的数据。
use v5.14; # /a modifier
...
my $rdate = join '', map{"(\\d{$_})"} 4, (2)x5;
my $rx = qr'$rdate (ZZ) (\d{6}) (.*)'xa;
while( my $line = <> ){
chomp $line;
my %data;
unless( @data{ @elem_names } = $line =~ $rx ){
die qq'unable to parse line "$line" ($.)';
}
...
还是会更好;使用named capture groups中添加的5.10。
...
my $rx = qr'
(?<year> \d{4} ) (?<month> \d{2} ) (?<day> \d{2} )
(?<hour> \d{2} ) (?<minute> \d{2} ) (?<second> \d{2} )
ZZ
(?<line_number> \d{6} )
(?<random_data> .* )
'xa;
while( my $line = <> ){
chomp $line;
unless( $line =~ $rx ){
die qq'unable to parse line "$line" ($.)';
}
my %data = %+;
# for compatibility with previous examples
$data{ZZ} = 'ZZ';
...