No trabalho, parece que nenhuma semana passa sem algum conluio, calamidade ou catástrofe relacionada à codificação. O problema geralmente deriva de programadores que pensam que podem processar de forma confiável um arquivo de “texto” sem especificar a codificação. Mas você não pode.
Portanto, decidiu-se, doravante, proibir que os arquivos tenham nomes que terminem em *.txt
ou *.text
. O pensamento é que essas extensões enganam o programador casual em uma complacência maçante em relação às codificações, e isso leva a um manuseio impróprio. Seria quase melhor não ter extensão alguma, porque pelo menos você sabe que não sabe o que tem.
No entanto, não iremos tão longe. Em vez disso, espera-se que você use um nome de arquivo que termine na codificação. Assim, para arquivos de texto, por exemplo, estes seriam algo como README.ascii
, README.latin1
, README.utf8
, etc.
Para arquivos que exigem uma extensão particular, se alguém pode especificar a codificação dentro do próprio arquivo, como em Perl ou Python, então você deve fazer isso. Para arquivos como o código-fonte Java, em que não existe tal recurso interno ao arquivo, você colocará a codificação antes da extensão, como SomeClass-utf8.java
.
Para saída, UTF-8 deve ser fortemente preferido.
Mas, para entrada, precisamos descobrir como lidar com os milhares de arquivos em nossa base de código chamada *.txt
. Queremos renomear todos eles para se adequarem ao nosso novo padrão. Mas não podemos olhar para todos eles. Portanto, precisamos de uma biblioteca ou programa que realmente funcione.
Eles estão em ASCII, ISO-8859-1, UTF-8, Microsoft CP1252 ou Apple MacRoman. Embora saibamos que podemos dizer se algo é ASCII, e temos uma boa mudança de saber se algo é provavelmente UTF-8, estamos perplexos quanto às codificações de 8 bits. Como estamos executando em um ambiente Unix misto (Solaris, Linux, Darwin) com a maioria dos desktops sendo Macs, temos alguns arquivos MacRoman irritantes. E isso é especialmente um problema.
Já faz algum tempo que procuro uma maneira de determinar programaticamente qual dos
- ASCII
- ISO-8859-1
- CP1252
- MacRoman
- UTF-8
um arquivo está em e eu não encontrei um programa ou biblioteca que possa distinguir com segurança entre as três codificações de 8 bits diferentes. Provavelmente temos mais de mil arquivos MacRoman sozinhos, então qualquer detector de charset que usamos deve ser capaz de farejá-los. Nada do que vi pode resolver o problema. Eu tinha grandes esperanças para a biblioteca do detector de conjuntos de caracteres ICU , mas ela não pode lidar com o MacRoman. Também examinei módulos para fazer o mesmo tipo de coisa em Perl e Python, mas sempre é a mesma história: sem suporte para detectar MacRoman.
O que estou procurando, portanto, é uma biblioteca ou programa existente que determine com segurança em qual dessas cinco codificações um arquivo está - e de preferência mais do que isso. Em particular, ele deve distinguir entre as três codificações de 3 bits que citei, especialmente a MacRoman . Os arquivos têm mais de 99% de texto no idioma inglês; existem alguns em outras línguas, mas não muitos.
Se for um código de biblioteca, nossa preferência de linguagem é em Perl, C, Java ou Python, e nessa ordem. Se for apenas um programa, não nos importamos em qual linguagem ele está, contanto que venha com o código-fonte completo, seja executado no Unix e seja totalmente livre.
Alguém mais teve esse problema de um zilhão de arquivos de texto legados codificados aleatoriamente? Se sim, como você tentou resolvê-lo e qual foi seu grau de sucesso? Este é o aspecto mais importante da minha pergunta, mas também estou interessado em saber se você acha que encorajar os programadores a nomear (ou renomear) seus arquivos com a codificação real em que esses arquivos estão nos ajudará a evitar o problema no futuro. Alguém já tentou impor isso em uma base institucional, e se sim, foi que bem sucedida ou não, e por quê?
E sim, compreendo perfeitamente por que não se pode garantir uma resposta definitiva dada a natureza do problema. Esse é especialmente o caso de arquivos pequenos, nos quais você não tem dados suficientes para continuar. Felizmente, nossos arquivos raramente são pequenos. Além do README
arquivo aleatório , a maioria está na faixa de tamanho de 50k a 250k, e muitos são maiores. Qualquer coisa maior do que alguns K de tamanho está garantido em inglês.
O domínio do problema é a mineração de texto biomédica, então às vezes lidamos com corpora extensos e extremamente grandes, como todo o repositório de acesso aberto do PubMedCentral. Um arquivo bastante grande é o BioThesaurus 6.0, com 5,7 gigabytes. Este arquivo é especialmente irritante porque é quase todo UTF-8. No entanto, alguns estúpidos foram e enfiaram algumas linhas nele que estão em alguma codificação de 8 bits - Microsoft CP1252, eu acredito. Demora um pouco antes de você tropeçar naquele. :(
Respostas:
Primeiro, os casos fáceis:
ASCII
Se seus dados não contiverem bytes acima de 0x7F, então são ASCII. (Ou uma codificação ISO646 de 7 bits, mas esses são muito obsoletos.)
UTF-8
Se seus dados forem validados como UTF-8, você pode assumir com segurança que é UTF-8. Devido às regras de validação estritas do UTF-8, falsos positivos são extremamente raros.
ISO-8859-1 vs. windows-1252
A única diferença entre essas duas codificações é que ISO-8859-1 tem os caracteres de controle C1, onde windows-1252 tem os caracteres imprimíveis € ‚ƒ„… † ‡ ˆ Š ‹ŒŽ ''“ ”• –—˜ ™ š› œžŸ. Já vi muitos arquivos que usam aspas curvas ou travessões, mas nenhum que usa caracteres de controle C1. Portanto, nem se preocupe com eles, ou ISO-8859-1, apenas detecte o Windows-1252.
Isso agora deixa você com apenas uma pergunta.
Como você distingue o MacRoman do cp1252?
Isso é muito mais complicado.
Caracteres indefinidos
Os bytes 0x81, 0x8D, 0x8F, 0x90, 0x9D não são usados no Windows-1252. Se ocorrerem, suponha que os dados sejam MacRoman.
Personagens idênticos
Os bytes 0xA2 (¢), 0xA3 (£), 0xA9 (©), 0xB1 (±), 0xB5 (µ) são iguais em ambas as codificações. Se esses forem os únicos bytes não ASCII, não importa se você escolhe MacRoman ou cp1252.
Abordagem estatística
Conte frequências de caracteres (NÃO bytes!) Nos dados que você sabe serem UTF-8. Determine os caracteres mais frequentes. Em seguida, use esses dados para determinar se os caracteres cp1252 ou MacRoman são mais comuns.
Por exemplo, em uma pesquisa que acabei de realizar em 100 artigos aleatórios da Wikipedia em inglês, os caracteres não ASCII mais comuns são
·•–é°®’èö—
. Com base neste fato,Conte os bytes sugerindo cp1252 e os bytes sugerindo MacRoman e vá com o que for maior.
fonte
Mozilla nsUniversalDetector (vínculos Perl: Encode :: Detect / Encode :: Detect :: Detector ) é comprovado milhões de vezes.
fonte
x-mac-cyrillic
é suportado,x-mac-hebrew
é discutido detalhadamente nos comentários,x-mac-anything-else
não é mencionado.Minha tentativa de tal heurística (assumindo que você descartou ASCII e UTF-8):
Nota:
Não faça isso!!
O compilador Java espera que os nomes dos arquivos correspondam aos nomes das classes, portanto, renomear os arquivos tornará o código-fonte não compilável. O correto seria adivinhar a codificação e usar a
native2ascii
ferramenta para converter todos os caracteres não ASCII em sequências de escape Unicode .fonte
*.text
arquivos."Perl, C, Java ou Python, e nessa ordem": atitude interessante :-)
"temos uma boa chance de saber se algo provavelmente é UTF-8": na verdade, a chance de que um arquivo contendo texto significativo codificado em algum outro conjunto de caracteres que usa bytes com conjuntos de bits altos seja decodificado com sucesso como UTF-8 é muito pequena.
Estratégias UTF-8 (no idioma de preferência):
Depois de decidir que não é ASCII nem UTF-8:
Os detectores de charset de origem Mozilla que eu conheço não suportam MacRoman e em qualquer caso não fazem um bom trabalho em charsets de 8 bits, especialmente com o inglês porque AFAICT eles dependem de verificar se a decodificação faz sentido no dado idioma, ignorando os caracteres de pontuação e com base em uma ampla seleção de documentos nesse idioma.
Como outros observaram, você realmente só tem os caracteres de pontuação de conjunto de bits altos disponíveis para distinguir entre cp1252 e macroman. Eu sugiro treinar um modelo do tipo Mozilla em seus próprios documentos, não Shakespeare ou Hansard ou a Bíblia KJV, e levando todos os 256 bytes em consideração. Presumo que seus arquivos não tenham nenhuma marcação (HTML, XML, etc) neles - isso distorceria as probabilidades algo chocante.
Você mencionou arquivos que são principalmente UTF-8, mas não conseguem decodificar. Você também deve suspeitar muito de:
(1) arquivos que são supostamente codificados em ISO-8859-1, mas contêm "caracteres de controle" na faixa de 0x80 a 0x9F, inclusive ... isso é tão predominante que o rascunho do padrão HTML5 diz para decodificar TODOS os streams HTML declarados como ISO-8859 -1 usando cp1252.
(2) arquivos que decodificam OK como UTF-8, mas o Unicode resultante contém "caracteres de controle" no intervalo U + 0080 a U + 009F inclusive ... isso pode resultar da transcodificação de cp1252 / cp850 (já vi acontecer!) / Etc arquivos de "ISO-8859-1" a UTF-8.
Contexto: Eu tenho um projeto de tarde de domingo úmido para criar um detector de conjunto de caracteres baseado em Python que é orientado a arquivo (em vez de orientado para a web) e funciona bem com conjuntos de caracteres de 8 bits, incluindo
legacy ** n
alguns como cp850 e cp437. Ainda não está nem perto do horário nobre. Estou interessado em arquivos de treinamento; seus arquivos ISO-8859-1 / cp1252 / MacRoman são tão "desimpedidos" quanto você espera que a solução de código de qualquer pessoa seja?fonte
Como você descobriu, não existe uma maneira perfeita de resolver esse problema, porque sem o conhecimento implícito sobre qual codificação um arquivo usa, todas as codificações de 8 bits são exatamente as mesmas: Uma coleção de bytes. Todos os bytes são válidos para todas as codificações de 8 bits.
O melhor que você pode esperar é algum tipo de algoritmo que analise os bytes e, com base nas probabilidades de um determinado byte sendo usado em um determinado idioma com uma determinada codificação, adivinhará qual codificação o arquivo usa. Mas isso tem que saber qual idioma o arquivo usa, e se torna completamente inútil quando você tem arquivos com codificações mistas.
Por outro lado, se você sabe que o texto em um arquivo está escrito em inglês, é improvável que você note qualquer diferença na codificação que decidir usar para esse arquivo, já que as diferenças entre todas as codificações mencionadas estão todas localizadas em as partes das codificações que especificam caracteres normalmente não usados no idioma inglês. Você pode ter alguns problemas onde o texto usa formatação especial ou versões especiais de pontuação (CP1252 tem várias versões dos caracteres de aspas, por exemplo), mas para a essência do texto provavelmente não haverá problemas.
fonte
Se você puder detectar todas as codificações EXCETO para macroman, então seria lógico supor que aquelas que não podem ser decifradas estão em macroman. Em outras palavras, basta fazer uma lista dos arquivos que não puderam ser processados e tratá-los como se fossem macroman.
Outra maneira de classificar esses arquivos seria fazer um programa baseado em servidor que permite aos usuários decidir qual codificação não está distorcida. Claro, seria dentro da empresa, mas com 100 funcionários fazendo alguns por dia, você terá milhares de arquivos feitos em nenhum momento.
Finalmente, não seria melhor apenas converter todos os arquivos existentes em um único formato e exigir que os novos arquivos estivessem nesse formato.
fonte
Atualmente, estou escrevendo um programa que traduz arquivos em XML. Ele precisa detectar automaticamente o tipo de cada arquivo, que é um superconjunto do problema de determinar a codificação de um arquivo de texto. Para determinar a codificação, estou usando uma abordagem bayesiana. Ou seja, meu código de classificação calcula uma probabilidade (probabilidade) de que um arquivo de texto tenha uma codificação específica para todas as codificações que entende. O programa então seleciona o decodificador mais provável. A abordagem bayesiana funciona assim para cada codificação.
Verifica-se que Bayes teorema torna-se muito fácil de fazer se ao invés de probabilidades de computação, você calcular conteúdo de informação , que é o logaritmo das probabilidades :
info = log(p / (1.0 - p))
.Você terá que calcular a probabilidade initail priori, e as correlações, examinando um corpus de arquivos que você classificou manualmente.
fonte