Domanda

Sto mantenendo uno script che può ottenere il suo ingresso da varie fonti, e lavora su di esso per linea.A seconda dei tipi di fonte utilizzata, gli "a capo" potrebbe essere in stile Unix, Windows-style, o anche, per alcuni aggregati di ingresso, misto(!).

Durante la lettura da un file va qualcosa come questo:

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

...

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

Allora, che cosa ho bisogno di fare è sostituire il chomp con qualcosa che rimuove uno stile Unix o Windows-style gli "a capo".Sto arrivando con troppi modi per risolvere questo problema, uno dei soliti inconvenienti di Perl :)

Qual è la tua opinione su grazioso modo di chomp off generico gli "a capo"?Che cosa sarebbe la più efficiente?

Edit:Un piccolo chiarimento - il metodo di "processo", si ottiene un elenco di righe da qualche parte, non nessecarily leggere da un file.Ogni riga può avere

  • Finali gli "a capo"
  • Stile Unix gli "a capo"
  • In stile Windows, gli "a capo"
  • Appena di Ritorno (quando dati originali in stile Windows, gli "a capo" e viene letto con $/ = ' ')
  • Un aggregato set in cui le linee si hanno diversi stili
È stato utile?

Soluzione

Dopo aver scavato un po 'attraverso la href="http://perldoc.perl.org/perlre.html" rel="noreferrer"> perlre docs un po', io presento il mio suggerimento migliore

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

E 'lo stesso di:

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

Lo terrò questa domanda aprire un po 'ancora, solo per vedere se ci sono altri modi ingegnosi in attesa di essere suggerito.

Altri suggerimenti

Ogni volta che vado attraverso l'ingresso e la voglia di rimuovere o sostituire caratteri corro attraverso piccole subroutine come questo.

sub clean {

    my $text = shift;

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

    return $text;
}

Esso non può essere di fantasia, ma questo metodo è stato di lavoro impeccabile per me per anni.

perlport Io suggerirei una cosa del genere

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

per essere sicuro per qualsiasi piattaforma sei su e qualunque sia lo stile di avanzamento riga si può essere l'elaborazione perché ciò che è in \ r \ n può differire attraverso diversi sapori Perl.

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

Nota a partire dal 2017:File::Slurp non è raccomandato a causa di errori di progettazione e non errori.Utilizzare File::Slurper o Percorso::Tiny invece.

si estende su una tua risposta

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

File::Slurp abstract lontano il File IO di roba e restituisce una stringa per voi.

NOTA

  1. Importante notare l'aggiunta di /g senza di essa, dato un multi-linea stringa, basta sostituire il prima offendere carattere.

  2. Inoltre, la rimozione di $, che è ridondante per questo scopo, come vogliamo striscia tutti le interruzioni di riga, non solo interruzioni di linea prima di qualsiasi cosa si intende per $ su questo OS.

  3. In un multi-linea stringa, $ indica la fine di una string e che sarebbe problematico ).

  4. Punto 3, significa che il punto 2 è fatto con l'assunzione che desideri utilizzare /m altrimenti '$' sarebbe fondamentalmente priva di significato per qualcosa di pratico in una stringa con >1 linee, o, facendo singola linea di trattamento, un sistema operativo che in realtà capisce $ e riesce a trovare il \R* che procedere con la $

Esempi

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

Tenuto conto di quanto sopra notazione, un sistema operativo che non capisce qualunque sia il vostro file ' ' o ' ', delimitatori, nello scenario di default con il sistema operativo predefinito insieme di delimitatori per $/ si tradurrà in lettura il file come un'unica stringa ( a meno che la tua stringa ha il $OS di delimitatori, dove sarà delimitare da che )

Quindi, in questo caso tutti questi regex sono inutili:

  • /\R*$// :Solo cancellare l'ultima sequenza di \R nel file
  • /\R*// :Solo cancellare la prima sequenza di \R nel file
  • /\012?\015?// :Quando sarà cancellare solo il primo 012\015 , \012 o \015 sequenza, \015\012 risulterà \012 o \015 viene emesso.

  • /\R*$// :Se vi capita di essere sequenze di byte di '\015$OSDELIMITER' nel file, quindi NO gli "a capo" saranno rimossi se non per il sistema operativo proprio quelli.

Sembra che nessuno si di cosa sto parlando, ecco un codice di esempio che è testato per NON rimuovere avanzamenti di riga.Esecuzione di esso, vedrete che lascia i caratteri di avanzamento riga in.

#!/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 };
    }
}

Per il CHIARAMENTE Non trasformati uscita, vedi qui: http://pastebin.com/f2c063d74

Nota: ci sono alcune combinazioni che ovviamente funzionano, ma sono probabilmente quelle che hai te naívely testato.

Si noti che in questa uscita, tutti i risultati devono essere della forma >|$string|<>|$string|< con NO AVANZAMENTI DI RIGA per essere considerato valido in uscita.

e $string è la forma generale {0}$data{1}$delimiter{2} dove in tutte le sorgenti in uscita, ci dovrebbe essere :

  1. Niente di tra {1} e {2}
  2. solo |<>| tra {1} e {2}

Nel tuo esempio, si può solo andare:

chomp(@lines);

o

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

o

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

L'utilizzo di questi direttamente su un file:

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

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

Per estendere la risposta di Ted Cambron sopra e qualcosa che non è stato affrontato qui: se si rimuove tutte le interruzioni di riga indiscriminatamente da una porzione di testo inserito, si finirà con i paragrafi correre l'uno nell'altro senza spazi quando si output che il testo dopo. Questo è quello che 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;
}

L'ultima sostituzione utilizza il modificatore g 'avidi' in modo che continua a trovare doppi spazi fino a quando non li sostituisce. (Efficacemente sostituendo qualcosa di più che solo spazio)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top