Como posso detectar a codificação / página de código de um arquivo de texto

Em nosso aplicativo, recebemos arquivos de texto ( .txt , .csv , etc.) de diversas fonts. Ao ler, esses arquivos às vezes contêm lixo, porque os arquivos foram criados em uma página de códigos diferente / desconhecida.

Existe uma maneira de detectar (automaticamente) a página de código de um arquivo de texto?

O detectEncodingFromByteOrderMarks , no construtor StreamReader , funciona para UTF8 e outros arquivos marcados com unicode, mas estou procurando uma maneira de detectar páginas de código, como ibm850 , ibm850 .


Obrigado por suas respostas, isso é o que eu fiz.

Os arquivos que recebemos são de usuários finais, eles não têm a menor idéia sobre páginas de códigos. Os receptores também são usuários finais, e agora é isso que eles sabem sobre páginas de código: existem códigos de código e são irritantes.

Solução:

  • Abra o arquivo recebido no bloco de notas, olhe para um trecho de texto truncado. Se alguém é chamado de François ou algo assim, com sua inteligência humana, você pode adivinhar isso.
  • Eu criei um pequeno aplicativo que o usuário pode usar para abrir o arquivo e digite um texto que o usuário saiba que aparecerá no arquivo, quando a página de código correta for usada.
  • Faça um loop em todas as páginas de código e exiba as que fornecem uma solução com o texto fornecido pelo usuário.
  • Se mais de uma página de código aparecer, peça ao usuário para especificar mais texto.

Você não pode detectar a página de códigos, você precisa ser informado. Você pode analisar os bytes e adivinha-lo, mas isso pode dar alguns resultados bizarros (às vezes divertidos). Não consigo encontrá-lo agora, mas tenho certeza de que o Notepad pode ser induzido a exibir texto em inglês em chinês.

De qualquer forma, isso é o que você precisa ler: O Mínimo Absoluto Todo Desenvolvedor de Software Absolutamente, Positivamente Deve Saber Sobre Unicode e Conjuntos de Caracteres (Sem desculpas!) .

Especificamente Joel diz:

O único fato mais importante sobre as codificações

Se você esquecer completamente tudo que acabei de explicar, lembre-se de um fato extremamente importante. Não faz sentido ter uma string sem saber qual codificação ela usa. Você não pode mais enfiar a cabeça na areia e fingir que o texto “simples” é ASCII. Não há nenhuma coisa como texto simples.

Se você tiver uma cadeia de caracteres, na memory, em um arquivo ou em uma mensagem de email, precisará saber em que codificação ela está ou não poderá interpretá-la ou exibi-la aos usuários corretamente.

Se você está procurando detectar codificações não-UTF (ou seja, sem BOM), você está basicamente ligado à heurística e análise estatística do texto. Você pode querer dar uma olhada no papel Mozilla na detecção de charset universal ( mesmo link, com melhor formatação via Wayback Machine ).

Você já tentou porta C # para Mozilla Universal Charset Detector

Exemplo de http://code.google.com/p/ude/

 public static void Main(String[] args) { string filename = args[0]; using (FileStream fs = File.OpenRead(filename)) { Ude.CharsetDetector cdet = new Ude.CharsetDetector(); cdet.Feed(fs); cdet.DataEnd(); if (cdet.Charset != null) { Console.WriteLine("Charset: {0}, confidence: {1}", cdet.Charset, cdet.Confidence); } else { Console.WriteLine("Detection failed."); } } } 

Você não pode detectar a página de códigos

Isso é claramente falso. Todo navegador tem algum tipo de detector universal de caracteres para lidar com páginas que não têm nenhuma indicação de codificação. O Firefox tem um. Você pode baixar o código e ver como ele faz isso. Veja alguma documentação aqui . Basicamente, é uma heurística, mas que funciona muito bem.

Dada uma quantidade razoável de texto, é até mesmo possível detectar o idioma.

Aqui está outro que acabei de encontrar usando o Google:

Eu sei que é muito tarde para esta pergunta e esta solução não vai agradar a alguns (por causa do seu preconceito inglês e sua falta de testes estatísticos / empíricos), mas funcionou muito bem para mim, especialmente para processar dados CSV enviados:

http://www.architectshack.com/TextFileEncodingDetector.ashx

Vantagens:

  • Detecção de lista técnica embutida
  • Codificação padrão / de fallback personalizável
  • bastante confiável (na minha experiência) para arquivos baseados na Europa Ocidental contendo alguns dados exóticos (por exemplo, nomes franceses) com uma mistura de arquivos UTF-8 e Latin-1 – basicamente a maior parte dos ambientes europeus e ocidentais.

Nota: Eu sou o único que escreveu esta class, então, obviamente, tome isso com um grão de sal! 🙂

