Saltar al contenido

Forzar codificación de US-ASCII a UTF-8 (iconv)

Solución:

ASCII es un subconjunto de UTF-8, por lo que todos los archivos ASCII ya están codificados en UTF-8. Los bytes en el archivo ASCII y los bytes que resultarían de “codificarlo en UTF-8” serían exactamente los mismos bytes. No hay diferencia entre ellos, por lo que no es necesario hacer nada.

Parece que su problema es que los archivos no son en realidad ASCII. Debe determinar qué codificación están usando y transcodificarlos correctamente.

Respuesta corta

  • file solo adivina la codificación del archivo y puede estar equivocado (especialmente en los casos en que los caracteres especiales solo aparecen tarde en archivos grandes).
  • puedes usar hexdump para mirar bytes de texto que no sea ASCII de 7 bits y comparar con tablas de códigos para codificaciones comunes (ISO 8859- *, UTF-8) para decidir por sí mismo cuál es la codificación.
  • iconv utilizará cualquier codificación de entrada / salida que especifique independientemente del contenido del archivo. Si especifica la codificación de entrada incorrecta, la salida se distorsionará.
  • incluso después de correr iconv, file no puede informar ningún cambio debido a la forma limitada en que file intenta adivinar la codificación. Para ver un ejemplo específico, vea mi respuesta larga.
  • ASCII de 7 bits (también conocido como ASCII de EE. UU.) Es idéntico a nivel de bytes a UTF-8 y las extensiones ASCII de 8 bits (ISO 8859- *). Entonces, si su archivo solo tiene caracteres de 7 bits, puede llamarlo UTF-8, ISO 8859- * o US ASCII porque a nivel de bytes todos son idénticos. Solo tiene sentido hablar sobre UTF-8 y otras codificaciones (en este contexto) una vez que su archivo tenga caracteres fuera del rango ASCII de 7 bits.

Respuesta larga

Me encontré con esto hoy y encontré tu pregunta. Quizás pueda agregar un poco más de información para ayudar a otras personas que se encuentren con este problema.

ASCII

Primero, el término ASCII está sobrecargado y eso genera confusión.

ASCII de 7 bits solo incluye 128 caracteres (00-7F o 0-127 en decimal). ASCII de 7 bits también se denomina a veces US-ASCII.

ASCII

UTF-8

La codificación UTF-8 utiliza la misma codificación que ASCII de 7 bits para sus primeros 128 caracteres. Por lo tanto, un archivo de texto que solo contiene caracteres de ese rango de los primeros 128 caracteres será idéntico a nivel de bytes, ya sea que esté codificado con UTF-8 o ASCII de 7 bits.

Diseño de página de códigos

ISO 8859- * y otras extensiones ASCII

El término ASCII extendido (o alto ASCII) se refiere a codificaciones de caracteres de ocho bits o más grandes que incluyen los caracteres ASCII estándar de siete bits, más caracteres adicionales.

ASCII extendido

ISO 8859-1 (también conocido como “ISO Latin 1”) es un estándar de extensión ASCII de 8 bits específico que cubre la mayoría de los caracteres para Europa Occidental. Existen otras normas ISO para idiomas de Europa del Este y cirílicos. ISO 8859-1 incluye caracteres como Ö, é, ñ y ß para alemán y español.

“Extensión” significa que ISO 8859-1 incluye el estándar ASCII de 7 bits y le agrega caracteres utilizando el octavo bit. Entonces, para los primeros 128 caracteres, es equivalente a un nivel de bytes a los archivos codificados en ASCII y UTF-8. Sin embargo, cuando comienza a trabajar con caracteres más allá de los primeros 128, ya no es equivalente a UTF-8 a nivel de bytes, y debe realizar una conversión si desea que su archivo “ASCII extendido” esté codificado en UTF-8.

ISO 8859 y adaptaciones patentadas

Detectando codificación con file

Una lección que aprendí hoy es que no podemos confiar file para dar siempre una interpretación correcta de la codificación de caracteres de un archivo.

archivo (comando)

El comando solo dice cómo se ve el archivo, no qué es (en el caso de que el archivo vea el contenido). Es fácil engañar al programa poniendo un número mágico en un archivo cuyo contenido no coincide. Por lo tanto, el comando no se puede utilizar como herramienta de seguridad más que en situaciones específicas.

file busca números mágicos en el archivo que indiquen el tipo, pero estos pueden ser incorrectos, no hay garantía de corrección. file también intenta adivinar la codificación de caracteres mirando los bytes en el archivo. Básicamente file tiene una serie de pruebas que le ayudan a adivinar el tipo de archivo y la codificación.

