En el trabajo, parece que no pasa una semana sin alguna connipción, calamidad o catástrofe relacionada con la codificación. El problema suele derivar de los programadores que piensan que pueden procesar de forma fiable un archivo de "texto" sin especificar la codificación. Pero no puedes.
Por lo tanto, se decidió prohibir a partir de ahora que los archivos tengan nombres que terminen en *.txt
o *.text
. La idea es que esas extensiones inducen a error al programador casual a una aburrida complacencia con respecto a las codificaciones, y esto conduce a un manejo inadecuado. Casi sería mejor no tener ninguna extensión, porque al menos entonces sabes que no sabes lo que tienes.
Sin embargo, no vamos a llegar tan lejos. En su lugar, se esperará que utilice un nombre de archivo que termine en la codificación. Así que para archivos de texto, por ejemplo, estos serían algo así como README.ascii
, README.latin1
, README.utf8
, etc.
Para los archivos que exigen una extensión en particular, si se puede especificar la codificación dentro del archivo en sí, como en Perl o Python, entonces debe hacerlo. Para archivos como la fuente de Java donde no existe tal facilidad interna al archivo, colocará la codificación antes de la extensión, como SomeClass-utf8.java
.
Para la salida, se prefiere mucho UTF-8 .
Pero para la entrada, tenemos que averiguar cómo tratar con los miles de archivos en nuestra base de código denominada *.txt
. Queremos cambiar el nombre de todos ellos para que se ajusten a nuestro nuevo estándar. Pero no podemos mirarlos a todos. Entonces necesitamos una biblioteca o programa que realmente funcione.
Estos están en ASCII, ISO-8859-1, UTF-8, Microsoft CP1252 o Apple MacRoman. Aunque sabemos que podemos decir si algo es ASCII, y tenemos un buen cambio de saber si algo es probablemente UTF-8, estamos perplejos acerca de las codificaciones de 8 bits. Debido a que estamos funcionando en un entorno Unix mixto (Solaris, Linux, Darwin) y la mayoría de los equipos de escritorio son Mac, tenemos bastantes archivos MacRoman molestos. Y estos especialmente son un problema.
Desde hace algún tiempo, he estado buscando una forma de determinar mediante programación cuál de
- ASCII
- ISO-8859-1
- CP1252
- MacRoman
- UTF-8
hay un archivo y no he encontrado un programa o biblioteca que pueda distinguir de manera confiable entre esas tres codificaciones diferentes de 8 bits. Probablemente solo tengamos más de mil archivos MacRoman, por lo que cualquier detector de juego de caracteres que usemos debe ser capaz de detectarlos. Nada de lo que he visto puede manejar el truco. Tenía grandes esperanzas para la biblioteca de detectores de juegos de caracteres ICU , pero no puede manejar MacRoman. También miré módulos para hacer el mismo tipo de cosas tanto en Perl como en Python, pero una y otra vez siempre es la misma historia: no hay soporte para detectar MacRoman.
Por lo tanto, lo que estoy buscando es una biblioteca o programa existente que determine de manera confiable en cuál de esas cinco codificaciones se encuentra un archivo, y preferiblemente más que eso. En particular, tiene que distinguir entre las tres codificaciones de 3 bits que he citado, especialmente MacRoman . Los archivos tienen más del 99% de texto en inglés; hay algunos en otros idiomas, pero no muchos.
Si se trata de código de biblioteca, nuestra preferencia de idioma es que esté en Perl, C, Java o Python, y en ese orden. Si es solo un programa, entonces realmente no nos importa en qué idioma esté siempre que venga en código fuente completo, se ejecute en Unix y no tenga ningún tipo de carga.
¿Alguien más ha tenido este problema de un trillón de archivos de texto heredados codificados aleatoriamente? Si es así, ¿cómo intentó resolverlo y qué éxito tuvo? Este es el aspecto más importante de mi pregunta, pero también me interesa si cree que animar a los programadores a nombrar (o cambiar el nombre) a sus archivos con la codificación real en la que se encuentran nos ayudará a evitar el problema en el futuro. Alguien ha tratado de hacer cumplir esta sobre una base institucional, y si es así, fue que el éxito o no, y por qué?
Y sí, comprendo perfectamente por qué no se puede garantizar una respuesta definitiva dada la naturaleza del problema. Este es especialmente el caso de archivos pequeños, en los que no tiene suficientes datos para continuar. Afortunadamente, nuestros archivos rara vez son pequeños. Aparte del README
archivo aleatorio , la mayoría están en el rango de tamaño de 50k a 250k, y muchos son más grandes. Se garantiza que todo lo que tenga un tamaño superior a unos pocos K estará en inglés.
El dominio del problema es la minería de textos biomédicos, por lo que a veces tratamos con corpora extensos y extremadamente grandes, como todos los repositorios de acceso abierto de PubMedCentral. Un archivo bastante grande es el BioThesaurus 6.0, de 5,7 gigabytes. Este archivo es especialmente molesto porque casi todo es UTF-8. Sin embargo, algunos tontos fueron y pegaron algunas líneas que están en una codificación de 8 bits — Microsoft CP1252, creo. Toma bastante tiempo antes de que te tropieces con eso. :(
Respuestas:
Primero, los casos fáciles:
ASCII
Si sus datos no contienen bytes por encima de 0x7F, entonces es ASCII. (O una codificación ISO646 de 7 bits, pero son muy obsoletas).
UTF-8
Si sus datos se validan como UTF-8, entonces puede asumir con seguridad que es UTF-8. Debido a las estrictas reglas de validación de UTF-8, los falsos positivos son extremadamente raros.
ISO-8859-1 frente a windows-1252
La única diferencia entre estas dos codificaciones es que ISO-8859-1 tiene los caracteres de control C1 donde windows-1252 tiene los caracteres imprimibles € ‚ƒ„… † ‡ ˆ ‰ Š ‹ŒŽ ''“ ”• –—˜ ™ š› œžŸ. He visto muchos archivos que usan comillas o guiones, pero ninguno que usa caracteres de control C1. Así que ni siquiera se moleste con ellos, o ISO-8859-1, simplemente detecte windows-1252 en su lugar.
Eso ahora te deja con una sola pregunta.
¿Cómo distingue MacRoman de cp1252?
Esto es mucho más complicado.
Caracteres indefinidos
Los bytes 0x81, 0x8D, 0x8F, 0x90, 0x9D no se utilizan en windows-1252. Si ocurren, asuma que los datos son MacRoman.
Caracteres idénticos
Los bytes 0xA2 (¢), 0xA3 (£), 0xA9 (©), 0xB1 (±), 0xB5 (µ) resultan ser los mismos en ambas codificaciones. Si estos son los únicos bytes que no son ASCII, entonces no importa si elige MacRoman o cp1252.
Enfoque estadístico
Cuente las frecuencias de caracteres (¡NO de bytes!) En los datos que sabe que son UTF-8. Determina los personajes más frecuentes. Luego, use estos datos para determinar si los caracteres cp1252 o MacRoman son más comunes.
Por ejemplo, en una búsqueda que acabo de realizar en 100 artículos aleatorios de Wikipedia en inglés, los caracteres no ASCII más comunes son
·•–é°®’èö—
. Basado en este hecho,Cuente los bytes que sugieren cp1252 y los bytes que sugieren MacRoman, y elija el que sea mayor.
fuente
Mozilla nsUniversalDetector (enlaces de Perl: Encode :: Detect / Encode :: Detect :: Detector ) está probado en un millón de veces.
fuente
x-mac-cyrillic
es compatible,x-mac-hebrew
se discute extensamente en los comentarios,x-mac-anything-else
no se menciona.Mi intento de tal heurística (asumiendo que ha descartado ASCII y UTF-8):
Nota al margen:
¡¡No hagas esto!!
El compilador de Java espera que los nombres de los archivos coincidan con los nombres de las clases, por lo que cambiar el nombre de los archivos hará que el código fuente no se pueda compilar. Lo correcto sería adivinar la codificación y luego usar la
native2ascii
herramienta para convertir todos los caracteres no ASCII en secuencias de escape Unicode .fuente
*.text
archivos."Perl, C, Java o Python, y en ese orden": actitud interesante :-)
"Tenemos un buen cambio al saber si algo es probablemente UTF-8": En realidad, existe la posibilidad de que un archivo que contenga texto significativo codificado en algún otro juego de caracteres que use bytes de conjunto de bits altos se decodifique con éxito ya que UTF-8 es extremadamente pequeño.
Estrategias UTF-8 (en el idioma menos preferido):
Una vez que haya decidido que no es ASCII ni UTF-8:
Los detectores de juegos de caracteres de origen Mozilla que conozco no son compatibles con MacRoman y, en cualquier caso, no hacen un buen trabajo con los juegos de caracteres de 8 bits, especialmente con el inglés porque AFAICT dependen de verificar si la decodificación tiene sentido en el dado. idioma, ignorando los caracteres de puntuación y basado en una amplia selección de documentos en ese idioma.
Como han señalado otros, realmente solo tiene los caracteres de puntuación de conjunto de bits altos disponibles para distinguir entre cp1252 y macroman. Sugeriría entrenar un modelo tipo Mozilla en sus propios documentos, no en Shakespeare o Hansard o la Biblia KJV, y tener en cuenta los 256 bytes. Supongo que sus archivos no tienen marcado (HTML, XML, etc.), eso distorsionaría las probabilidades de algo impactante.
Ha mencionado archivos que son en su mayoría UTF-8 pero no se pueden decodificar. También deberías sospechar mucho de:
(1) archivos que supuestamente están codificados en ISO-8859-1 pero que contienen "caracteres de control" en el rango 0x80 a 0x9F inclusive ... esto es tan frecuente que el borrador del estándar HTML5 dice decodificar TODOS flujos HTML declarados como ISO-8859 -1 usando cp1252.
(2) archivos que decodifican OK como UTF-8 pero el Unicode resultante contiene "caracteres de control" en el rango U + 0080 a U + 009F inclusive ... esto puede resultar de la transcodificación de cp1252 / cp850 (¡visto que sucede!) / Etc archivos de "ISO-8859-1" a UTF-8.
Antecedentes: tengo un proyecto húmedo de domingo por la tarde para crear un detector de conjuntos de caracteres basado en Python que esté orientado a archivos (en lugar de orientado a la web) y que funcione bien con conjuntos de caracteres de 8 bits, incluidos
legacy ** n
los como cp850 y cp437. Aún no está cerca del horario de máxima audiencia. Me interesan los archivos de entrenamiento; ¿Están sus archivos ISO-8859-1 / cp1252 / MacRoman igualmente "libres de cargas" como espera que sea la solución de código de cualquier persona?fuente
Como ha descubierto, no existe una manera perfecta de resolver este problema, porque sin el conocimiento implícito sobre qué codificación utiliza un archivo, todas las codificaciones de 8 bits son exactamente iguales: una colección de bytes. Todos los bytes son válidos para todas las codificaciones de 8 bits.
Lo mejor que puede esperar es algún tipo de algoritmo que analice los bytes y, en función de las probabilidades de que un determinado byte se utilice en un determinado idioma con una determinada codificación, adivinará qué codificación utilizan los archivos. Pero eso tiene que saber qué idioma usa el archivo, y se vuelve completamente inútil cuando tiene archivos con codificaciones mixtas.
Por el lado positivo, si sabe que el texto de un archivo está escrito en inglés, entonces es poco probable que note alguna diferencia con la codificación que decida usar para ese archivo, ya que las diferencias entre todas las codificaciones mencionadas están localizadas en las partes de las codificaciones que especifican caracteres que no se utilizan normalmente en el idioma inglés. Es posible que tenga algunos problemas cuando el texto utiliza un formato especial o versiones especiales de puntuación (CP1252 tiene varias versiones de los caracteres de las comillas, por ejemplo), pero en lo esencial del texto, probablemente no habrá problemas.
fuente
Si puede detectar todas las codificaciones EXCEPTO para macroman, entonces sería lógico suponer que las que no se pueden descifrar están en macroman. En otras palabras, simplemente haga una lista de archivos que no se pueden procesar y manipúlelos como si fueran macroman.
Otra forma de ordenar estos archivos sería crear un programa basado en servidor que permita a los usuarios decidir qué codificación no está distorsionada. Por supuesto, sería dentro de la empresa, pero con 100 empleados haciendo unos pocos cada día, tendrá miles de archivos terminados en poco tiempo.
Finalmente, ¿no sería mejor simplemente convertir todos los archivos existentes a un formato único y requerir que los archivos nuevos estén en ese formato?
fuente
Actualmente estoy escribiendo un programa que traduce archivos a XML. Tiene que detectar automáticamente el tipo de cada archivo, que es un superconjunto del problema de determinar la codificación de un archivo de texto. Para determinar la codificación, estoy usando un enfoque bayesiano. Es decir, mi código de clasificación calcula una probabilidad (probabilidad) de que un archivo de texto tenga una codificación particular para todas las codificaciones que comprende. A continuación, el programa selecciona el decodificador más probable. El enfoque bayesiano funciona así para cada codificación.
Resulta que el teorema de Bayes se vuelve muy fácil de hacer si en lugar de calcular probabilidades, calcula el contenido de la información , que es el logaritmo de las probabilidades :
info = log(p / (1.0 - p))
.Tendrá que calcular la probabilidad inicial a priori y las correlaciones examinando un corpus de archivos que ha clasificado manualmente.
fuente