Perl: как вставить строку, если найдено совпадение?

#perl

#perl

Вопрос:

Содержимое файла:

 456 name1 name2 345 678
423 name3 name4 345 678
435 name5 name6 345 678
866 name7 name8 345 678
 

Содержимое файла после вставки строки :

 456 name1 name2 345 678
423 name3 name4 345 678
Name3_Found
435 name5 name6 345 678
866 name7 name8 345 678
 

Содержимое файла, которое я получаю:

 456 name1 name2 345 678
423 name3 name4 345 678
Name3_Found
me6 345 678
866 name7 name8 345 678
 

код:

 open (temp2, " <$file") or die "Could not open file";
my $point;
   
while(my $lin =<temp2>) {
    $point = tell(temp2);
    if ( $lin =~ /name5/ ){
        seek(temp2,$point-2,0);
        chk: 
        while (my $lin =<temp2>){
            my @rw = split" ",$lin;
            if ($rw[1] eq "name3"  ) {
                say temp2 "Name3_Found";
            } elsif( $lin =~ /name5/){
                last chk;
            }
        }
    }
}
close temp2;
 

Кто-нибудь знает, почему он удаляет другие данные строки? и как это исправить?

Комментарии:

1. Ну, вы перезаписываете существующие байты ( 435 name5 na ) с добавлением ( Name3_Foundn ). Файл представляет собой последовательность байтов, и между ними нельзя «добавить» или что-то подобное. Вам нужно скопировать ее и добавить эту новую строку туда, где она находится в процессе.

2. @zdim, я не копировал.

Ответ №1:

Здесь вы смешиваете несколько вещей. При записи в дескриптор файла в смешанном режиме ( < ) данные не вставляются. Вы перезаписываете то, что уже есть.

Если у вас есть записи фиксированной длины и вы хотите перейти к определенной записи, используйте seek . Чтобы заменить запись, вы должны записать точно такое же количество октетов. Если вы записываете слишком мало октетов, вы оставляете позади октеты из предыдущей записи, а если вы записываете слишком много, вы перезаписываете следующую запись.

Если вы хотите делать вещи, ориентированные на строки, не используйте смешанный режим open . Откройте файл, чтобы прочитать его, и выведите в новый файл. perlfaq5 описывает это в разделе «Как мне изменить, удалить или вставить строку в файл или добавить в начало файла?».

Я думаю, что в 4-м издании Learning Perl все еще была глава об этом материале.

Ответ №2:

Только потому, что вы спросили: «… и как это исправить?»

 #!/usr/bin/env perl

use Data::Dumper;
use Hash::Util qw(lock_keys);
use strict;
use warnings;

my $file=q{TheData.txt};

# using three argument open and dying with error
open (my $temp, " <",$file)
    or die "Could not open '$file'! $!";
my %ptr_h=(READER=>tell $temp,WRITER=>tell $temp);
# The only keys we're going to use are READER and WRITER --- anything will error out
lock_keys(%ptr_h);

# Our buffer
my @buffer_a;
while(my $line=<$temp>) {
    # The next read will start here
    $ptr_h{READER}=tell($temp);

    # Make any changes to the line - pushing them onto the buffer
    if ($line =~ m{name3}) { # only if name3
        # pushing them onto the buffer
        push @buffer_a,$line.qq{Name3_Foundn};
        }
    else { # otherwise just save it!
        push @buffer_a,$line;
        }
    # end of changes

    # Do we have anything to write and the room to write it
    while (@buffer_a amp;amp; length($buffer_a[0]) <= $ptr_h{READER}-$ptr_h{WRITER}) {
        # We do have room to write the first record of the buffer
        seek $temp,$ptr_h{WRITER},0;
        # Enough room to write $Buffer_a[0] so write it ...
        print $temp shift(@buffer_a);
        $ptr_h{WRITER}=tell($temp);
        };
    }
# Go back to where we were reading
continue {
    seek $temp,$ptr_h{READER},0;
    };
# We've read and processed all of the file
# ... now write out what remains of the buffer
seek $temp,$ptr_h{WRITER},0;
while (@buffer_a) {
    print $temp shift(@buffer_a);
    };
# ... and truncate the file
truncate $temp,tell($temp);
# ... and close
close($temp)
    or die "Can't close '$file'! $!";
 

ACHTUNG: если что-то должно произойти во время вашего обновления, вы можете оказаться «в ручье без весла».