2012-08-28 10 views
5

Problema: Tengo datos (principalmente en formato CSV) producidos en Windows y * nix, y procesados ​​principalmente en * nix. Windows usa CRLF para terminaciones de línea y Unix usa LF. Para cualquier archivo en particular, no sé si tiene ventanas o finales de línea * nix. Hasta ahora, he estado escribiendo algo como esto para manejar la diferencia:¿Detecta correctamente los finales de línea de un archivo en Perl?

while (<$fh>){ 
    tr/\r\n//d; 
    my @fields = split /,/, $_; 
    # ... 
} 

En * nix la parte \ n es equivalente a masticar, y, además, se deshace de \ r (CR) si es un ventanas archivo producido.

Pero ahora quiero enviar un mensaje de texto :: CSV_XS b/c Estoy empezando a obtener archivos de datos más extraños con datos entrecomillados, potencialmente con saltos de línea incrustados, etc. Para que este módulo lea dichos archivos, Text :: CSV_XS :: getline() requiere que especifique los caracteres de fin de línea. (No puedo leer cada línea como arriba, tr/\ n \ r // d, y las analiza con Text :: CSV b/c que no manejaría correctamente los saltos de línea integrados). ¿Cómo puedo correctamente detectar si un archivo arbitrario usa ventanas o terminaciones de línea de estilo * nix, por lo que puedo decir a Text :: CSV_XS :: eol() cómo chomp()?

No pude encontrar un módulo en CPAN que simplemente detecta terminaciones de línea. No quiero convertir primero todos mis archivos de datos a través de dos2unix, b/c los archivos son enormes (cientos de gigabytes), y pasar más de 10 minutos para cada archivo para hacer frente a algo tan simple parece una tontería. Pensé en escribir una función que lea los primeros cientos de bytes de un archivo y cuente los LF frente a CRLF, pero me niego a creer que esto no tiene una mejor solución.

¿Algún ayuda?

Nota: todos los archivos tienen terminaciones de línea de Windows o terminaciones * nix, es decir, no están mezclados en un solo archivo.

Respuesta

9

Puede abrir el archivo usando :crlfPerlIO layer y luego indicar Text::CSV_XS para usar \n como el carácter de final de línea. Esto correlacionará silenciosamente cualquier par de CR/LF con alimentaciones de una sola línea, pero presumiblemente es lo que quiere.

use Text::CSV_XS; 
my $csv = Text::CSV_XS->new({ binary => 1, eol => "\n" }); 

open($fh, '<:crlf', 'data.csv') or die $!; 

while (my $row = $csv->getline($fh)) { 
    # do something with $row 
} 
+0

Gracias, nunca antes había tenido conocimiento de PerlIO. Esto es exactamente lo que necesitaba. – user1481

3

Lea en la primera línea de cada archivo, observe su último pero único carácter. Si es \r, el archivo proviene de Windows, si no, es * nix. Luego seek para comenzar y comenzar el procesamiento.

Si es posible que un archivo tenga terminaciones de línea mixtas (por ejemplo, tipo diferente para las líneas nuevas incorporadas), solo puede adivinar.

1

En teoría, las terminaciones de línea no se pueden determinar con fiabilidad: ¿Es este archivo una sola línea con terminaciones de línea DOS con incrustado \n s o es un grupo de líneas con algunos caracteres \r al final de algunas líneas?

foo\n 
ba\r\n 

frente

foo\nba\r\n 

Si el análisis estadístico no es una opción porque es demasiado impreciso y caro (se necesita tiempo para analizar este tipo de archivos de gran tamaño), usted tiene que realmente saben cuál es la codificación es.

Lo mejor sería especificar el formato de archivo exacto si tiene control sobre las aplicaciones de producción o utilizar algún tipo de metadato para realizar un seguimiento de la plataforma en la que se produjeron los datos.

En Perl, el carácter \n representa Depende de la localización: \n/\012 en máquinas * nix, \r/\015 en el viejo Mac y la secuencia \r\n/\015\012 sobre el Dos-descendientes también conocido como Windows. Entonces, para hacer un procesamiento confiable, debe usar los valores octales.

5

Desde Perl 5.10, puede utilizar esto para comprobar los finales de línea en general,

s/\R//g; 

Se debe trabajar en todos los casos, tanto * nix y Windows.

1

Puede usar la variable PERLIO. Esto tiene la ventaja de no tener que modificar el código fuente de sus scripts según la plataforma.

Si usted está tratando con archivos de texto DOS, establezca la variable de entorno PERLIO-:unix:crlf:

$ PERLIO=:unix:crlf my-script.pl dos-text-file.txt 

Si eres principalmente se trata de archivos de texto DOS (por ejemplo, en Cygwin), se puede poner esto en su .bashrc:

export PERLIO=:unix:crlf 

(. Creo que ese valor debe ser el valor predeterminado para PERLIO en Cygwin, pero al parecer no lo es)

Cuestiones relacionadas