2009-05-19 77 views
48

Mantengo una secuencia de comandos que puede obtener su entrada de varias fuentes y funciona en línea. Dependiendo de la fuente real utilizada, los saltos de línea pueden ser estilo Unix, estilo Windows o incluso, para alguna entrada agregada, mixta (!).La mejor forma de eliminar los saltos de línea en Perl

Cuando se lee desde 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 
    } 
} 

Por lo tanto, lo que tengo que hacer es reemplazar el mordisco con algo que elimina ya sea al estilo Unix o saltos de línea de estilo de Windows. Se me ocurren demasiadas formas de resolver esto, uno de los inconvenientes habituales de Perl :)

¿Cuál es su opinión sobre la mejor forma de eliminar los saltos de línea genéricos? ¿Cuál sería el más eficiente?

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

  • No hay saltos de línea de fuga
  • saltos de línea al estilo Unix
  • saltos de línea de estilo de Windows
  • Sólo retorno de carro (cuando los datos original tiene saltos de línea al estilo de Windows y se lee con $/= '\ n')
  • Un conjunto agregado donde las líneas tienen diferentes estilos
+0

Si el operador <> reconoce las líneas nuevas, ¿no las matará? – outis

+0

Pero el operador <> no reconoce líneas nuevas correctamente, y además del uso de <> es un caso especial, la entrada no siempre proviene de un archivo. – Christoffer

+1

ya sea ejecute el código que acabo de pegar o lea la salida adjunta que genera. Esperemos ver el punto que estoy tratando de hacer. La condición "mixta" es de lejos la peor. –

Respuesta

82

Después de excavar un poco en el perlre un poco, le presentaré mi mejor sugerencia hasta el momento que parece funcionar 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 esta pregunta abrir un tiempo, sin embargo, sólo para ver si hay más ingenioso formas esperando ser sugeridas.

+1

Le animo a que acepte su propia respuesta si le resulta útil. Es posible que \ R no funcione como se esperaba en algunas plataformas exóticas (por lo que sugerí el método de cableado anterior), pero si no le interesa escribir código portátil, pero solo quiere hacer el trabajo, ya ha terminado. Puede considerar poner primero los archivos de prueba de Kent Fredric a través de su código porque realmente son un buen caso de prueba. – Olfan

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

lectura perlport me gustaría sugerir algo así como

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

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

+1

Errores potenciales: 1) No/g, por lo que no funcionará en cadenas multi-line. 2) $, por lo que solo coincidirá con los delimitadores que aparecen directamente antes del final de la cadena. 3) ordenó \ 015 \ 012, de modo que si tienen \ 012 \ 015 solo se comerá uno de ellos. –

+2

1) +2) Como no sé lo que hay dentro de las líneas, tuve que asumir que puede haber caracteres de nueva línea dentro que no deberían eliminarse (p.registros de la base de datos con columnas de datos linebreaking). Mi intención era hacer coincidir el comportamiento de chomp() lo más cerca posible. 3) He visto que los Mac antiguos usan \ 015 solamente y Windows todavía usa \ 015 \ 012, pero aún no he visto un sistema del mundo real usando \ 012 \ 015, así que sentí que este orden sería seguro. ;) – Olfan

+0

Eche un vistazo a mi respuesta actualizada y lo que emite, hay condiciones que * especialmente * prevalecen en la lectura basada en líneas que realmente no son obvias hasta que lo pruebe. es decir: local $/= "\ 015" # de repente tiene un montón de \ 012 en la salida. –

6

Nota de 2017: File :: Slurp no se recomienda debido a errores de diseño y errores no mantenidos. Use File::Slurper o Path::Tiny en su lugar.

se extiende en su respuesta

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

resúmenes File :: Slurp distancia del archivo IO cosas y simplemente devuelve una cadena para usted.

NOTA

  1. importante tener en cuenta la adición de /g, sin que, dada una cadena de múltiples líneas, un viaje de solo reemplazar el carácter ofensivo primera.

  2. Además, la eliminación de $, que es redundante para este propósito, ya que queremos despojar 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 múltiples líneas, $ coincide con el final de la cadena de y que sería problemático).

  4. El punto 3 significa que el punto 2 se realiza suponiendo que también desea usar /m; de lo contrario, '$' no tendría sentido para nada práctico en una cadena con> 1 línea, o haciendo un procesamiento de línea única , un sistema operativo que en realidad entiende $ y se las arregla para encontrar el \R* que proceden del $

Ejemplos

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

Dada la notación anterior, un sistema operativo que no comprende los delimitadores de sus archivos '\ n' o '\ r', en el escenario predeterminado con el delimitador predeterminado del sistema operativo establecido para $/ dará como resultado la lectura de todo el archivo como una cadena contigua (a menos que su cadena tiene delimitadores de los $ OS en el mismo, donde se delimitará por eso)

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

  • /\R*$//: sólo se borrará la última secuencia de \R en el archivo
  • /\R*//: Solo e rase la primera secuencia de \R en el archivo
  • /\012?\015?//: Cuando sólo se borrará el primero 012\015, \012, o \015 secuencia, \015\012 den lugar a \012 o \015 siendo emitida.

  • /\R*$//: Si sucede que hay no hay secuencias de bytes de '\ 015 $ OSDELIMITER' en el archivo, a continuación, a continuación, se eliminarán NO saltos de línea a excepción de las propias del sistema operativo.

Al parecer, nadie se lo que estoy hablando, asi que aquí hay código de ejemplo, que es a prueba a NO alimenta la línea de eliminación. Ejecútalo, verás que deja los avances de línea.

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

Nota hay ciertas combinaciones que de trabajo del curso, pero probablemente son los que usted mismo ingenuamente a prueba.

Tenga en cuenta que en esta salida, todos los resultados deben tener el formato >|$string|<>|$string|< con SIN ALIMENTACIÓN DE LÍNEA para que se considere salida válida.

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. única |<>| entre {1} y {2}
+0

Si tira * cada * línea nueva antes de trabajar en su contenido, ¿cómo sabe dónde se rompe la línea (por ejemplo, que un salto de línea constituye un nuevo registro)? – Anon

+0

la tarea consiste en eliminar * todo * salto de línea, independientemente del sistema operativo actual –

+0

No, la tarea consiste en eliminar los saltos de línea finales de una lista de cadenas. – Christoffer

11

Siempre Repaso las entradas y quiero eliminar o reemplazar caracteres. Lo ejecuto a través de pequeñas subrutinas como esta. .

sub clean { 

    my $text = shift; 

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

    return $text; 
} 

Puede que no sea elegante, pero este método ha funcionado perfectamente para mí durante años.

+0

Apuesto a que esta solución es probablemente más eficiente que una expresión regular condicional. Buena respuesta. – freeworlder

1

En su ejemplo, 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 
+0

No creo que chomp haga lo mismo que las otras cosas: si tienes un archivo dos en un sistema Unix, quitará \ n el final y dejará \ r * chomp. Esta versión más segura de "chop" elimina cualquier cadena final que corresponda al valor actual de $/(también conocido como $ INPUT_RECORD_SEPARATOR en el módulo "Inglés"). * – msouth

1

Para ampliar la respuesta anterior de Ted Cambron y algo que no se ha tratado aquí: si elimina indiscriminadamente todos los saltos de línea de un fragmento de texto introducido, terminará con párrafos que se unen sin espacios cuando imprima 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 modificador de la g 'codicioso' por lo que sigue encontrando dobles espacios hasta que todos ellos reemplaza. (Sustitución efectiva de más de un espacio)

Cuestiones relacionadas