1

我有一个大约有 3,000 行的文本文件。99% 的时间我需要全部 3,000 行。但是,我会定期 grep 出我需要的行并将输出定向到另一个文本文件以供使用。

我这样做的唯一问题是:嵌入在文本文件中的是一个 6 字符的数字字符串,表示行号。为了使用文件,这个区域需要正确重新编号...(我不需要重新排序数据,但是我需要用新的行号替换当前的六个字符。并且必须填充零!不幸的是,整行是一长行数据,没有字段分隔符!

例如,我的前三行可能类似于:

20130918082020ZZ000001RANDOMDATAFOLLOWSAFTERTHISABCDEFGH
20130810112000ZZ000999MORERANDOMDATAFOLLOWSAFTERTHISABCD
20130810112000ZZ000027SILLMORERANDOMDATAFOLLOWSAFTERTHIS

位置 17-22 的六个字符(紧跟在“ZZ”之后),需要根据当前行号重新编号......所以上面需要看起来像:

20130918082020ZZ000001RANDOMDATAFOLLOWSAFTERTHISABCDEFGH
20130810112000ZZ000002MORERANDOMDATAFOLLOWSAFTERTHISABCD
20130810112000ZZ000003SILLMORERANDOMDATAFOLLOWSAFTERTHIS

任何想法将不胜感激!

谢谢,KSL。

4

4 回答 4

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
于 2013-09-18T17:15:44.977 回答
1

如果我要解决这个问题,我将创建一个简单的 python 脚本,通过像 grep 一样过滤并使用 python 脚本内部的内部计数器来读取这些行。

作为简单的提示,您可以读取字符串中的每一行并使用 variablename[17:22] 访问它们(17:22 是您尝试使用的字符串的位置)。

现在,python 中的字符串中有一个方法可以进行替换,只需用您创建的计数器替换值即可。

我希望这有帮助。

于 2013-09-18T16:38:11.297 回答
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]}'
于 2013-09-18T19:56:41.417 回答
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" ($.)';
  }

...

还是会更好;使用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';

...
于 2013-09-19T06:18:21.593 回答