Pergunta

Estou mantendo um script que pode obter sua entrada de várias fontes e funciona nele por linha. Dependendo da fonte real usada, os quebra-linhas podem ser no estilo UNIX, no estilo Windows ou até, para alguma entrada agregada, mista (!).

Ao ler de um arquivo, vai algo assim:

@lines = <IN>;
process(\@lines);

...

sub process {
    @lines = shift;
    foreach my $line (@{$lines}) {
        chomp $line;
        #Handle line by line
    }
}

Portanto, o que eu preciso fazer é substituir o chomp por algo que remove os quebra-linhas no estilo UNIX ou no estilo Windows. Estou inventando muitas maneiras de resolver isso, uma das desvantagens usuais de Perl :)

Qual é a sua opinião sobre a maneira mais interessante de desligar o Linebreaks genéricos? Qual seria o mais eficiente?

Editar: um pequeno esclarecimento - o método 'processo' recebe uma lista de linhas de algum lugar, não lê -se nesses em um arquivo. Cada linha pode ter

  • Sem quebra -linhas à direita
  • Breakbreaks de linhas no estilo Unix
  • Os quebra-linhas no estilo Windows
  • Basta retorno de carruagem (quando os dados originais têm linhas de linha no estilo Windows e são lidas com $/ = ' n')
  • Um conjunto agregado onde as linhas têm estilos diferentes
Foi útil?

Solução

Depois de cavar um pouco através do Perlre Docs um pouco, apresentarei minha melhor sugestão até agora que parece funcionar muito bem. O Perl 5.10 adicionou a classe de caracteres r como um linebreak generalizado:

$line =~ s/\R//g;

É o mesmo que:

(?>\x0D\x0A?|[\x0A-\x0C\x85\x{2028}\x{2029}])

Vou manter essa pergunta aberta um pouco ainda, apenas para ver se há maneiras mais bacanas esperando para serem sugeridas.

Outras dicas

Sempre que passo pela entrada e quero remover ou substituir os caracteres, executo pequenas sub -rotinas como esta.

sub clean {

    my $text = shift;

    $text =~ s/\n//g;
    $text =~ s/\r//g;

    return $text;
}

Pode não ser sofisticado, mas esse método está funcionando impecável para mim há anos.

Leitura Perlport Eu sugeriria algo como

$line =~ s/\015?\012?$//;

Para ser seguro para qualquer plataforma em que você está e qualquer estilo de linha de linha que você possa estar processando, porque o que está em r e n pode diferir através de diferentes sabores perl.

$line =~ s/[\r\n]+//g;

Nota de 2017: o arquivo :: slurp não é recomendado devido a erros de projeto e erros não mantidos. Usar Arquivo :: Slurper ou Path :: Tiny em vez de.

estendendo -se em sua resposta

use File::Slurp ();
my $value = File::Slurp::slurp($filename);
$value =~ s/\R*//g;

Arquivo :: Slurp abstrair as coisas do arquivo IO e apenas retorna uma string para você.

NOTA

  1. Importante notar a adição de /g , sem ele, dada uma sequência de várias linhas, ele apenas substituirá o primeiro caráter ofensivo.

  2. Além disso, a remoção de $, o que é redundante para esse fim, pois queremos desmaiar tudo quebras de linha, não apenas quebras de linha antes de tudo o que se entende por $ neste sistema operacional.

  3. Em uma string multi-line, $ corresponde ao fim do corda e isso seria problemático).

  4. Ponto 3 significa que o ponto 2 é feito com a suposição de que você também gostaria de usar /m Caso contrário, '$' seria basicamente sem sentido para qualquer coisa prática em uma corda com> 1 linhas ou, fazendo processamento de linha única, um sistema operacional que realmente entende $ e consegue encontrar o \R* que prossiga o $

Exemplos

while( my $line = <$foo> ){
      $line =~ $regex;
}

Dada a notação acima, um sistema operacional que não entende os delimitadores de seus arquivos ' n' ou ' r', no cenário padrão com o delimitador padrão do sistema operacional definido para $/ resultará na leitura de todo o seu arquivo como uma string contígua (a menos que sua string tenha os delimitadores de $ OS, onde será delimitada por isso)