O Notepad ++ tem esse recurso pronto para uso. Também suporta a mudança.

Procurando por solução diferente, descobri que

https://code.google.com/p/ude/

esta solução é meio pesada.

Eu precisava de alguma detecção básica de codificação, baseada em 4 primeiros bytes e provavelmente na detecção de charset xml – então eu peguei alguns exemplos de código fonte da internet e adicionei uma versão ligeiramente modificada de

http://lists.w3.org/Archives/Public/www-validator/2002Aug/0084.html

escrito para Java.

  public static Encoding DetectEncoding(byte[] fileContent) { if (fileContent == null) throw new ArgumentNullException(); if (fileContent.Length < 2) return Encoding.ASCII; // Default fallback if (fileContent[0] == 0xff && fileContent[1] == 0xfe && (fileContent.Length < 4 || fileContent[2] != 0 || fileContent[3] != 0 ) ) return Encoding.Unicode; if (fileContent[0] == 0xfe && fileContent[1] == 0xff ) return Encoding.BigEndianUnicode; if (fileContent.Length < 3) return null; if (fileContent[0] == 0xef && fileContent[1] == 0xbb && fileContent[2] == 0xbf) return Encoding.UTF8; if (fileContent[0] == 0x2b && fileContent[1] == 0x2f && fileContent[2] == 0x76) return Encoding.UTF7; if (fileContent.Length < 4) return null; if (fileContent[0] == 0xff && fileContent[1] == 0xfe && fileContent[2] == 0 && fileContent[3] == 0) return Encoding.UTF32; if (fileContent[0] == 0 && fileContent[1] == 0 && fileContent[2] == 0xfe && fileContent[3] == 0xff) return Encoding.GetEncoding(12001); String probe; int len = fileContent.Length; if( fileContent.Length >= 128 ) len = 128; probe = Encoding.ASCII.GetString(fileContent, 0, len); MatchCollection mc = Regex.Matches(probe, "^< \\?xml[^<>]*encoding[ \\t\\n\\r]?=[\\t\\n\\r]?['\"]([A-Za-z]([A-Za-z0-9._]|-)*)", RegexOptions.Singleline); // Add '[0].Groups[1].Value' to the end to test regex if( mc.Count == 1 && mc[0].Groups.Count >= 2 ) { // Typically picks up 'UTF-8' string Encoding enc = null; try { enc = Encoding.GetEncoding( mc[0].Groups[1].Value ); }catch (Exception ) { } if( enc != null ) return enc; } return Encoding.ASCII; // Default fallback } 

É o suficiente para ler provavelmente 1024 bytes do arquivo, mas estou carregando o arquivo inteiro.

Se alguém estiver procurando por uma solução de 93,9%. Isso funciona para mim:

 public static class StreamExtension { ///  /// Convert the content to a string. ///  /// The stream. ///  public static string ReadAsString(this Stream stream) { var startPosition = stream.Position; try { // 1. Check for a BOM // 2. or try with UTF-8. The most (86.3%) used encoding. Visit: http://w3techs.com/technologies/overview/character_encoding/all/ var streamReader = new StreamReader(stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true), detectEncodingFromByteOrderMarks: true); return streamReader.ReadToEnd(); } catch (DecoderFallbackException ex) { stream.Position = startPosition; // 3. The second most (6.7%) used encoding is ISO-8859-1. So use Windows-1252 (0.9%, also know as ANSI), which is a superset of ISO-8859-1. var streamReader = new StreamReader(stream, Encoding.GetEncoding(1252)); return streamReader.ReadToEnd(); } } } 

Eu fiz algo semelhante em Python. Basicamente, você precisa de muitos dados de amostra de várias codificações, que são divididas por uma janela deslizante de dois bytes e armazenadas em um dictionary (hash), codificadas em pares de bytes fornecendo valores de listas de codificações.

Dado esse dictionary (hash), você pega seu texto de input e:

  • se começar com qualquer caractere de BOM (‘\ xfe \ xff’ para UTF-16-BE, ‘\ xff \ xfe’ para UTF-16-LE, ‘\ xef \ xbb \ xbf’ para UTF-8 etc), eu trate como sugerido
  • se não, então pegue uma amostra grande o suficiente do texto, pegue todos os pares de bytes da amostra e escolha a codificação que é a menos comum sugerida pelo dictionary.

Se você também tiver amostrado textos codificados em UTF que não iniciam com qualquer BOM, a segunda etapa abrangerá aqueles que caíram da primeira etapa.

Até agora, funciona para mim (os dados de amostra e os dados de input subseqüentes são legendas em vários idiomas) com taxas de erro decrescentes.

O construtor da class StreamReader usa um parâmetro ‘detect encoding’.

A ferramenta “uchardet” faz isso bem usando modelos de distribuição de frequência de caracteres para cada conjunto de caracteres. Arquivos maiores e mais arquivos “típicos” têm mais confiança (obviamente).

No Ubuntu, você acabou apt-get install uchardet .

Em outros sistemas, obtenha a fonte, o uso e a documentação aqui: https://github.com/BYVoid/uchardet

Se você pode vincular a uma biblioteca C, você pode usar libenca . Veja http://cihar.com/software/enca/ . Na página man:

Enca lê arquivos de texto, ou input padrão quando nenhum é dado, e usa o conhecimento sobre seu idioma (deve ser suportado por você) e uma mistura de análise estatística, adivinhação e magia negra para determinar suas codificações.

É a GPL v2.

Tem o mesmo problema, mas ainda não encontrou uma boa solução para detectá-lo automaticamente. Agora estou usando o PsPad (www.pspad.com) para isso;) funciona bem

Como basicamente se trata de heurística, pode ser útil usar a codificação de arquivos recebidos anteriormente da mesma fonte como uma primeira dica.

A maioria das pessoas (ou aplicativos) fazem as coisas quase sempre na mesma ordem, geralmente na mesma máquina, então é bem provável que, quando Bob criar um arquivo .csv e enviá-lo para Mary, ele sempre use o Windows-1252 ou seja qual for o padrão de sua máquina.

Sempre que possível, um pouco de treinamento de clientes nunca é demais 🙂

Eu estava realmente procurando por uma maneira genérica, não de programação, de detectar a codificação de arquivos, mas eu não encontrei isso ainda. O que eu encontrei testando com diferentes codificações foi que meu texto era UTF-7.

Então, onde eu estava fazendo primeiro: StreamReader file = File.OpenText (fullfilename);

Eu tive que alterá-lo para: StreamReader file = new StreamReader (fullfilename, System.Text.Encoding.UTF7);

A OpenText assume que é UTF-8.

Você também pode criar o StreamReader como este novo StreamReader (fullfilename, true), o segundo parâmetro significa que ele deve tentar detectar a codificação do byteordermark do arquivo, mas isso não funcionou no meu caso.

Abra o arquivo no AkelPad (ou apenas copie / cole um texto distorcido), vá em Edit -> Selection -> Recode … -> marque “Autodetect”.

Como complemento ao post do ITmeze, eu usei essa function para converter a saída da porta C # do Mozilla Universal Charset Detector

  private Encoding GetEncodingFromString(string codePageName) { try { return Encoding.GetEncoding(codePageName); } catch { return Encoding.ASCII; } } 

MSDN

Obrigado @ Erik Aronesty por mencionar uchardet .

Enquanto isso, a ferramenta (mesmo?) Existe para o linux: chardet .
Ou, no cygwin você pode querer usar: chardetect .

Veja: chardet man page: https://www.commandlinux.com/man-page/man1/chardetect.1.html

Isso detectará heuristicamente (adivinhar) a codificação de caracteres para cada arquivo fornecido e informará o nome e o nível de confiança da codificação de caracteres detectada de cada arquivo.

10Y (!) Se passaram desde que isso foi perguntado, e ainda não vejo nenhuma menção da solução boa, não-GPL da MS: API IMultiLanguage2 .

A maioria das bibliotecas já mencionadas é baseada no UDE da Mozilla – e parece razoável que os navegadores já tenham lidado com problemas semelhantes. Eu não sei qual é a solução do chrome, mas desde que o IE 5.0 MS liberou o deles, e é:

  1. Livre de problemas de licenciamento GPL e semelhantes,
  2. Apoiado e mantido provavelmente para sempre,
  3. Fornece saída avançada – todos os candidatos válidos para codificação / códigos de página junto com pontuações de confiança,
  4. Surpreendentemente fácil de usar (é uma chamada de function única).

É uma chamada COM nativa, mas aqui está um trabalho muito bom de Carsten Zeumer, que lida com a confusão de interoperabilidade para uso em .net. Existem outros ao redor, mas em geral essa biblioteca não recebe a atenção que merece.

Eu uso esse código para detectar Unicode e página de códigos ansi padrão do Windows ao ler um arquivo. Para outras codificações, é necessário verificar o conteúdo, manualmente ou por programação. Isso pode ser usado para salvar o texto com a mesma codificação de quando foi aberto. (Eu uso vb.net)

 'Works for Default and unicode (auto detect) Dim mystreamreader As New StreamReader(LocalFileName, Encoding.Default) MyEditTextBox.Text = mystreamreader.ReadToEnd() Debug.Print(mystreamreader.CurrentEncoding.CodePage) 'Autodetected encoding mystreamreader.Close()