2009-10-09 4 views
2

Есть ли у кого-нибудь решение задачи обработки многострочной строки по одной строке за раз, кроме решения строки-как-файла, показанного ниже?Как я могу избежать Perl :: Критические предупреждения при обработке многострочной строки с помощью дескриптора файла?

my $multiline_string = "line one\nline two\nline three\nline four"; 
my $filehandle; 
open($filehandle, '<', \$multiline_string) 
    or croak("Can't open multi-line string as a filehandle: $!"); 
while (defined (my $single_line = <$filehandle>)) { 
    # do some processing of $single_line here ... 
} 
close($filehandle); 

Причина, по которой я не хочу использовать дескриптор файла, довольно слаба. Test :: Perl :: Критические крики, когда у меня есть более 10 строк исходного кода между моей командой open и моей командой закрытия на любом дескрипторе файла. Я занимаюсь обработкой $ single_line, поэтому на самом деле у меня около 40 строк кода между моим открытым вызовом и моим закрытым звонком, и я не вижу никакого способа довести это до 10.

И я на самом деле не хочу игнорировать тест Perl :: Critic в моей сборке, потому что это действительно достойный тест, который я бы хотел передать всякий раз, когда я открываю фактический файл на диске в своем коде.

+2

Если '$ multiline_string' велико, список, возвращаемый' split', будет еще больше и победит по очереди на обработку '$ multiline_string'. Либо используйте регулярное выражение для соответствия строкам по одному, либо отменяйте работу, которую вы выполняете подпрограмме.Я лично предпочел бы последнее. –

+0

ничего себе. как глупо мне не думать об обходном пути подпрограммы. иногда я просто ничего не думаю. :-) –

ответ

9

Сделайте Кристалла Perl счастливым и сделайте себя еще более счастливым, создав подпрограмму и назовите ее с каждой строкой файла.

use strict; use warnings; 

sub do_something { 
    my ($line) = @_; 
    # do something with $line 
} 

open my $fh, '<', \$multiline_string 
    or die "Cannot open scalar for reading: $!"; 

while(<$fh>) { 
    chomp; 
    do_something($_); 
} 

close $fh; 
+0

Это, безусловно, правильный способ сделать это. Тем не менее, вы должны ** всегда ** проверять, удалось ли открыть, и использовать открытую форму с тремя лепестками с лексическими дескрипторами файлов. –

+1

Или по крайней мере ** всегда ** использование 3-arg open. Вы можете «использовать autodie» (наряду со строгими и предупреждениями), и если вы не хотите беспокоиться о проверке успеха ваших открытий. –

+1

Благодарю редакторов. Я не использовал Perl в течение 4 лет, поэтому ... –

3

насчет:

my $multiline_string = "line one\nline two\nline three\nline four"; 
my @lines = split(/\n/,$multiline_string); 
foreach my $line (@lines) { 
    #do stuff with string 
} 
3

я мог бы быть что-то отсутствую, но вы могли бы сделать:

my @lines = split(/\n/,$multiline_string); 
foreach my $single_line (@lines) { 
    ... 
} 
+0

Не забывайте, что вы можете обрабатывать многострочную строку с помощью регулярных выражений с помощью параметра/m или/s, как описано в perldoc perlre - это может быть проще, чем разбиение на \ n, в зависимости от того, что вы ищете , – Ether

3

Задолго до того, я даже знал, что вы могли бы впихнуть многострочные строки в дескриптор, было split :

foreach my $single_line (split /\n/, $multiline_string) { 
    # process $single_line here 
    # although note that it doesn't end in a newline anymore 
} 

Вставьте отказ от использования литерального и не переносного \n здесь.

0

Вы можете использовать регулярное выражение.

#!/usr/bin/perl 

use strict; 
use warnings; 

my $s = "line one\nline two\nline three\nline four"; 

while ($s =~ m'^(.*)$'gm) { 
    print "'$1'\n"; 
} 

die "Exited loop too early\n" unless pos $s == length $s; 

Или вы могли бы использовать split:

for my $line (split m'\n', $multiline_string){ 

    # ... 

} 
+0

Подход с регулярным выражением является лучшим ИМХО. Вам не нужны '\ G' и'/m'. Используйте: 'while ($ s = ~ /(.+?)\n/g) {'. 'split' является расточительным, потому что это означает сохранение двух копий по существу одних и тех же данных в памяти. –

+1

*, а не там, или вы пропустите пустые строки. А также ? бесполезно. \ n принадлежит в захвате, чтобы больше походить на путь чтения файла. – ysth

+0

И хотя \ G может быть ненужным, я бы сохранил его; когда вы ожидаете, что потребляете всю строку по частям, лучше всего ее применять (с проверкой m/\ G .../gc и pos() после цикла), чтобы вы случайно не перепутали свое регулярное выражение и не потеряли некоторые данные (например, ваш + вместо *). – ysth

-1

Лично я предпочитаю использовать $/ для разделения строк в многострочной строке.

my $multiline_string = "line one\nline two\nline three\nline four"; 
foreach (split($/, $mutliline_string)) { 
    process_file($_); 
} 
sub process_file { 
    my $filename = shift; 
    my $filehandle; 
    open($filehandle, '<', $filename) 
     or croak("Can't open multi-line string as a filehandle: $!"); 
    while (defined (my $single_line = <$filehandle>)) { 
     process_line($single_line); 
    } 
    close($filehandle); 
} 
sub process_line { 
    my $line = shift; 
    ... 
} 
5

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

Многие люди предложили split /\n/. split /^/ больше похож на путь filehandle.

2

Perl::Critic приятно, но когда вы начинаете одерживать некоторые из своих требований, он начинает тратить свое время, а не спасать его. Я просто дайте указатель_на_файл выйти за рамки и не беспокоиться о закрытии:

my $multiline_string = "line one\nline two\nline three\nline four"; 

{ 
    open my($fh), '<', \$multiline_string) 
     or croak("Can't open multi-line string as a filehandle: $!"); 
    while (defined (my $single_line = <$fh>)) { 
     # do some processing of $single_line here ... 
    } 
} 

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