¿Cómo encontrar la codificación de un archivo a través de un script en Linux?

303

Necesito encontrar la codificación de todos los archivos que se colocan en un directorio. ¿Hay alguna manera de encontrar la codificación utilizada?

El filecomando no puede hacer esto.

La codificación que me interesa es: ISO-8859-1. Si la codificación es otra cosa, quiero mover el archivo a otro directorio.

Manglu
fuente
1
Si tiene una idea de qué tipo de lenguaje de script puede usar, etiquete su pregunta con el nombre de ese idioma. Eso podría ayudar ...
MatrixFrog
1
¿O tal vez solo está tratando de construir un script de shell?
Shalom Craimer
1
Cuál sería una respuesta a "qué lenguaje de programación".
bignose
77
Tal vez no esté relacionado con esta respuesta, pero un consejo en general: cuando pueda describir toda su duda en una palabra ("codificación", aquí), simplemente hágalo apropos encoding. Busca los títulos y descripciones de todas las páginas de manual. Cuando hago esto en mi máquina, veo 3 herramientas que me puedan ayudar, a juzgar por sus descripciones: chardet, chardet3, chardetect3. Luego, al hacer man chardety leer la página de manual, me dice que esa chardetes la utilidad que necesito.
John Red
1
La codificación puede cambiar cuando cambia el contenido de un archivo. Por ejemplo, en vi, cuando se escribe un programa c simple, es probable us-ascii, pero después de agregar una línea de comentario chino, se convierte en utf-8. filepuede decir la codificación leyendo el contenido del archivo y adivinar.
Eric Wang

Respuestas:

419

Suena como si estuvieras buscando enca. Puede adivinar e incluso convertir entre codificaciones. Solo mira la página del manual .

O, en su defecto, use file -i(linux) u file -I(osx). Eso generará información de tipo MIME para el archivo, que también incluirá la codificación del juego de caracteres. También encontré una página de manual para ello :)

Shalom Craimer
fuente
1
Según la página de manual, conoce el conjunto ISO 8559. Tal vez lea un poco menos acertadamente :-)
bignose
55
Enca suena interesante. Lamentablemente, la detección parece depender mucho del idioma y el conjunto de idiomas admitidos no es muy grande. Mina (de) falta :-( De todos modos enfriar herramienta.
er4z0r
1
Buena publicación sobre herramientas como enca, enconv, convmv
GuruM
66
encaparece ser completamente inútil para analizar un archivo escrito en inglés, pero si está buscando algo en estonio, podría resolver todos sus problemas. Herramienta muy útil, que ... </sarcasm>
cbmanica
66
@vladkras si no hay caracteres no ascii en su archivo utf-8, entonces no se puede distinguir de ascii :)
vadipp
85
file -bi <file name>

Si te gusta hacer esto por un montón de archivos

for f in `find | egrep -v Eliminate`; do echo "$f" ' -- ' `file -bi "$f"` ; done
madu
fuente
Sin embargo, si el archivo es un archivo xml, con el atributo "encoding = 'iso-8859-1' en la declaración xml, el comando del archivo dirá que es un archivo iso, incluso si la codificación verdadera es utf-8 ...
Por
66
¿Por qué usas el argumento -b? Si solo hace el archivo -i *, genera el juego de caracteres adivinado para cada archivo.
Hans-Peter Störr
44
Tenía curiosidad sobre el argumento -b también. La página del manual dice que significa "breve"Do not prepend filenames to output lines
craq
1
No es necesario analizar la salida del archivo, file -b --mime-encodingsolo genera la codificación del
juego de
-b significa 'ser breve', lo que básicamente significa no mostrar el nombre de archivo que acaba de dar.
Nikos
36

uchardet : una biblioteca de detectores de codificación portada desde Mozilla.

Uso:

~> uchardet file.java 
UTF-8

Varias distribuciones de Linux (Debian / Ubuntu, OpenSuse-packman, ...) proporcionan binarios.

qwert2003
fuente
1
¡Gracias! No estoy encantado con aún más paquetes, pero sudo apt-get install uchardetes tan fácil que decidí no preocuparme por eso ...
sabio
Como acabo de decir en un comentario anterior: uchardet me dice falsamente que la codificación de un archivo era "windows-1252", aunque guardé explícitamente ese archivo como UTF-8. uchardet ni siquiera dice "con confianza 0.4641618497109827", lo que al menos le daría una pista de que le está diciendo una tontería completa. file, enca y encguess funcionaron correctamente.
Algoman
uchardettiene una gran ventaja sobre filey enca, en el sentido de que analiza todo el archivo (solo lo intenté con un archivo de 20GiB) en lugar de solo el comienzo.
tuxayo
10

Aquí hay un script de ejemplo que usa el archivo -I e iconv que funciona en MacOsX. Para su pregunta, necesita usar mv en lugar de iconv

