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

295

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

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

A detectEncodingFromByteOrderMarks, no StreamReaderconstrutor, trabalha para UTF8 e outros arquivos unicode marcada, mas eu estou procurando uma maneira de detectar páginas de código, como ibm850, windows1252.


Obrigado por suas respostas, foi o que eu fiz.

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

Solução:

  • Abra o arquivo recebido no bloco de notas, observe um pedaço de texto ilegível. Se alguém se chama François ou algo assim, com sua inteligência humana, você pode adivinhar.
  • Criei um aplicativo pequeno que o usuário pode usar para abrir o arquivo e insira um texto que ele saiba que aparecerá no arquivo, quando a página de código correta for usada.
  • Passe por todas as páginas de código e exiba as que fornecem uma solução com o texto fornecido pelo usuário.
  • Se aparecer mais de uma página de código, peça ao usuário para especificar mais texto.
GvS
fonte

Respostas:

260

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

De qualquer forma, é isso que você precisa ler: O Mínimo Absoluto Todo desenvolvedor de software deve saber absolutamente, positivamente, sobre Unicode e conjuntos de caracteres (sem desculpas!) .

Joel diz especificamente:

O fato mais importante sobre codificações

Se você esquecer completamente tudo o 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 existe nada como texto sem formatação.

Se você possui uma string, na memória, em um arquivo ou em uma mensagem de email, precisa saber em que codificação está ou não pode interpretá-la ou exibi-la aos usuários corretamente.

JV.
fonte
43
Eu votei esta resposta por duas razões. Primeiro, dizer que "você precisa ser informado" não é útil. Quem me diria e com que meio eles o fariam? Se sou eu quem salvou o arquivo, quem eu perguntaria? Eu mesmo? Segundo, o artigo não é especialmente útil como recurso para responder à pergunta. O artigo é mais uma história de codificação escrita no estilo David Sedaris. Agradeço a narrativa, mas ela simplesmente não responde diretamente à pergunta.
geneorama
9
@geneorama, acho que o artigo de Joel aborda suas perguntas melhor do que eu jamais poderia, mas aqui vai ... O meio certamente depende do ambiente em que o texto é recebido. Melhor que o arquivo (ou o que seja) contenha essas informações (estou pensando em HTML e XML). Caso contrário, a pessoa que enviar o texto deve poder fornecer essas informações. Se você foi quem criou o arquivo, como pode não saber qual codificação ele usa?
JV.
4
@geneorama, continuação ... Finalmente, suponho que o principal motivo pelo qual o artigo não responda à pergunta seja simplesmente porque não há uma resposta simples para essa pergunta. Se a pergunta fosse "Como posso adivinhar ...", teria respondido de maneira diferente.
JV.
1
Mais tarde, aprendi que o xml / html pode especificar a codificação de caracteres, obrigado por mencionar esse boato útil.
geneorama
1
@JV "Criar um arquivo" pode ser uma má escolha de palavras. Presumo que um usuário possa especificar a codificação de um arquivo que o usuário gera. Recentemente, "criei" um arquivo a partir de um Hadoop Cluster usando o Hive e o passei para um FTP antes de baixá-lo para várias máquinas clientes. O resultado continha algum lixo unicode, mas não sei qual etapa criou o problema. Eu nunca especifiquei explicitamente a codificação. Eu gostaria de poder verificar a codificação em cada etapa.
geneorama
31

Se você deseja detectar codificações que não sejam UTF (ou seja, sem BOM), é basicamente uma análise heurística e estatística do texto. Você pode dar uma olhada no documento da Mozilla sobre detecção universal de charset ( mesmo link, com melhor formatação via Wayback Machine ).

