2013-05-29 7 views
0

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

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

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

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

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

Бит застрял!

+1

Почему бы вам не «искать» позиции, открывая файл только один раз? – choroba

+1

'CRLF' не символ. Текстовый файл Windows имеет строки, заканчивающиеся на * два символа 'CR' и' LF' или возврат каретки и перевод строки. Трудно поверить, что ваш файл * «полный двойных возвратов каретки» *. – Borodin

+0

Это то, что я получаю, когда добавляю binmode(). Как строка байтов:/******************************************* ****/CRCRLF/* Linker Script */CRCRLF – Chris

ответ

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

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

  • Это звучит как то, что вам нужно сделать, это

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

Я попытался опустить мой пример, чтобы избежать путаницы. Да, мне нужно создать копию всего файла «n» раз с добавлением/интересных/разделов. Текстовый файл на самом деле является сценарием компоновщика, а/интересные/части - это разделы .input. В конце скрипт Perl отправится многопоточным вызывающим LD с каждым из этих модифицированных скриптов компоновщика. Моя проблема - это команда read(), возвращающая слишком много байтов. – Chris

+0

Я бы подумал, что это была работа для ['Tie :: File'] (https://metacpan.org/module/Tie::File), но не должно быть никаких проблем с использованием' binmode $ fh'. Да, вы получите 'LF' или' CR' + 'LF' в конце строк, в зависимости от того, откуда был получен файл, но значение из' seek' будет правильным. – Borodin

0

От 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 утверждением?

0

Мой стандартный способ справиться с этим, когда входной файл не является ginormous, заключается в том, чтобы вставить файл и нормализовать окончания строки, сохраняя каждую строку в качестве элемента массива. Мне иногда приходится иметь дело с Windows (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; 
}