Sauberste Perl-Parser für Makefile-like Fortsetzungszeilen
-
05-07-2019 - |
Frage
Ein Perl-Skript, das ich bin Bedürfnis Schreiben eine Datei zu analysieren, die Fortsetzungszeilen wie ein Makefile hat. d.h. Zeilen, die mit Leerzeichen beginnen, sind Teil der vorhergehenden Zeile.
Ich schrieb den Code unten, aber nicht, wie es das Gefühl ist sehr sauber oder Perl-ish (Heck, es nicht einmal verwenden „Redo“!)
Es gibt viele Grenzfälle: EOF an den ungeraden Plätzen, einzeilig Dateien, Dateien, die mit einer Leerzeile (oder Nicht-Leerzeile oder Weiterleitung), leeren Dateien beginnen oder enden. Alle meine Testfälle (und Code) sind hier: http://whatexit.org/tal/flatten.tar
Können Sie schreiben Reiniger, perl-ish, Code, der alle meine Tests bestanden?
#!/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;
Lösung
Wie wäre es die gesamte Datei in den Speicher schlürfen und mit regulären Ausdrücken zu verarbeiten. Viel mehr ‚Perlish‘. Dies geht Ihre Tests und ist viel kleiner und übersichtlicher:
#!/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;
Andere Tipps
Wenn Sie nichts dagegen nicht die gesamte Datei in den Speicher geladen, dann geht der folgende Code die Tests. Es speichert die Leitungen in einem Array, das Hinzufügen jede Zeile entweder mit der vorhergehenden (Fortsetzung) oder am Ende des Arrays (andere).
#!/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] .= $_; } # yes, continuation, add to last line
else
{ push @out, $_; } # no, add as new line
}
$, = "\n"; # set output field separator
$\ = "\n"; # set output record separator
print @out;