#!/bin/bash
# 2016-02-08
# check encoding and convert files
for f in *.java
do
  encoding=`file -I $f | cut -f 2 -d";" | cut -f 2 -d=`
  case $encoding in
    iso-8859-1)
    iconv -f iso8859-1 -t utf-8 $f > $f.utf8
    mv $f.utf8 $f
    ;;
  esac
done
Wolfgang Fahl
fuente
66
file -b --mime-encodingproduce solo el juego de caracteres, para que pueda evitar todo el procesamiento de tuberías
jesjimher
1
Gracias. Como se señaló en MacOS, esto no funcionará: file -b --mime-encoding Uso: file [-bchikLNnprsvz0] [-e test] [-f namefile] [-F separator] [-m magicfiles] [-M magicfiles] ] archivo ... archivo -C -m archivos mágicos Pruebe `archivo - ayuda 'para obtener más información.
Wolfgang Fahl
6

Es realmente difícil determinar si es iso-8859-1. Si tiene un texto con solo caracteres de 7 bits que también podría ser iso-8859-1 pero no lo sabe. Si tiene caracteres de 8 bits, los caracteres de la región superior también existen en codificaciones de orden. Por lo tanto, tendría que usar un diccionario para adivinar mejor qué palabra es y determinar a partir de ahí qué letra debe ser. Finalmente, si detecta que puede ser utf-8, está seguro de que no es iso-8859-1

La codificación es una de las cosas más difíciles de hacer porque nunca se sabe si nada te dice

Norbert Hartl
fuente
Puede ser útil tratar de forzar la fuerza bruta. El siguiente comando intentará convertir desde todos los formatos de codificación electrónica con nombres que comienzan con WIN o ISO a UTF8. Entonces uno necesitaría verificar manualmente la salida buscando una pista en la codificación correcta. Por supuesto, puede cambiar los formatos filtrados reemplazando ISO o WIN por algo apropiado o eliminar el filtro eliminando el comando grep. para i en $ (iconv -l | tail -n +2 | grep "(^ ISO \ | ^ WIN)" | sed -e 's / \ / \ ///'); hacer eco $ i; iconv -f $ i -t UTF8 santos; hecho;
ndvo
5

En Debian también puedes usar encguess:

$ encguess test.txt
test.txt  US-ASCII
not2qubit
fuente
Lo instalé uchardeten Ubuntu y me dijo que mi archivo era WINDOWS-1252. Sé que esto estaba mal porque lo guardé como UTF-16 con Kate, para probar. Sin embargo, encguessadivine correctamente, y fue preinstalado en Ubuntu 19.04.
Nagev
5

Para convertir la codificación de 8859 a ASCII:

iconv -f ISO_8859-1 -t ASCII filename.txt
fimbulwinter
fuente
4

Con Python, puede usar el módulo chardet: https://github.com/chardet/chardet

fccoelho
fuente
Dominio no existente: feedparser.org
Runa
A partir de este comentario, todavía está disponible en Github: github.com/dcramer/chardet
Rick Hanlon II
A partir de este comentario, está en chardet / chardet en github. Respuesta actualizada
Quentin Pradet
informes Chardet "Ninguno", choques chardet3 en la primera línea del archivo en la exacta misma manera que mi script en Python hace.
Joels Elf
3

Esto no es algo que pueda hacer de manera infalible. Una posibilidad sería examinar cada carácter en el archivo para asegurarse de que no contenga ningún carácter en los rangos 0x00 - 0x1fo0x7f -0x9f , como dije, esto puede ser cierto para cualquier número de archivos, incluida al menos otra variante de ISO8859.

Otra posibilidad es buscar palabras específicas en el archivo en todos los idiomas admitidos y ver si puede encontrarlas.

Entonces, por ejemplo, encuentre el equivalente del inglés "y", "pero", "a", "de" y así sucesivamente en todos los idiomas admitidos de 8859-1 y vea si tienen una gran cantidad de ocurrencias dentro del expediente.

No estoy hablando de traducción literal como:

English   French
-------   ------
of        de, du
and       et
the       le, la, les

Aunque eso es posible. Estoy hablando de palabras comunes en el idioma de destino (por lo que sé, el islandés no tiene una palabra para "y" - probablemente tendría que usar su palabra para "pez" [lo siento, es un poco estereotípico, no lo hice significa cualquier ofensa, solo ilustrando un punto]).

paxdiablo
fuente
2

Sé que está interesado en una respuesta más general, pero lo que es bueno en ASCII generalmente es bueno en otras codificaciones. Aquí hay una línea única de Python para determinar si la entrada estándar es ASCII. (Estoy bastante seguro de que esto funciona en Python 2, pero solo lo he probado en Python 3.)

python -c 'from sys import exit,stdin;exit()if 128>max(c for l in open(stdin.fileno(),"b") for c in l) else exit("Not ASCII")' < myfile.txt
wkschwartz
fuente
2

Si está hablando de archivos XML (ISO-8859-1), la declaración XML dentro de ellos especifica la codificación: por lo <?xml version="1.0" encoding="ISO-8859-1" ?>
tanto, puede usar expresiones regulares (por ejemplo, con perl) para verificar cada archivo para dicha especificación.
Puede encontrar más información aquí: Cómo determinar la codificación de archivos de texto .

evgeny9
fuente
bueno, esa línea podría ser copiada y pegada por alguien que no sabe qué codificación está usando.
Algoman
Una advertencia: nada sobre la declaración en la parte superior garantiza que el archivo REALMENTE esté codificado de esa manera. Si realmente te importa la codificación que necesitas para validarla tú mismo.
Jazzepi
2

En php puedes marcar como a continuación:

Especificando la lista de codificación explícitamente:

php -r "echo 'probably : ' . mb_detect_encoding(file_get_contents('myfile.txt'), 'UTF-8, ASCII, JIS, EUC-JP, SJIS, iso-8859-1') . PHP_EOL;"

"Mb_list_encodings" más precisos:

php -r "echo 'probably : ' . mb_detect_encoding(file_get_contents('myfile.txt'), mb_list_encodings()) . PHP_EOL;"

Aquí, en el primer ejemplo, puede ver que pongo una lista de codificaciones (detectar el orden de la lista) que podrían coincidir. Para obtener un resultado más preciso, puede usar todas las codificaciones posibles a través de: mb_list_encodings ()

Nota: las funciones mb_ * requieren php-mbstring

apt-get install php-mbstring
Mohamed23gharbi
fuente
0

En Cygwin, parece que funciona para mí:

find -type f -name "<FILENAME_GLOB>" | while read <VAR>; do (file -i "$<VAR>"); done

Ejemplo:

find -type f -name "*.txt" | while read file; do (file -i "$file"); done

Puede canalizar eso a awk y crear un comando iconv para convertir todo a utf8, desde cualquier codificación fuente admitida por iconv.

Ejemplo:

find -type f -name "*.txt" | while read file; do (file -i "$file"); done | awk -F[:=] '{print "iconv -f "$3" -t utf8 \""$1"\" > \""$1"_utf8\""}' | bash
skeetastax
fuente
0

Puede extraer la codificación de un solo archivo con el comando de archivo. Tengo un archivo sample.html con:

$ file sample.html 

sample.html: documento HTML, texto UTF-8 Unicode, con líneas muy largas

$ file -b sample.html

Documento HTML, texto Unicode UTF-8, con líneas muy largas

$ file -bi sample.html

texto / html; charset = utf-8

$ file -bi sample.html  | awk -F'=' '{print $2 }'

utf-8

Daniel Faure
fuente
1
el resultado que obtengo es solo "archivo normal"
Mordechai
0

Estoy usando el siguiente script para

  1. Encuentra todos los archivos que coinciden con FILTER con SRC_ENCODING
  2. Crea una copia de seguridad de ellos
  3. Conviértalos a DST_ENCODING
  4. (opcional) Eliminar las copias de seguridad

.

#!/bin/bash -xe

SRC_ENCODING="iso-8859-1"
DST_ENCODING="utf-8"
FILTER="*.java"

echo "Find all files that match the encoding $SRC_ENCODING and filter $FILTER"
FOUND_FILES=$(find . -iname "$FILTER" -exec file -i {} \; | grep "$SRC_ENCODING" | grep -Eo '^.*\.java')

for FILE in $FOUND_FILES ; do
    ORIGINAL_FILE="$FILE.$SRC_ENCODING.bkp"
    echo "Backup original file to $ORIGINAL_FILE"
    mv "$FILE" "$ORIGINAL_FILE"

    echo "converting $FILE from $SRC_ENCODING to $DST_ENCODING"
    iconv -f "$SRC_ENCODING" -t "$DST_ENCODING" "$ORIGINAL_FILE" -o "$FILE"
done

echo "Deleting backups"
find . -iname "*.$SRC_ENCODING.bkp" -exec rm {} \;
Matyas
fuente
0

con este comando:

for f in `find .`; do echo `file -i "$f"`; done

Puede enumerar todos los archivos en un directorio y subdirectorios y la codificación correspondiente.

danilo
fuente
-2

Con Perl, use Encode :: Detect.

manu_v
fuente
77
¿Puedes dar un ejemplo de cómo usarlo en el shell?
Lri
Otro póster (@fccoelho) proporcionó un módulo Python como una solución que obtiene un +3 y este póster obtiene un -2 por una respuesta muy similar, excepto que es para un módulo Perl. ¿Por qué el doble rasero?
Happy Green Kid Naps el
44
Tal vez un ejemplo de código de un perl one-liner ayudaría a esta respuesta.
vikingsteve