Como encontrar a codificação de um arquivo via script no Linux?

303

Preciso encontrar a codificação de todos os arquivos que são colocados em um diretório. Existe uma maneira de encontrar a codificação usada?

O filecomando não é capaz de fazer isso.

A codificação que me interessa é: ISO-8859-1. Se a codificação for qualquer outra coisa, desejo mover o arquivo para outro diretório.

Manglu
fonte
1
Se você tem uma idéia do tipo de linguagem de script que deseja usar, marque sua pergunta com o nome dessa linguagem. Que possa ajudar ...
MatrixFrog
1
Ou talvez ele esteja apenas tentando criar um script de shell?
Shalom Craimer 30/04/09
1
Qual seria a resposta para "qual linguagem de script".
Bignose
7
Talvez não esteja relacionado a esta resposta, mas a uma dica em geral: quando você pode descrever toda a sua dúvida em uma palavra ("codificação", aqui), basta fazê-lo apropos encoding. Ele pesquisa os títulos e descrições de todas as páginas de manual. Quando faço isso na minha máquina, eu vejo 3 ferramentas que podem me ajudar, a julgar por suas descrições: chardet, chardet3, chardetect3. Então, ao man chardetler e ler a página de manual, chardeté preciso apenas o utilitário de que preciso.
John Red
1
A codificação pode mudar quando você altera o conteúdo de um arquivo. Por exemplo, no vi, ao escrever um programa c simples, é provável que us-ascii, mas depois de adicionar uma linha de comentário em chinês, ele se torne utf-8. filepode dizer a codificação lendo o conteúdo do arquivo e adivinhe.
Eric Wang

Respostas:

419

Parece que você está procurando enca. Pode adivinhar e até converter entre codificações. Basta olhar para a página do manual .

Ou, na sua falta, use file -i(linux) ou file -I(osx). Isso produzirá informações do tipo MIME para o arquivo, que também incluirá a codificação do conjunto de caracteres. Também encontrei uma página de manual para ele :)

Shalom Craimer
fonte
1
De acordo com a página de manual, ele conhece o conjunto ISO 8559. Leia talvez um pouco menos superficialmente :-)
Bignose
5
Enca parece interessante. Infelizmente, a detecção parece ser muito dependente do idioma e o conjunto de idiomas suportados não é muito grande. O meu (de) está faltando :-( De qualquer forma, ferramenta legal.
er4z0r
1
Bom post sobre ferramentas como enca, enconv, convmv
gurum
6
encaparece ser completamente inútil para analisar um arquivo escrito em inglês, mas se você estiver procurando algo em estoniano, isso poderá resolver todos os seus problemas. Ferramenta muito útil, que ... </ sarcasm>
cbmanica
6
@vladkras se não houver caracteres não-ASCII em seu arquivo utf-8, então é indistinguível de ascii :)
vadipp
85
file -bi <file name>

Se você gosta de fazer isso para vários arquivos

for f in `find | egrep -v Eliminate`; do echo "$f" ' -- ' `file -bi "$f"` ; done
madu
fonte
No entanto, se o arquivo for um arquivo xml, com o atributo "encoding = 'iso-8859-1' na declaração xml, o comando file dirá que é um arquivo iso, mesmo que a verdadeira codificação seja utf-8 ...
Por
6
Por que você usa o argumento -b? Se você apenas fizer o arquivo -i *, ele gera o conjunto de caracteres adivinhado para cada arquivo.
Hans-Peter Störr
4
Eu também estava curioso sobre o argumento -b. A página de manual diz que significa "breve"Do not prepend filenames to output lines
craq
1
Não há nenhuma necessidade de saída do arquivo de análise, file -b --mime-encodingas saídas apenas o charset encoding
jesjimher
-b significa 'seja breve', o que basicamente significa não exibir o nome do arquivo que você acabou de fornecer.
Nikos
36

uchardet - Uma biblioteca de detectores de codificação portada do Mozilla.

Uso:

~> uchardet file.java 
UTF-8

