Как заменить и заменить цифру буквой в столбце специфик c - PullRequest
3 голосов
/ 27 мая 2020

Итак, я пытаюсь понять, как заменить числа в определенных c столбцах и заменить это число буквой. Я кодирую 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  

** MyCode

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;

Ответы [ 3 ]

4 голосов
/ 27 мая 2020

Это хорошо, и единственная проблема, которую я вижу, заключается в том, что вы, возможно, неправильно подсчитали, поскольку substr счетчики начинаются с 0. Еще одна вещь заключается в том, что каждая строка должна быть записана в выходной файл, поэтому потеряйте последний else и просто напишите строку, изменена она или нет.

Однако я бы предложил улучшение. Поиск символа путем запуска регулярного выражения для каждого ключа не требуется, это расточительно и дорого. Есть и другие способы, более простые и эффективные.

Используя substr, нам нужны два вызова, когда его замена определяется динамически

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

Это заменяет подстроку длиной 1 на позиция 17 (отсчет начинается с 0) со значением в %repl для ключа, который является подстрокой длины 1 в позиции 17.

Но эта строка сама по себе может иметь проблему, если строка была короче требуемой позиции. Фактически, это относится к строке, которая получает замену { и состоит всего из 44 символов. Учитывая довольно «специфическое c» поведение substr с крайними случаями, лучше не добираться до «исправлений», а проверять

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

Эти позиции могут быть подготовлены в виде массива, чтобы избежать разбросанных магов c числа или скаляры.

Другой способ - использовать регулярное выражение

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

Это соответствует 16 символам, а \K отбрасывает эти совпадения, поэтому их не нужно возвращать, и следующий символ захватывается и заменяется его значением в ha sh. Теперь, возможно, несуществующая позиция (за концом строки) не требует специальной обработки, поскольку совпадение просто не выполняется.

Любой из этих двух вышеупомянутых может использоваться вместо foreach циклов по %repl ключи и ваш код должны работать как есть, за исключением ветки else, которую нужно удалить, а $line просто напечатать. Я также надеюсь, что у вас есть

use warnings;
use strict;

где-то в начале, так как они настоятельно рекомендуются в каждой программе.

Соответствие строки любой 6 / 7 / 8 в порядке, но вместо этого вы можете создать ha sh с этими числами в качестве ключей, а их значениями будут ссылки на код, выполняющие необходимые замены. Если эти числа меняются часто (или их может быть намного больше), подумайте о том, чтобы сделать что-то подобное.


Наконец, print $ww $_; в ветке else будет выводить предупреждения, поскольку если актуализатор используется в условии while ($line), то $_ больше не предоставляется (он не определен).

Однако, вероятно, это вопрос публикации, поскольку он использует $ww, в то время как определенный дескриптор - $wr, и есть несколько других опечаток.

2 голосов
/ 27 мая 2020

Обычно я предпочитаю избегать использования механизма регулярных выражений, если я не сопоставляю шаблон. Вот как я могу это сделать.

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
1 голос
/ 27 мая 2020

Определите массив @repl с символами, создайте ha sh %match, используйте ключи %match ha sh в качестве входного фильтра регулярных выражений.

Считайте данные, и если строка соответствует регулярному выражению фильтр передать его для дальнейшей обработки. Regex захватывает совпадение di git, которое мы используем для извлечения позиций из %match ha sh позже.

Разделите строку ввода на массив цифр и замените di git в интересующая позиция с заменой из массива @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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...