Mi archivo es un archivo CSV de gran tamaño. file informa que este archivo está codificado en ASCII de EE. UU., que es INCORRECTO.

$ ls -lh
total 850832
-rw-r--r--  1 mattp  staff   415M Mar 14 16:38 source-file
$ file -b --mime-type source-file
text/plain
$ file -b --mime-encoding source-file
us-ascii

Mi archivo tiene diéresis (es decir, Ö). El primer ascii que no es de 7 bits no aparece hasta que se encuentran más de 100k líneas en el archivo. Sospecho que esta es la razón file no se da cuenta de que la codificación del archivo no es US-ASCII.

$ pcregrep -no '[^x00-x7F]' source-file | head -n1
102321:

Estoy en una Mac, así que usando PCRE grep. Con GNU grep podría usar el -P opción. Alternativamente, en una Mac, se podría instalar coreutils (a través de Homebrew u otro) para obtener GNU grep.

No he profundizado en el código fuente de file, y la página de manual no analiza la detección de codificación de texto en detalle, pero supongo file no mira el archivo completo antes de adivinar la codificación.

Cualquiera que sea la codificación de mi archivo, estos caracteres que no son ASCII de 7 bits rompen cosas. Mi archivo CSV alemán es ;-separado y extraer una sola columna no funciona.

$ cut -d";" -f1 source-file > tmp
cut: stdin: Illegal byte sequence
$ wc -l *
 3081673 source-file
  102320 tmp
 3183993 total

Nota la cut error y que mi archivo “tmp” tiene solo 102320 líneas con el primer carácter especial en la línea 102321.

Echemos un vistazo a cómo se codifican estos caracteres no ASCII. Vuelvo el primer ascii que no es de 7 bits en hexdump, formatee un poco, elimine las líneas nuevas (0a) y tome solo los primeros.

$ pcregrep -o '[^x00-x7F]' source-file | head -n1 | hexdump -v -e '1/1 "%02xn"'
d6
0a

De otra manera. Sé que el primer carácter no ASCII de 7 bits está en la posición 85 en la línea 102321. Tomo esa línea y digo hexdump para tomar los dos bytes que comienzan en la posición 85. Puede ver el carácter especial (no ASCII de 7 bits) representado por un “.”, y el siguiente byte es “M” … así que este es un byte único codificación de caracteres.

$ tail -n +102321 source-file | head -n1 | hexdump -C -s85 -n2
00000055  d6 4d                                             |.M|
00000057

En ambos casos, vemos que el carácter especial está representado por d6. Dado que este carácter es una Ö que es una letra alemana, supongo que ISO 8859-1 debería incluir esto. Efectivamente, puede ver que “d6” es una coincidencia (ISO / IEC 8859-1).

Pregunta importante … ¿cómo sé que este carácter es un Ö sin estar seguro de la codificación del archivo? La respuesta es el contexto. Abrí el archivo, leí el texto y luego determiné qué carácter se supone que es. Si lo abro en Vim, se muestra como un Ö porque Vim hace un mejor trabajo de adivinación la codificación de caracteres (en este caso) que file lo hace.

Entonces, mi archivo parece ser ISO 8859-1. En teoría, debería comprobar el resto de los caracteres que no son ASCII de 7 bits para asegurarme de que ISO 8859-1 se ajusta bien … No hay nada que obligue a un programa a utilizar una única codificación al escribir un archivo en el disco. (aparte de los buenos modales).

Saltaré la verificación y pasaré al paso de conversión.

$ iconv -f iso-8859-1 -t utf8 source-file > output-file
$ file -b --mime-encoding output-file
us-ascii

Mmm. file todavía me dice que este archivo es ASCII de EE. UU. incluso después de la conversión. Vamos a comprobar con hexdump de nuevo.

$ tail -n +102321 output-file | head -n1 | hexdump -C -s85 -n2
00000055  c3 96                                             |..|
00000057

Definitivamente un cambio. Tenga en cuenta que tenemos dos bytes de ASCII que no son de 7 bits (representados por el “.” A la derecha) y el código hexadecimal para los dos bytes es ahora c3 96. Si echamos un vistazo, parece que ahora tenemos UTF-8 (c3 96 es la codificación de Ö en UTF-8) Tabla de codificación UTF-8 y caracteres Unicode

Pero file todavía informa nuestro archivo como us-ascii? Bueno, creo que esto se remonta al punto sobre file no mirar el archivo completo y el hecho de que los primeros caracteres que no son ASCII de 7 bits no aparecen hasta más tarde en el archivo.

