2011-06-04 9 views
20

Mi programa Perl toma algo de texto de un archivo de disco como entrada, lo envuelve en algún XML, luego lo envía a STDOUT. La entrada es nominalmente UTF-8, pero a veces se ha insertado basura. Necesito desinfectar la salida de modo que no se emitan octetos UTF-8 inválidos, de lo contrario, el consumidor descendente (Sphinx) explotará.¿Cómo desinfeto el UTF-8 no válido en Perl?

Por lo menos me gustaría saber si los datos son inválidos así que puedo evitar pasarlos; idealmente podría eliminar solo los bytes ofensivos. Sin embargo, habilitar todos los fatalismos que puedo encontrar no me lleva allí con Perl 5.12 (FWIW, use v5.12; use warnings qw(FATAL utf8); está en vigencia).

Estoy teniendo problemas específicamente con la secuencia "\xFE\xBF\xBE". Si creo un archivo que contiene solo estos tres bytes (perl -e 'print "\xEF\xBF\xBE"' > bad.txt), tratando de leer el archivo con el modo :encoding(UTF-8) se produce un error con utf8 "\xFFFE" does not map to Unicode, pero solo debajo de 5.14.0. 5.12.3 y anterior son perfectamente buenas lecturas y más tarde escribir esa secuencia. No estoy seguro de dónde está obteniendo el \xFFFE (BOM inversa inverso), pero al menos tener una queja es consistente con Sphinx.

Desafortunadamente, decode_utf8("\xEF\xBF\xBE", 1) no causa errores en 5.12 o 5.14. Preferiría un método de detección que no requiriera una capa de E/S codificada, ya que eso me dejará un mensaje de error y no habrá manera de desinfectar los octetos sin formato.

Estoy seguro de que hay más secuencias que necesito abordar, pero solo manejar esto sería un comienzo. Entonces mis preguntas son: ¿puedo detectar de manera confiable este tipo de datos problemáticos con un perl antes de 5.14? ¿Qué rutina de sustitución generalmente puede desinfectar casi-UTF-8 en estricto UTF-8?

Respuesta

21

Debe leer el UTF-8 vs. utf8 vs. UTF8 section de los documentos Encode.

En resumen, Perl tiene dos codificaciones UTF-8 diferentes. Su codificación nativa se llama utf8, y básicamente permite cualquier punto de código, independientemente de lo que diga el estándar Unicode sobre ese punto de código.

La otra codificación se llama utf-8 (a.k.a. utf-8-strict). Esto permite solo los puntos de código que se enumeran como legales para el intercambio por el estándar Unicode.

"\xEF\xBF\xBE", cuando se interpreta como UTF-8, decodifica al punto de código U+FFFE. Pero eso no es legal para el intercambio de acuerdo con Unicode, por lo que los programas que son estrictos sobre tales cosas se quejan.

En lugar de utilizar decode_utf8 (que utiliza el laxa utf8 codificación), utilice decode con la codificación utf-8. Y lea la sección Handling Malformed Data para ver las diferentes maneras en que puede manejar o quejarse de problemas.

Actualización: Parece que algunas versiones de Perl no se quejan de U + FFFE, incluso cuando se utiliza la codificación utf-8-strict. Esto parece ser un error. Puede que tenga que crear una lista de puntos de código de los que se queja Sphinx y filtrarlos manualmente (por ejemplo, con tr).

+3

Ejemplos de código: http: // stackoverflow.com/questions/3735721/checklist-for-going-the-unicode-way-with-perl/3736787 # 3736787 – daxim

+1

Gracias, eso es muy útil. Dado que los caracteres Unicode permitidos en XML están bien definidos, 'tr [\ x {9} \ x {A} \ x {D} \ x {20} - \ x {D7FF} \ x {E000} - \ x {FFFD} \ x {10000} - \ x {10FFFF}] [] cd' parece que funcionará, al menos en 5.14. Una sugerencia completamente diferente que encontré no se basa en un nuevo perl en absoluto: 'iconv -c --desde UTF-8 - hasta UTF-8'. –

3

usted tiene una cadena UTF-8 UTF-8 que contiene algunos inválida ...

Esto lo sustituye por una "mala char por defecto.

use Encode qw(decode encode); 

my $octets = decode('UTF-8', $malformed_utf8, Encode::FB_DEFAULT); 

my $good_utf8 = encode('UTF-8', $octets,   Encode::FB_CROAK); 
Cuestiones relacionadas