regular - La mejor forma de eliminar los saltos de línea en Perl
regex r (7)
Estoy manteniendo un script 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 (!).
Al leer de un archivo, dice algo como esto:
@lines = <IN>;
process(/@lines);
...
sub process {
@lines = shift;
foreach my $line (@{$lines}) {
chomp $line;
#Handle line by line
}
}
Entonces, lo que tengo que hacer es reemplazar el chomp con algo que elimine los saltos de línea estilo Unix o 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 lugar, no se lee de forma similar desde un archivo . Cada línea podría tener
- Sin saltos de línea al final
- Saltos de línea estilo Unix
- Saltos de línea al estilo de Windows
- Just Carriage-Return (cuando los datos originales tienen saltos de línea al estilo de Windows y se leen con $ / = ''/ n'')
- Un conjunto agregado donde las líneas tienen diferentes estilos
Cada vez que reviso 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.
Después de excavar un poco en el perlre docs un poco, voy a presentar mi mejor sugerencia hasta ahora que parece funcionar bastante bien. Perl 5.10 agregó la clase de caracteres / R como un salto de línea generalizado:
$line =~ s//R//g;
Es lo mismo que:
(?>/x0D/x0A?|[/x0A-/x0C/x85/x{2028}/x{2029}])
Mantendré esta pregunta abierta por un tiempo, solo para ver si hay formas más ingeniosas esperando ser sugeridas.
En su ejemplo, puede ir:
chomp(@lines);
O:
$_=join("", @lines);
s/[/r/n]+//g;
O:
@lines = split /[/r/n]+/, join("", @lines);
Utilizándolos 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 ampliar la respuesta anterior de Ted Cambron y algo que no se ha abordado aquí: si elimina indiscriminadamente todos los saltos de línea de un fragmento de texto introducido, terminará con párrafos que se unirán sin espacios cuando imprima ese texto más adelante. Esto es lo que 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 usa el modificador g ''codicioso'' para que continúe encontrando espacios dobles hasta que los reemplace a todos. (Sustituyendo efectivamente más de ese espacio)
Nota de 2017: File :: Slurp no se recomienda debido a errores de diseño y errores no mantenidos. Use File::Slurper o Path::Tiny lugar.
extendiendo en su respuesta
use File::Slurp ();
my $value = File::Slurp::slurp($filename);
$value =~ s//R*//g;
File :: Slurp abstrae el material File IO y simplemente devuelve una cadena para usted.
NOTA
Es importante tener en cuenta la adición de
/g
, sin ella, dada una cadena de varias líneas, solo reemplazará el primer carácter ofensivo.Además, la eliminación de
$
, que es redundante para este propósito, ya que queremos quitar todos los saltos de línea, no solo los saltos de línea antes de lo que significa$
en este sistema operativo.En una cadena de varias líneas,
$
coincide con el final de la cadena y eso sería problemático).El punto 3 significa que el punto 2 está hecho con la suposición de que también desearía usar
/m
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 logra encontrar el/R*
que proceden$
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 tenga los delimitadores de $ OS, donde lo delimitará)
Entonces, en este caso, todas estas expresiones regulares son inútiles:
-
//R*$//
: Borrará solo la última secuencia de/R
en el archivo -
//R*//
: Borrará solo la primera secuencia de/R
en el archivo //012?/015?//
: Cuando solo borre la primera secuencia012/015
,/012
o/015
,/015/012
dará como resultado la/012
o/015
.//R*$//
: Si no hay secuencias de bytes de ''/ 015 $ OSDELIMITER'' en el archivo, entonces NO se eliminarán los saltos de línea, excepto los del propio sistema operativo.
Parece que nadie obtiene de lo que estoy hablando, así que aquí está el código de ejemplo, que está probado para NO eliminar alimentaciones de línea. 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 CLEARLY sin procesar, consulte aquí: http://pastebin.com/f2c063d74
Tenga en cuenta que existen ciertas combinaciones que, por supuesto, funcionan, pero que probablemente sean las que usted mismo probó.
Tenga en cuenta que en esta salida, todos los resultados deben ser de la forma >|$string|<>|$string|<
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 haber:
- Nada entre
{1}
y{2}
- solo
|<>|
entre{1}
y{2}
$line =~ s/[/r/n]+//g;