Yo usaré sed pegar una Ö al principio del archivo y ver qué pasa.

$ sed '1s/^/Ö'$'n/' source-file > test-file
$ head -n1 test-file
Ö
$ head -n1 test-file | hexdump -C
00000000  c3 96 0a                                          |...|
00000003

Genial, tenemos diéresis. Tenga en cuenta que la codificación es c3 96 (UTF-8). Mmm.

Verificando nuestras otras diéresis en el mismo archivo nuevamente:

$ tail -n +102322 test-file | head -n1 | hexdump -C -s85 -n2
00000055  d6 4d                                             |.M|
00000057

ISO 8859-1. ¡UPS! Simplemente demuestra lo fácil que es estropear las codificaciones. Para ser claros, me las arreglé para crear una combinación de codificaciones UTF-8 e ISO 8859-1 en el mismo archivo.

Intentemos convertir nuestro nuevo archivo de prueba con la diéresis (Ö) al frente y veamos qué sucede.

$ iconv -f iso-8859-1 -t utf8 test-file > test-file-converted
$ head -n1 test-file-converted | hexdump -C
00000000  c3 83 c2 96 0a                                    |.....|
00000005
$ tail -n +102322 test-file-converted | head -n1 | hexdump -C -s85 -n2
00000055  c3 96                                             |..|
00000057

UPS. La primera diéresis que fue UTF-8 se interpretó como ISO 8859-1 ya que eso es lo que dijimos iconv. La segunda diéresis se convierte correctamente de d6 (ISO 8859-1) para c3 96 (UTF-8).

Lo intentaré de nuevo, pero esta vez usaré Vim para hacer la inserción Ö en lugar de sed. Vim pareció detectar mejor la codificación (como “latin1” también conocido como ISO 8859-1), por lo que quizás inserte la nueva Ö con una codificación consistente.

$ vim source-file
$ head -n1 test-file-2

$ head -n1 test-file-2 | hexdump -C
00000000  d6 0d 0a                                          |...|
00000003
$ tail -n +102322 test-file-2 | head -n1 | hexdump -C -s85 -n2
00000055  d6 4d                                             |.M|
00000057

Se ve bien. Parece ISO 8859-1 para diéresis nuevas y antiguas.

Ahora la prueba.

$ file -b --mime-encoding test-file-2
iso-8859-1
$ iconv -f iso-8859-1 -t utf8 test-file-2 > test-file-2-converted
$ file -b --mime-encoding test-file-2-converted
utf-8

¡Auge! Moraleja de la historia. No confíes file para adivinar siempre tu codificación correcta. Es fácil mezclar codificaciones dentro del mismo archivo. En caso de duda, mire el maleficio.

Un truco (también propenso a fallar) que abordaría esta limitación específica de file cuando se trata de archivos grandes sería acortar el archivo para asegurarse de que los caracteres especiales (no ascii) aparezcan al principio del archivo, por lo que file es más probable que los encuentre.

$ first_special=$(pcregrep -o1 -n '()[^x00-x7F]' source-file | head -n1 | cut -d":" -f1)
$ tail -n +$first_special source-file > /tmp/source-file-shorter
$ file -b --mime-encoding /tmp/source-file-shorter
iso-8859-1

A continuación, podría utilizar (presumiblemente correcta) la codificación detectada para alimentar como entrada a iconv para asegurarse de que está convirtiendo correctamente.

Actualizar

Christos Zoulas actualizado file para hacer configurable la cantidad de bytes considerados. Un día de respuesta a la solicitud de funciones, ¡increíble!

http://bugs.gw.com/view.php?id=533
Permitir alterar cuántos bytes leer de los archivos analizados desde la línea de comando

La función fue lanzada en file versión 5.26.

Mirar un archivo más grande antes de adivinar la codificación lleva tiempo. Sin embargo, es bueno tener la opción para casos de uso específicos en los que una mejor estimación puede superar el tiempo y las E / S adicionales.

Utilice la siguiente opción:

−P, −−parameter name=value

    Set various parameter limits.

    Name    Default     Explanation
    bytes   1048576     max number of bytes to read from file

Algo como…

file_to_check="myfile"
bytes_to_scan=$(wc -c < $file_to_check)
file -b --mime-encoding -P bytes=$bytes_to_scan $file_to_check

... debería funcionar si quieres forzar file para mirar el archivo completo antes de adivinar. Por supuesto, esto solo funciona si tiene file 5.26 o más reciente.

Forzar file para mostrar UTF-8 en lugar de US-ASCII

