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;
}