Perl читать, искать, рассказывать и текстовые файлы. Слишком много читаемых байтов. Слои и обработка новой строки

У меня есть скрипт Perl, который анализирует текстовый файл (может быть UNIX или конец строки Windows), сохраняя смещения файла, когда он находит что-то интересное.

open(my $fh, $filename);
my $groups;
my %hash;
while(<$fh>) {
   if($_ =~ /interesting/ ) {
      $hash{$groups++}{offset} = tell($fh);
   }
}
close $fh;

Затем в сценарии я хочу создать n копий текстового файла, но с дополнительным содержимым в каждой "интересной" области. Для этого я перебираю хэш смещений:

foreach my $group (keys %hash) {
   my $href = $hash{$group};
   my $offset = $href->{offset};

   my $top;
   open( $fh, $file);
   read( $fh, $top, $offset);
   my $bottom = do{local $/; <$fh>};
   close $fh;

   $href->{modified} = $top . "Hello World\n" . $bottom;
}

Проблема в том, что команда чтения читает слишком много байтов. Я подозреваю, что это проблема окончания строки, так как количество байтов (символов?) Совпадает с номером строки. Используя Блокнот ++ tell() Команда возвращает реальное смещение к интересующей точке, но используя это значение смещения в read() возвращает персонажей после точки интереса.

Я пытался добавить binmode($fh) сразу после open() Команда до read(), Это находит правильную позицию в текстовом файле, но затем я получаю (CR + CRLF) вывод, и текстовый файл полон двойных возвратов каретки.

Я играл со слоями:crlf,:bytes, но без улучшений.

Немного застрял!

3 ответа

  • Хеш с непрерывным диапазоном целых чисел в качестве ключей должен быть массивом.

  • Вы храните копию всего файла для каждого случая /interesting/

  • Похоже, что вам нужно сделать, это

    open(my $fh, $filename);
    while (<$fh>) {
      print;
      print "Hello World\n" if /interesting/;
    }
    

От perldoc -f read:

read FILEHANDLE,SCALAR,LENGTH,OFFSET
read FILEHANDLE,SCALAR,LENGTH

Итак, когда вы делаете:

read( $fh, $top, $offset);

ваш $offset на самом деле длина. Решите, сколько символов вам нужно прочитать. read не учитывает окончания строк, он читает указанное количество байтов.

Если вы хотите прочитать строку, то не используйте read, используйте:

seek($fh, $offset, 0);
$top = <$fh>;

Ваш файл заполнен двумя новыми строками, или вы добавляете один с print заявление?

Мой стандартный способ справиться с этим, когда входной файл не слишком большой, - это вставить в файл и нормализовать окончания строк, сохраняя каждую строку как элемент массива. Мне иногда приходится иметь дело с виндой (CR+LF) и UNIX (LF только) и Mac (CR только) окончания строк в одной и той же партии файлов. Один и тот же скрипт должен корректно работать на всех трех платформах.

Я обычно придерживаюсь подхода пояса и брекетов, когда приходится иметь дело с такими вещами. Один способ, который должен работать:

sub read_file_into_array
{
    my $file = shift;
    my ($len, $cnt, $data, @file);

    open my $fh, "<", $file         or die "Can't read $file: $!";
    seek $fh, 0, 2                  or die "Can't seek $file: $!";
    $len = tell $fh;
    seek $fh, 0, 0                  or die "Can't seek $file: $!";

    $cnt = read $fh, $data, $len;
    close $fh;

    $cnt == $len or die "Attempted to read $len bytes; got $cnt";

    $data =~ s/\r\n/\n/g;       # Convert DOS line endings to UNIX
    $data =~ s/\r/\n/g;         # Convert Mac line endings to UNIX

    @file = split /\n/, $data;  # Split on UNIX line endings

    return \@file;
}

Затем сделайте всю свою обработку в строках в @file, Для ваших "интересных" тегов вы должны хранить индекс массива, а не смещение файла. Индекс массива - это, по сути, номер строки в исходном файле, начиная с 0, а не с 1.

Чтобы фактически дополнить файлы, вместо того, чтобы циклически проходить по ключам хеша, почему бы не создать хеш, состоящий из пар строка-номер => вещь-к-добавлению, генерируя расширенный файл следующим образом:

sub generate_augmented_file
{
    my $file   = shift @_;   # array ref
    my $extras = shift @_;   # hash ref of line => extra pairs
    my $text;        

    foreach my $line ( 0 .. scalar( $file ) - 1 )
    {
        $text .= $file->[$line];
        $text .= $extras->{$line} if defined $extras->{$line};
        $text .= "\n";
    }

    return $text;
}
Другие вопросы по тегам