Algunas de las otras respuestas parecen centrarse en tratar de hacer file mostrar UTF-8 incluso si el archivo solo contiene ascii simple de 7 bits. Si piensa bien en esto, probablemente nunca debería querer hacer esto.

  1. Si un archivo contiene solo ascii de 7 bits pero el file El comando dice que el archivo es UTF-8, lo que implica que el archivo contiene algunos caracteres con codificación específica UTF-8. Si eso no es realmente cierto, podría causar confusión o problemas en el futuro. Si file muestra UTF-8 cuando el archivo solo contiene caracteres ascii de 7 bits, esto sería un error en el file programa.
  2. Cualquier software que requiera archivos de entrada con formato UTF-8 no debería tener ningún problema para consumir ascii de 7 bits, ya que es el mismo en un nivel de bytes que UTF-8. Si hay software que utiliza el file salida del comando antes de aceptar un archivo como entrada y no procesará el archivo a menos que "vea" UTF-8 ... bueno, ese es un diseño bastante malo. Yo diría que esto es un error en ese programa.

Si es absolutamente necesario tomar un archivo ascii simple de 7 bits y convertirlo a UTF-8, simplemente inserte un único carácter que no sea ascii de 7 bits en el archivo con codificación UTF-8 para ese carácter y listo. Pero no puedo imaginar un caso de uso en el que necesites hacer esto. El carácter UTF-8 más fácil de usar para esto es la marca de orden de bytes (BOM) que es un carácter especial que no se imprime y que sugiere que el archivo no es ASCII. Esta es probablemente la mejor opción porque no debería afectar visualmente el contenido del archivo, ya que generalmente se ignorará.

Los compiladores e intérpretes de Microsoft, y muchas piezas de software en Microsoft Windows, como el Bloc de notas, tratan la lista de materiales como un número mágico obligatorio en lugar de utilizar heurísticas. Estas herramientas agregan una lista de materiales al guardar texto como UTF-8 y no puede interpretar UTF-8 a menos que la lista de materiales esté presente o el archivo contenga solo ASCII.

Esta es la clave:

o el archivo contiene solo ASCII

Por lo tanto, algunas herramientas en Windows tienen problemas para leer archivos UTF-8 a menos que el carácter BOM esté presente. Sin embargo, esto no afecta a los archivos simples ascii de 7 bits. Es decir, esta no es una razón para forzar archivos ascii de 7 bits simples a ser UTF-8 agregando un carácter BOM.

A continuación, se ofrece más información sobre las posibles dificultades de utilizar la lista de materiales cuando no se necesita (ES necesaria para archivos UTF-8 reales que consumen algunas aplicaciones de Microsoft). https://stackoverflow.com/a/13398447/3616686

Sin embargo, si aún desea hacerlo, me interesaría escuchar su caso de uso. Aquí es cómo. En UTF-8, la lista de materiales está representada por una secuencia hexadecimal 0xEF,0xBB,0xBF y así podemos agregar fácilmente este carácter al frente de nuestro archivo ascii simple de 7 bits. Al agregar un carácter ascii que no sea de 7 bits al archivo, el archivo ya no es solo ascii de 7 bits. Tenga en cuenta que no hemos modificado ni convertido el contenido original ascii de 7 bits en absoluto. Hemos agregado un carácter único que no es ascii de 7 bits al principio del archivo, por lo que el archivo ya no está compuesto por caracteres ascii de 7 bits.

$ printf 'xEFxBBxBF' > bom.txt # put a UTF-8 BOM char in new file
$ file bom.txt
bom.txt: UTF-8 Unicode text, with no line terminators
$ file plain-ascii.txt  # our pure 7-bit ascii file
plain-ascii.txt: ASCII text
$ cat bom.txt plain-ascii.txt > plain-ascii-with-utf8-bom.txt # put them together into one new file with the BOM first
$ file plain-ascii-with-utf8-bom.txt
plain-ascii-with-utf8-bom.txt: UTF-8 Unicode (with BOM) text

La gente dice que no puede y entiendo que puede sentirse frustrado cuando hace una pregunta y obtiene esa respuesta.

Si realmente desea que se muestre en UTF-8 en lugar de US ASCII, debe hacerlo en dos pasos.

Primero:

iconv -f us-ascii -t utf-16 yourfile > youfileinutf16.*

Segundo:

iconv -f utf-16le -t utf-8 yourfileinutf16 > yourfileinutf8.*

Entonces si haces un file -i, verá que el nuevo conjunto de caracteres es UTF-8.

Nos encantaría que puedieras recomendar este artículo si te ayudó.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *