用行号替换一串字符

时间:2013-09-18 16:29:18

标签: perl sed awk printf

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

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

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

20130918082020ZZ000001RANDOMDATAFOLLOWSAFTERTHISABCDEFGH
20130810112000ZZ000999MORERANDOMDATAFOLLOWSAFTERTHISABCD
20130810112000ZZ000027SILLMORERANDOMDATAFOLLOWSAFTERTHIS

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

20130918082020ZZ000001RANDOMDATAFOLLOWSAFTERTHISABCDEFGH
20130810112000ZZ000002MORERANDOMDATAFOLLOWSAFTERTHISABCD
20130810112000ZZ000003SILLMORERANDOMDATAFOLLOWSAFTERTHIS

任何想法都将不胜感激!

谢谢, KSL。

4 个答案:

答案 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';

...