Pregunta

Estoy manteniendo una secuencia de comandos que puede conseguir su entrada de varias fuentes, y trabaja por línea.Dependiendo de la fuente utilizada, puedes incluir varias líneas podría ser de estilo Unix, Windows-estilo o incluso, para algunos agregados de entrada, mixto(!).

Cuando la lectura de un archivo que es algo como esto:

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

...

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

Entonces, lo que necesitamos hacer es reemplazar el chomp con algo que remueve bien de estilo Unix o Windows estilo puedes incluir varias líneas.Voy con demasiado muchas maneras de resolver este, uno de los habituales inconvenientes de Perl :)

¿Cuál es tu opinión sobre las mejores manera de chomp de descuento genérico puedes incluir varias líneas?¿Cuál sería el más eficiente?

Editar:Una pequeña aclaración - el método de 'proceso' se obtiene una lista de líneas de algún lugar, no nessecarily leer desde un archivo.Cada línea puede tener

  • No trailing puedes incluir varias líneas
  • De estilo Unix puedes incluir varias líneas
  • Estilo de Windows puedes incluir varias líneas
  • Sólo el Retorno de Carro (cuando los datos originales ha de estilo de Windows puedes incluir varias líneas y se lee con $/ = ' ')
  • Un agregado establece que las líneas tienen diferentes estilos
¿Fue útil?

Solución

Después de cavar un poco a través de la perlre docs un poco, voy a presentar mi mejor sugerencia tan lejos que parece que funciona bastante bien. Perl 5.10 añade la clase de caracteres \ R como un salto de línea generalizada:

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

Es lo mismo que:

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

Voy a mantener abierta la cuestión de un tiempo, sin embargo, sólo para ver si hay más formas ingeniosas que esperan ser sugerido.

Otros consejos

Siempre que vaya a través de la entrada y quiero quitar o reemplazar los caracteres corro a través de pequeñas subrutinas como ésta.

sub clean {

    my $text = shift;

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

    return $text;
}

Puede que no sea lujoso, pero este método ha sido impecable trabajo para mí durante años.

perlport me gustaría sugerir algo así como

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

para ser seguro para cualquier plataforma que está en cualquier estilo y avance de línea que puede ser procesado porque lo que está en \ r \ n puede variar a través de diferentes sabores Perl.

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

Nota a partir de 2017:Archivo::Slurp, no es recomendable debido a errores de diseño y mantenido errores.Uso Archivo::Slurper o Ruta De Acceso::Pequeño en su lugar.

se extiende en su respuesta

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

Archivo::Slurp, resúmenes de distancia el Archivo IO cosas y simplemente devuelve una cadena para usted.

NOTA

  1. Importante tenga en cuenta la adición de /g , sin que, dada una cadena de varias líneas, basta con reemplazar el primero ofender carácter.

  2. También, la eliminación de $, que es redundante para este propósito, como queremos tira todos los saltos de línea, no sólo los saltos de línea antes de lo que se entiende por $ en este sistema operativo.

  3. En una cadena de varias líneas, $ coincide con el final de la cadena y que sería problemático ).

  4. Punto 3 significa que el punto 2 es el que hizo con la suposición de que también le desea utilizar /m de lo contrario, ' $ ' sería básicamente de sentido para nada práctico en una cadena con >1 líneas, o bien, haciendo una sola línea de procesamiento, un sistema operativo que realmente entiende $ y se las arregla para encontrar el \R* que proceda a la $

Ejemplos

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

Dada la anterior notación, un sistema operativo que no entiende lo que sus archivos ' ' o ' ' delimitadores, en el escenario de cesación de pagos con el sistema operativo del delimitador predeterminado establecido para $/ resultará en la lectura de su archivo entero como uno contiguos de la cadena ( a menos que la cadena tiene el $OS del delimitadores, donde se delimitan por que )

Así que en este caso todas estas expresiones son inútiles:

  • /\R*$// :Sólo borrar la última secuencia de \R en el archivo
  • /\R*// :Sólo borrar la primera secuencia de \R en el archivo
  • /\012?\015?// :Cuando sólo borrar la primera 012\015 , \012 o \015 la secuencia, \015\012 dará como resultado \012 o \015 siendo emitido.

  • /\R*$// :Si es que hay no hay secuencias de bytes de '\015$OSDELIMITER' en el archivo, a continuación, NO puedes incluir varias líneas serán eliminados, excepto para el sistema operativo de la propia queridos.

Parece que nadie se de lo que estoy hablando, así que aquí está el código de ejemplo, que es probado a NO quitar saltos de línea.Ejecutar, vas a ver que las hojas de los saltos de línea en.

#!/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 el CLARAMENTE Sin procesar de salida, ver aquí: http://pastebin.com/f2c063d74

Tenga en cuenta que hay ciertas combinaciones que de trabajo de curso, pero son probablemente los que usted mismo naívely probado.

Tenga en cuenta que en esta salida, todos los resultados deben ser de la forma >|$string|<>|$string|< con NO HAY SALTOS DE LÍNEA para ser considerado válido de salida.

y $string es de la forma general {0}$data{1}$delimiter{2} donde en todas las fuentes de salida, debe ser :

  1. Nada entre {1} y {2}
  2. sólo |<>| entre {1} y {2}

En el ejemplo, se puede ir:

chomp(@lines);

O:

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

O:

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

El uso de estos directamente en un archivo:

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

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

Para extender la respuesta de Ted Cambron arriba y algo que no ha sido abordado aquí: Si se quita toda la línea se rompe indiscriminadamente desde un trozo de texto introducido, el resultado final será con los párrafos funcionamiento en sí sin espacios cuando emita ese texto más tarde. Esto es lo que yo 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;
}

La última sustitución utiliza el modificador g 'codicioso' por lo que sigue encontrando dobles espacios hasta que todos ellos reemplaza. (Efectivamente sustituyendo algo más que solo espacio)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top