Tomer Gabel
fonte
9
Curiosamente, minha instalação do Firefox 3.05 detecta essa página como UTF-8, mostrando vários glifos de ponto de interrogação em um diamante, embora a fonte tenha uma metatag para Windows-1252. Alterar manualmente a codificação de caracteres mostra o documento corretamente.
Devstuff 29/12/08
5
Sua frase "Se você deseja detectar codificações não UTF (ou seja, sem BOM)" é um pouco enganadora; o padrão unicode não recomenda adicionar uma lista técnica aos documentos utf-8! (e esta recomendação, ou a falta dela, é a fonte de muitas dores de cabeça). ref: en.wikipedia.org/wiki/Byte_order_mark#UTF-8
Tao
Isso é feito para que você possa concatenar cadeias UTF-8 sem acumular listas técnicas redundantes. Além disso, uma marca de ordem de bytes não é necessária para o UTF-8, ao contrário do UTF-16, por exemplo.
Sashoalm
26

Você já tentou a porta C # para o 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.");
        }
    }
}    
ITmeze
fonte
1
Funcionou perfeitamente para o tipo Windows-1252.
seebiscuit
E como você pode usá-lo para ler um arquivo de texto para string usando isso? CharsetDetector retorna o nome da codificação no formato de cadeia e é isso ...
Bartosz
@Bartosz private Encoding GetEncodingFromString(string encoding) { try { return Encoding.GetEncoding(encoding); } catch { return Encoding.ASCII; } }
PrivatePyle
15

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

Isto é claramente falso. Todo navegador da web possui algum tipo de detector de conjunto de caracteres universal 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 o faz. Veja alguma documentação aqui . Basicamente, é uma heurística, mas que funciona muito bem.

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

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

shoosh
fonte
39
"heurística" - para que o navegador não a detecte, está fazendo um palpite. "funciona muito bem" - então não funciona o tempo todo, então? Parece-me que estamos de acordo.
JV.
10
O padrão para HTML determina que, se o conjunto de caracteres não for definido pelo documento, ele deverá ser considerado codificado como UTF-8.
Jon Trauntvein
5
O que é legal, a menos que estejamos lendo documentos HTML fora do padrão. Ou documentos não HTML.
Kos
2
Esta resposta está errada, então eu tive que votar. Dizer que seria falso que você não pode detectar a página de códigos está errado. Você pode adivinhar e suas suposições podem ser bastante boas, mas não pode "detectar" uma página de código.
Z80crew 3/04
1
@ JonTrauntvein De acordo com as especificações do HTML5a character encoding declaration is required even if the encoding is US-ASCII - uma falta de declaração resulta no uso de um algoritmo heurístico, e não no retorno ao UTF8.
Z80crew 03/04
9

Sei que é muito tarde para esta pergunta e esta solução não agradará a alguns (devido ao seu viés centrado no inglês e à falta de testes estatísticos / empíricos), mas funcionou muito bem para mim, especialmente para o processamento de dados CSV enviados:

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

Vantagens:

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

Nota: Fui eu quem escreveu esta aula, então, obviamente, leve-a com um pouco de sal! :)

Tao
fonte
7

O Notepad ++ possui esse recurso pronto para uso. Ele também suporta a alteração.

hegearon
fonte
7

Procurando uma solução diferente, descobri que

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

essa solução é meio pesada.

Eu precisava de alguma detecção básica de codificação, com base nos 4 primeiros bytes e provavelmente na detecção de conjunto de caracteres xml - por isso, peguei um código-fonte de amostra da Internet e adicionei uma versão ligeiramente modificada do

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
    }

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

TarmoPikaro
fonte
7

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

public static class StreamExtension
{
    /// <summary>
    /// Convert the content to a string.
    /// </summary>
    /// <param name="stream">The stream.</param>
    /// <returns></returns>
    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();
        }
    }
}
Magu
fonte
Solução muito boa. É possível agrupar facilmente o corpo de ReadAsString () em um loop de codificações permitidas, se mais de 2 codificações (UTF-8 e ASCI 1252) forem permitidas.
ViRuSTriNiY
Depois de tentar vários exemplos, finalmente cheguei ao seu. Estou em um lugar feliz agora. lol Obrigado !!!!!!!
Sedrick
Esta pode não ser a resposta para como detectar 1252 vs 1250, mas deve ser absolutamente a resposta para "Como detectar UTF-8" com ou sem uma lista técnica !!
Chuckc 11/11/19
4

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 dicionário (hash), digitado em pares de bytes, fornecendo valores das listas de codificações.

Dado esse dicionário (hash), você pega seu texto de entrada e:

  • se iniciar com qualquer caractere de lista técnica ('\ xfe \ xff' para UTF-16-BE, '\ xff \ xfe' para UTF-16-LE, '\ xef \ xbb \ xbf' para UTF-8 etc), I trate-o como sugerido
  • caso contrário, pegue uma amostra grande o suficiente do texto, pegue todos os pares de bytes da amostra e escolha a codificação menos comum sugerida no dicionário.

Se você também já experimentou textos codificados em UTF que não iniciam com nenhuma BOM, a segunda etapa cobrirá as que foram retiradas da primeira etapa.

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

tzot
fonte
4

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

No ubuntu, você apenas apt-get install uchardet.

Em outros sistemas, obtenha a fonte, o uso e os documentos aqui: https://github.com/BYVoid/uchardet

Erik Aronesty
fonte
No Mac via homebrew:brew install uchardet
Paul B
3

O construtor da classe StreamReader usa um parâmetro 'detect encoding'.

leppie
fonte
É só "codificação" ligação aqui .. ea descrição diz que temos de fornecer a codificação ..
SurajS
@ Surata: Olhe para as outras sobrecargas.
leppie
o autor original deseja detectar a codificação de um arquivo que potencialmente não teria o marcador de lista técnica. O StreamReader detecta a codificação do cabeçalho da BOM conforme a assinatura. public StreamReader (fluxo de fluxo, detecção de booleanoEncodingFromByteOrderMarks)
ibondre
1

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

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

É a GPL v2.

Nick Matteo
fonte
0

Obteve 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

DeeCee
fonte
0

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

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

Sempre que possível, um pouco de treinamento do cliente também não prejudica :-)

devstuff
fonte
0

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

Então, onde eu estava trabalhando pela primeira vez: StreamReader file = File.OpenText (fullfilename);

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

O OpenText assume que é UTF-8.

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

Dicas intraday
fonte
@JohnMachin Concordo que é raro, mas é obrigatório, por exemplo, em algumas partes do protocolo IMAP. Se é onde você está, você não precisaria adivinhar.
Tripleee
0

Abra o arquivo no AkelPad (ou apenas copie / cole um texto ilegível), vá em Editar -> Seleção -> Recodificar ... -> marque "Detecção automática".

plavozont
fonte
0

Como complemento para a publicação do ITmeze, usei essa função para converter a saída da porta C # para o Mozilla Universal Charset Detector

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

MSDN

PrivatePyle
fonte
0

Obrigado @ Erik Aronesty por mencionar uchardet.

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

Consulte: página do manual chardet: https://www.commandlinux.com/man-page/man1/chardetect.1.html

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

Schlacki
fonte
-1

Eu uso esse código para detectar Unicode e a página de código ansi padrão do Windows ao ler um arquivo. Para outras codificações, é necessária uma verificação do 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 o 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()
Thommy Johansson
fonte
-1

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

A maioria das bibliotecas já mencionadas é baseada no UDE da Mozilla - e parece razoável que os navegadores já tenham enfrentado problemas semelhantes. Não sei qual é a solução do chrome, mas desde o IE 5.0 MS os lançamos, e é:

  1. Livre de problemas de licenciamento GPL e similares,
  2. Apoiado e mantido provavelmente para sempre,
  3. Oferece resultados avançados - todos os candidatos válidos para páginas de codificação / código, além de pontuações de confiança,
  4. Surpreendentemente fácil de usar (é uma única chamada de função).

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

Ofek Shilon
fonte