Várias distribuições Linux (Debian / Ubuntu, OpenSuse-packman, ...) fornecem binários.

qwert2003
fonte
1
Obrigado! Eu não estou encantado com ainda mais pacotes, mas sudo apt-get install uchardeté tão fácil que eu não decidiu se preocupar com isso ...
sage
Como acabei de dizer em um comentário acima: o uchardet me diz falsamente que a codificação de um arquivo era "windows-1252", embora eu tenha salvado explicitamente esse arquivo como UTF-8. O uchardet nem diz "com confiança 0,4641618497109827", o que pelo menos lhe dá uma dica de que está lhe dizendo um absurdo completo. arquivo, encaixe e codificação funcionou corretamente.
Algoman
uchardetpossui uma grande vantagem filee enca, na medida em que analisa o arquivo inteiro (apenas tentei com um arquivo 20GiB), em vez de apenas o começo.
tuxayo 20/01
10

Aqui está um exemplo de script usando o arquivo -I e iconv, que funciona no MacOsX. Para sua pergunta, você precisa usar mv em vez 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
fonte
6
file -b --mime-encodingsaídas apenas o charset, assim você pode evitar todo o processamento de tubos
jesjimher
1
THX. Como indicado no MacOS, isso não funcionará: file -b --mime-codification Uso: file [-bchikLNnprsvz0] [-e test] [-f namefile] [-F separator] [-m magicfiles] [-M magicfiles ] file ... file -C -m magicfiles Tente `file --help 'para obter mais informações.
Wolfgang Fahl
6

É realmente difícil determinar se é iso-8859-1. Se você tiver um texto com apenas caracteres de 7 bits, também pode ser iso-8859-1, mas não sabe. Se você tiver caracteres de 8 bits, os caracteres da região superior também existirão em codificações de ordem. Portanto, você teria que usar um dicionário para entender melhor qual é a palavra e determinar a partir de qual letra deve ser. Finalmente, se você detectar que ele pode ser utf-8, você tem certeza de que não é iso-8859-1

Codificação é uma das coisas mais difíceis de fazer, porque você nunca sabe se nada está lhe dizendo

Norbert Hartl
fonte
Pode ajudar a tentar a força bruta. O comando a seguir tentará converter de todos os formatos de codificação com nomes que começam com WIN ou ISO em UTF8. Então, seria necessário verificar manualmente a saída, procurando uma pista para a codificação correta. Obviamente, você pode alterar os formatos filtrados substituindo ISO ou WIN por algo apropriado ou remover o filtro removendo o comando grep. para i em $ (iconv -l | tail -n +2 | grep "(^ ISO \ | ^ WIN)" | sed -e 's / \ / \ ///'); ecoam $ i; iconv -f $ i -t UTF8 santos; feito;
ndvo 16/01
5

No Debian você também pode usar encguess::

$ encguess test.txt
test.txt  US-ASCII
not2qubit
fonte
Eu instalei uchardetno Ubuntu e ele me disse que meu arquivo era WINDOWS-1252. Eu sei que isso estava errado, porque eu salvei como UTF-16 com Kate, para testar. No entanto, encguessadivinhe corretamente e foi pré-instalado no Ubuntu 19.04.
Nagev 11/06/19
5

Para converter codificação de 8859 para ASCII:

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

Com o Python, você pode usar o módulo chardet: https://github.com/chardet/chardet

fccoelho
fonte
Domínio não existente: feedparser.org
Rune
No momento, este comentário ainda está disponível no Github: github.com/dcramer/chardet
Rick Hanlon II
A partir deste comentário, está no chardet / chardet no github. Resposta atualizada.
Quentin Pradet 4/15
chardet relata "Nenhum", chardet3 engasga com a primeira linha do arquivo exatamente da mesma maneira que meu script python.
Joels Elf
3

Isso não é algo que você pode fazer de maneira infalível. Uma possibilidade seria examinar todos os caracteres no arquivo para garantir que ele não contenha caracteres nos intervalos 0x00 - 0x1fou0x7f -0x9f , mas, como eu disse, isso pode ser verdade para qualquer número de arquivos, incluindo pelo menos uma outra variante da ISO8859.

Outra possibilidade é procurar palavras específicas no arquivo em todos os idiomas suportados e ver se você as encontra.

Por exemplo, encontre o equivalente do inglês "and", "but", "to", "of" e assim por diante em todos os idiomas suportados do 8859-1 e verifique se eles têm um grande número de ocorrências dentro do Arquivo.

Não estou falando de tradução literal como:

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

embora isso seja possível. Estou falando de palavras comuns no idioma de destino (pelo que sei, islandês não tem palavra para "e" - você provavelmente teria que usar a palavra para "peixe" [desculpe, isso é um pouco estereotipado, eu não significa qualquer ofensa, apenas ilustrando um ponto]).

paxdiablo
fonte
2

Sei que você está interessado em uma resposta mais geral, mas o que é bom em ASCII geralmente é bom em outras codificações. Aqui está um liner Python para determinar se a entrada padrão é ASCII. (Tenho certeza de que isso funciona no Python 2, mas só o testei no 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
fonte
2

Se você está falando sobre arquivos XML (ISO-8859-1), a declaração XML dentro deles especifica a codificação: <?xml version="1.0" encoding="ISO-8859-1" ?>
Portanto, você pode usar expressões regulares (por exemplo, com perl) para verificar cada arquivo para essa especificação.
Mais informações podem ser encontradas aqui: Como determinar a codificação de arquivo de texto .

evgeny9
fonte
bem, essa linha pode ser copiada e colada por alguém que não sabe qual codificação está usando.
Algoman
Cuidado, nada sobre a declaração na parte superior garante que o arquivo REALMENTE seja codificado dessa maneira. Se você realmente se importa com a codificação, você precisa validá-la.
Jazzepi
2

No php você pode conferir como abaixo:

Especificando lista de codificação explicitamente:

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" mais preciso:

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

Aqui no primeiro exemplo, você pode ver que eu coloquei uma lista de codificações (detectar a ordem da lista) que podem estar correspondentes. Para obter resultados mais precisos, você pode usar todas as codificações possíveis em: mb_list_encodings ()

Nota As funções mb_ * requerem php-mbstring

apt-get install php-mbstring
Mohamed23gharbi
fonte
0

No Cygwin, parece que funciona para mim:

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

Exemplo:

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

Você pode canalizar isso para o awk e criar um comando iconv para converter tudo em utf8, a partir de qualquer codificação de origem suportada pelo iconv.

Exemplo:

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
fonte
0

Você pode extrair a codificação de um único arquivo com o comando file. Eu tenho um arquivo sample.html com:

$ file sample.html 

sample.html: documento HTML, texto Unicode UTF-8, com linhas muito longas

$ file -b sample.html

Documento HTML, texto Unicode UTF-8, com linhas muito longas

$ file -bi sample.html

texto / html; charset = utf-8

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

utf-8

Daniel Faure
fonte
1
a saída que eu vejo é apenas "arquivo regular"
Mordechai
0

Estou usando o seguinte script para

  1. Encontre todos os arquivos que correspondem a FILTER com SRC_ENCODING
  2. Crie um backup deles
  3. Converta-os em DST_ENCODING
  4. (opcional) Remova os backups

.

#!/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
fonte
0

com este comando:

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

você pode listar todos os arquivos em um diretório e subdiretórios e a codificação correspondente.

danilo
fonte
-2

Com Perl, use Encode :: Detect.

manu_v
fonte
7
Você pode dar um exemplo de como usá-lo no shell?
Lri
Outro pôster (@fccoelho) forneceu um módulo Python como uma solução que obtém um +3 e este pôster recebe um -2 para uma resposta muito semelhante, exceto que é para um módulo Perl. Por que o duplo padrão ?!
Happy Green Kid Naps
4
Talvez um exemplo de código de uma linha de comando perl ajude essa resposta.
vikingsteve