Portanto, neste caso, todos esses regex são inúteis:

  • /\R*$// : Só apagará a última sequência de \R no arquivo
  • /\R*// : Só apagará a primeira sequência de \R no arquivo
  • /\012?\015?// : Quando apenas apagará o primeiro 012\015 , \012 , ou \015 seqüência, \015\012 resultará em qualquer \012 ou \015 sendo emitido.

  • /\R*$// : Se por acaso não houver sequências de bytes de ' 015 $ osdelimiter' no arquivo, então então NÃO Os quebra -linhas serão removidos, exceto os próprios do sistema operacional.

Parece que ninguém entende o que estou falando, então aqui está o código de exemplo, isto é testado para NÃO Remova os feeds de linha. Execute -o, você verá que ele deixa os alinhados.

#!/usr/bin/perl 

use strict;
use warnings;

my $fn = 'TestFile.txt';

my $LF = "\012";
my $CR = "\015";

my $UnixNL = $LF;
my $DOSNL  = $CR . $LF;
my $MacNL  = $CR;

sub generate { 
    my $filename = shift;
    my $lineDelimiter = shift;

    open my $fh, '>', $filename;
    for ( 0 .. 10 )
    {
        print $fh "{0}";
        print $fh join "", map { chr( int( rand(26) + 60 ) ) } 0 .. 20;
        print $fh "{1}";
        print $fh $lineDelimiter->();
        print $fh "{2}";
    }
    close $fh;
}

sub parse { 
    my $filename = shift;
    my $osDelimiter = shift;
    my $message = shift;
    print "Parsing $message File $filename : \n";

    local $/ = $osDelimiter;

    open my $fh, '<', $filename;
    while ( my $line = <$fh> )
    {

        $line =~ s/\R*$//;
        print ">|" . $line . "|<";

    }
    print "Done.\n\n";
}


my @all = ( $DOSNL,$MacNL,$UnixNL);
generate 'Windows.txt' , sub { $DOSNL }; 
generate 'Mac.txt' , sub { $MacNL };
generate 'Unix.txt', sub { $UnixNL };
generate 'Mixed.txt', sub {
    return @all[ int(rand(2)) ];
};


for my $os ( ["$MacNL", "On Mac"], ["$DOSNL", "On Windows"], ["$UnixNL", "On Unix"]){
    for ( qw( Windows Mac Unix Mixed ) ){
        parse $_ . ".txt", @{ $os };
    }
}

Para o CLARAMENTE Saída não processada, veja aqui: http://pastebin.com/f2c063d74

Observe que existem certas combinações que, é claro, funcionam, mas provavelmente são as que você mesmo testou.

Observe que nesta saída, todos os resultados devem ser da forma >|$string|<>|$string|< com Sem feeds de linha a ser considerado saída válida.

e $string é da forma geral {0}$data{1}$delimiter{2} Onde em todas as fontes de saída, deve haver:

  1. Nada entre {1} e {2}
  2. |<>| entre {1} e {2}

No seu exemplo, você pode simplesmente ir:

chomp(@lines);

Ou:

$_=join("", @lines);
s/[\r\n]+//g;

Ou:

@lines = split /[\r\n]+/, join("", @lines);

Usando isso diretamente em um arquivo:

perl -e '$_=join("",<>); s/[\r\n]+//g; print' <a.txt |less

perl -e 'chomp(@a=<>);print @a' <a.txt |less

Para estender a resposta de Ted Cambron acima e algo que não foi abordado aqui: se você remover todas as quebras de linha indiscriminadamente de um pedaço de texto inserido, você acabará com parágrafos se correndo sem espaços quando você produzir esse texto posteriormente. Isso é o que eu uso:

sub cleanLines{

    my $text = shift;

    $text =~ s/\r/ /; #replace \r with space
    $text =~ s/\n/ /; #replace \n with space
    $text =~ s/  / /g; #replace double-spaces with single space

    return $text;
}

A última substituição usa o modificador G 'ganancioso', para que continue a encontrar espaços duplos até substituir todos eles. (Substituindo efetivamente qualquer coisa mais esse espaço único)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top