Como posso manter apenas as cinco primeiras linhas em um escalar Perl?
Pergunta
De qualquer tipo de escalar, que Regex eu poderia usar para corresponder às cinco primeiras linhas e descartar o resto?
Solução
Solicitação estranha, mas isso deve fazer isso:
#!/usr/bin/perl
use strict;
use warnings;
my $s = join '', map { "$_\n" } 1 .. 9;
my ($first) = $s =~ /^((?:.*\n){0,5})/;
my ($last) = $s =~ /((?:.*\n){0,5})$/;
print "first:\n${first}last:\n$last";
Uma solução mais comum seria algo assim:
#!/usr/bn/perl
use strict;
use warnings;
#fake a file for the example
my $s = join '', map { "$_\n" } 1 .. 9;
open my $fh, "<", \$s
or die "could not open in memory file: $!";
my @first;
while (my $line = <$fh>) {
push @first, $line;
last if $. == 5;
}
#rewind the file just in case the file has fewer than 10 lines
seek $fh, 0, 0;
my @last;
while (my $line = <$fh>) {
push @last, $line;
#remove the earliest line if we have to many
shift @last if @last == 6;
}
print "first:\n", @first, "last:\n", @last;
Outras dicas
Por que você simplesmente não usa head
por isso?
Você não precisa de um regex. Basta abrir um arquivo de arquivo em uma referência ao escalar e fazer as mesmas coisas que você faria em qualquer outro tipo de arquivo de arquivo:
my $scalar = ...;
open my($fh), "<", \ $scalar or die "Could not open filehandle: $!";
foreach ( 1 .. 5 )
{
push @lines, scalar <$fh>;
}
close $fh;
$scalar = join '', @lines;
my ($first_five) = $s =~ /\A((?:.*\n){5})/;
my ($last_five) = $s =~ /((?:.*\n){5})\z/;
Como Brian diz, você pode usar head
ou tail
com bastante facilidade para qualquer problema (primeiras 5 linhas ou 5 linhas).
Mas agora estou me perguntando se eu entendo sua pergunta corretamente. Quando você diz "para qualquer tipo de escalar", você quer dizer que (por qualquer motivo) o arquivo já está em um escalar?
Caso contrário, acho que a melhor solução não é regex. Usar $.
e leia o arquivo normalmente ou para trás. Para ler para trás, você pode tentar File::ReadBackwards
ou File::Bidirectional
.
As pessoas estão perdendo algumas bandeiras de chave:
/(?m)((?:^.*\n?){1,5})/
Sem o sinalizador de várias linhas, ele só vai olhar para a primeira linha. Também fazendo o \n
Opcional, podemos levar os cinco primeiros linhas, independentemente de uma nova linha no final do quinto.
Por que não usar apenas o Split com um limite, ele foi projetado para esse fim:
my @lines = (split /\n/, $scalar, 6)[0..4];
Se você quer isso de volta como um único escalar com cinco linhas, junte -a de volta:
my $scalar = join('\n', @lines) . "\n";
use strict;
my $line; #Store line currently being read
my $count=$ARGV[1]; # How many lines to read as passed from command line
my @last; #Array to store last count lines
my $index; #Index of the line being stored
#Open the file to read as supplied from command line
open (FILE,$ARGV[0]);
while ($line=<FILE>)
{
$index=$.%$count; # would help me in filter just $count records of the file
$last[$index]=$line; #store this value
}
close (FILE);
#Output the stored lines
for (my $i=$index+1;$i<$count;$i++)
{
print ("$last[$i]");
}
for (my $i=$0;$i<=$index;$i++)
{
print ("$last[$i]");
}