【问题标题】:How to substitute and replace number with letter in specific column如何在特定列中用字母替换和替换数字
【发布时间】:2020-05-26 22:22:45
【问题描述】:

所以我想弄清楚如何替换特定列中的数字并将该数字替换为字母。我正在用 Perl 编码


input.txt

10004226549870  
204226549870062001186000000000040008060802  
5032000067470318021604226549870062001186  
603200100001312293000522105000000456000131289  
603200200006545553000522109000004242000654555  
603200300002463923000522090000005571000246392  
603200400002635413000521196000248920000263541  
60320050000175960300052196600000101700017596  
603200600001054853004867190000001003000105485  
603200700001451223000522095000003981000145122  
75030000674703180216000700017222840007  
89999990674703180216000070001722284  
9000013  

替换表

1 -> A  
2 -> B  
3 -> C  
4 -> D  
5 -> E  
6 -> F  
7 -> G  
8 -> H  
9 -> I  
0 -> {  

替换应在以下情况下发生:

Line starts with a "6", for the numbers located in column 17 and column 45.  
Line starts with a "7", for the number located in column 34.  
Line starts with a "8", for the number located in column 35.  

通过上述替换规则,生成的文件(使用当前文件作为 一个例子),会导致:

output.txt

10004226549870  
2042265498700620  
5032000067470318021604226549870062001186  
6032001000013122I3000522105000000456000131289  
6032002000065455E3000522109000004242000654555  
6032003000024639B3000522090000005571000246392  
6032004000026354A3000521196000248920000263541  
6032005000017596{300052196600000101700017596  
6032006000010548E3004867190000001003000105485  
6032007000014512B3000522095000003981000145122  
750300006747031802160007000172228D0007  
8999999067470318021600007000172228D  
9000013  

**我的代码

my $fn = 'input.txt';
my $wr = 'output.txt';

my %repl = (
    1 => "A"
    2 => "B"
    3 => "C"
    4 => "D"
    5 => "E"
    6 => "F"
    7 => "G"
    8 => "H"
    9 => "I"
    0 => "{"
);

open ( my $fh, '<', $fn ) or die "Could not open file '$fn': $!";

open ( my $ww, '>', $wr ) or die "Could not open file '$fn': $!";


my @lines;
while (my $line = <$fh>) {
    chomp $line;
    push @lines, $line;
    if ( $line =~ /^6/) {
       foreach my $key (sort keys %repl){
            substr($line, 17, 1) =~ s/\b$key\b/$repl{$key}/g
            substr($line, 45, 1) =~ s/\b$key\b/$repl{$key}/g

       }
    }

    elsif ($line =~ /^7/) {
       foreach my $key (sort keys %repl){
            substr($line, 34, 1) =~ s/\b$key\b/$repl{$key}/g

       }
    }
    elsif ($line =~ /^8/) {
       foreach my $key (sort keys %repl){
            substr($line, 35, 1) =~ s/\b$key\b/$repl{$key}/g

       }

    }
    else {
         print $ww $_;
    }
}
close $fh;

close $ww;

【问题讨论】:

  • 预期的output.txt 不会在位置 45 处显示以 6 开头的数字的替换...这只是一个提示吗?然后,将{ 作为替换的行(在 17 处)没有 45 个字符,而是 44 个字符——那该怎么办,什么也不做或添加一个字符?

标签: perl


【解决方案1】:

这很好,我能看到的唯一问题是你可能记错了,因为substr 计数从 0 开始。另一件事是每一行都需要写入输出文件,所以丢掉最后的 @ 987654322@,然后写下该行,无论是否更改。

但是,我会提供改进。不需要通过为每个键运行正则表达式来探测字符,而且既浪费又昂贵。还有其他更简单、更高效的方法。

使用substr,当动态决定替换它时,我们需要两次调用

substr $line, 16, 1, $repl{ substr $line, 16, 1 };

这会将位置 17 处长度为 1 的子字符串(从 0 开始计数)替换为位置 17 处长度为 1 的键的值 %repl

但如果字符串比所需位置短,则此行本身可能会出现问题。事实上,将{ 作为替换的行就是这种情况,它只有 44 个字符。考虑到substr 的“特定”行为与边缘情况,最好不要去“修复”而是检查

# for $col == 45 (etc)
if (length $line >= $col) { 
    substr $line, $col-1, 1, $repl{ substr $line, $col-1, 1 };
}

可以在数组中准备这些位置以避免分散的幻数或标量。

另一种方法是使用正则表达式

$line =~ s/.{16}\K(.)/$repl{$1}/;

这匹配 16 个字符,\K 删除这些匹配,因此不需要放回它们,并捕获下一个字符,然后将其替换为散列中的值。现在一个可能不存在的位置(字符串的末尾)不需要特殊处理,因为匹配失败了。

上面这两个可以用来代替foreach循环在%repl键上,你的代码应该可以正常工作,除了需要删除的else分支和刚刚打印的$line .我也希望你有

use warnings;
use strict;

在开头的某个地方,因为在每个程序中强烈推荐这些。

匹配6/7/8 上的一行是可以的,但您可以使用这些数字作为键构建散列,它们的值是执行所需替换的代码引用。如果这些数字经常变化(或者可能更多),那么考虑做类似的事情。


最后,else 分支中的 print $ww $_; 会发出警告,因为一旦在 while 条件($line)中使用了主题化器,那么 $_ 就不是了t 不再提供(未定义)。

但是,这可能是发布的问题,因为它使用$ww,而定义的句柄是$wr,并且还有一些其他拼写错误。

【讨论】:

    【解决方案2】:

    我通常喜欢避免使用正则表达式引擎,除非我匹配一个模式。以下是我可能会做的事情。

    use strict;
    use warnings;
    
    my %table = (
        1 => 'A', 2 => 'B', 3 => 'C', 4 => 'D', 5 => 'E',
        6 => 'F', 7 => 'G', 8 => 'H', 9 => 'I', 0 => '{',
    );
    
    my %subs = (
        6 => [17, 45],
        7 => [34],
        8 => [35],
    );
    
    while (my $line = <DATA>) {
        chomp $line;
        my $cols = $subs{ substr($line, 0, 1) } // [];
        for my $col (@$cols) {
            next if length($line) < $col;
            my $char = substr($line, $col - 1, 1);
            substr($line, $col - 1, 1) = $table{$char};
        }
        print "$line\n";
    }
    
    __DATA__
    10004226549870
    204226549870062001186000000000040008060802
    5032000067470318021604226549870062001186
    603200100001312293000522105000000456000131289
    603200200006545553000522109000004242000654555
    603200300002463923000522090000005571000246392
    603200400002635413000521196000248920000263541
    60320050000175960300052196600000101700017596
    603200600001054853004867190000001003000105485
    603200700001451223000522095000003981000145122
    75030000674703180216000700017222840007
    89999990674703180216000070001722284
    9000013
    

    【讨论】:

      【解决方案3】:

      用字符定义数组@repl,创建散列%match,使用%match散列的键作为输入正则表达式过滤器。

      读取数据,如果行匹配到正则表达式过滤器,则将其传递给进一步处理。正则表达式捕获 匹配 位,我们稍后使用它从 %match 哈希中提取位置。

      将输入行拆分为一个数字数组,并将感兴趣位置的数字替换为@repl数组中的替代值。

      join 组合回线并打印结果。

      use strict;
      use warnings;
      use feature 'say';
      
      my @repl   = split '', '{ABCDEFGHI';
      my %match  = ( 6 => [17,45], 7 => [34], 8 => [35] );
      my $filter = join('|',keys %match);
      my $re = qr/^($filter)/;
      
      while(my $line = <DATA>) {
          chomp $line;
          if( $line =~ /$re/ ) {
              my @nums = split '', $line;
              for my $pos ( @{$match{$1}} ) {
                  my $i = $pos-1;
                  $nums[$i] = $repl[$nums[$i]] if defined $nums[$i];
              }
              $line = join '', @nums;
          }
          say $line;
      }
      
      __DATA__
      10004226549870  
      204226549870062001186000000000040008060802
      5032000067470318021604226549870062001186
      603200100001312293000522105000000456000131289
      603200200006545553000522109000004242000654555
      603200300002463923000522090000005571000246392
      603200400002635413000521196000248920000263541
      60320050000175960300052196600000101700017596
      603200600001054853004867190000001003000105485
      603200700001451223000522095000003981000145122
      75030000674703180216000700017222840007
      89999990674703180216000070001722284
      9000013
      

      输出

      10004226549870
      204226549870062001186000000000040008060802
      5032000067470318021604226549870062001186
      6032001000013122I300052210500000045600013128I
      6032002000065455E300052210900000424200065455E
      6032003000024639B300052209000000557100024639B
      6032004000026354A300052119600024892000026354A
      6032005000017596{300052196600000101700017596
      6032006000010548E300486719000000100300010548E
      6032007000014512B300052209500000398100014512B
      750300006747031802160007000172228D0007
      8999999067470318021600007000172228D
      9000013
      

      【讨论】:

        猜你喜欢
        • 2017-10-20
        • 2018-11-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-24
        • 1970-01-01
        • 2017-11-05
        • 2016-12-22
        相关资源
        最近更新 更多