Parser Perl più pulito per linee di continuazione simili a Makefile
-
05-07-2019 - |
Domanda
Uno script perl che sto scrivendo deve analizzare un file che ha linee di continuazione come un Makefile. vale a dire le righe che iniziano con uno spazio bianco fanno parte della riga precedente.
Ho scritto il codice qui sotto ma non mi sembra che sia molto pulito o perl-ish (diamine, non usa nemmeno " redo " ;!)
Esistono molti casi limite: EOF in punti dispari, file a riga singola, file che iniziano o finiscono con una riga vuota (o riga non vuota o linea di continuazione), file vuoti. Tutti i miei casi di test (e codice) sono qui: http://whatexit.org/tal/flatten.tar
Puoi scrivere codice più pulito, perl-ish, che supera tutti i miei test?
#!/usr/bin/perl -w
use strict;
sub process_file_with_continuations {
my $processref = shift @_;
my $nextline;
my $line = <ARGV>;
$line = '' unless defined $line;
chomp $line;
while (defined($nextline = <ARGV>)) {
chomp $nextline;
next if $nextline =~ /^\s*#/; # skip comments
$nextline =~ s/\s+$//g; # remove trailing whitespace
if (eof()) { # Handle EOF
$nextline =~ s/^\s+/ /;
if ($nextline =~ /^\s+/) { # indented line
&$processref($line . $nextline);
}
else {
&$processref($line);
&$processref($nextline) if $nextline ne '';
}
$line = '';
}
elsif ($nextline eq '') { # blank line
&$processref($line);
$line = '';
}
elsif ($nextline =~ /^\s+/) { # indented line
$nextline =~ s/^\s+/ /;
$line .= $nextline;
}
else { # non-indented line
&$processref($line) unless $line eq '';
$line = $nextline;
}
}
&$processref($line) unless $line eq '';
}
sub process_one_line {
my $line = shift @_;
print "$line\n";
}
process_file_with_continuations \&process_one_line;
Soluzione
Che ne dici di slurping l'intero file in memoria ed elaborarlo usando espressioni regolari. Molto più "perlish". Questo supera i tuoi test ed è molto più piccolo e più ordinato:
#!/usr/bin/perl
use strict;
use warnings;
$/ = undef; # we want no input record separator.
my $file = <>; # slurp whole file
$file =~ s/^\n//; # Remove newline at start of file
$file =~ s/\s+\n/\n/g; # Remove trailing whitespace.
$file =~ s/\n\s*#[^\n]+//g; # Remove comments.
$file =~ s/\n\s+/ /g; # Merge continuations
# Done
print $file;
Altri suggerimenti
Se non ti dispiace caricare l'intero file in memoria, il codice seguente supera i test. Memorizza le linee in una matrice, aggiungendo ciascuna riga alla precedente (continuazione) o alla fine della matrice (altra).
#!/usr/bin/perl
use strict;
use warnings;
my @out;
while( <>)
{ chomp;
s{#.*}{}; # suppress comments
next unless( m{\S}); # skip blank lines
if( s{^\s+}{ }) # does the line start with spaces?
{ $out[-1] .= Se non ti dispiace caricare l'intero file in memoria, il codice seguente supera i test.
Memorizza le linee in una matrice, aggiungendo ciascuna riga alla precedente (continuazione) o alla fine della matrice (altra).
<*>; } # yes, continuation, add to last line
else
{ push @out, Se non ti dispiace caricare l'intero file in memoria, il codice seguente supera i test.
Memorizza le linee in una matrice, aggiungendo ciascuna riga alla precedente (continuazione) o alla fine della matrice (altra).
<*>; } # no, add as new line
}
$, = "\n"; # set output field separator
$\ = "\n"; # set output record separator
print @out;