Pergunta

Em nossa aplicação, nós recebemos arquivos de texto (.txt, .csv, etc.) a partir de diversas fontes. Ao ler, esses arquivos contêm por vezes lixo, porque os arquivos onde criados em uma página de código diferente / desconhecido.

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

O detectEncodingFromByteOrderMarks, no construtor StreamReader, 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, é isso que eu fiz.

Os arquivos que recebemos são dos usuários finais, eles não têm uma pista sobre páginas de código. Os receptores são também utilizadores finais, por agora isso é o que eles sabem sobre páginas de código:. Codepages existem, e são irritantes

Solução:

  • Abra o arquivo recebido no bloco de notas, olhar para um pedaço ilegível de texto. Se alguém é chamado François ou algo assim, com a sua inteligência humana você pode adivinhar isso.
  • Eu criei um pequeno aplicativo que o usuário pode usar para abrir o arquivo com e digite um texto que o usuário sabe que vai aparecer no arquivo, quando a página de código correto é usado.
  • loop por todos os páginas de código e exibir os que dão uma solução com o texto do usuário fornecido.
  • Se mais como uma página de código aparece, perguntar ao usuário especificar mais texto.
Foi útil?

Solução

Você não pode detectar a página de códigos, você precisa ser dito isso. Você pode analisar os bytes e acho que, mas que pode dar alguns resultados bizarros (por vezes divertidas). Eu não posso encontrá-lo agora, mas tenho certeza que o Bloco de notas pode ser enganado e exibição de texto Inglês em chinês.

De qualquer forma, isso é o que você precisa ler: O desenvolvedor Absolute Minimum cada Software Absolutamente, Positivamente Precisa Saber Sobre Unicode e Conjuntos de caracteres (Sem Desculpas!) .

Especificamente Joel diz:

o único e mais importante fato sobre Encodings

Se você esquecer completamente tudo o que eu acabei de explicar, por favor, lembre-se um fato extremamente importante. Não faz sentido ter uma seqüência sem saber que codificação que utiliza. Você não pode enfiar a cabeça na areia e fingir que o texto "simples" é ASCII. Não existe tal coisa como texto simples.

Se você tem uma corda, na memória, em um arquivo ou em uma mensagem de e-mail, você tem que saber que codificação é ou você não pode interpretá-lo ou exibi-lo aos usuários corretamente.

Outras dicas

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

Você já tentou C # porta 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.");
        }
    }
}    

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

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

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

Aqui está mais um eu só encontrei usando Google:

Eu sei que é muito tarde para esta questão e esta solução não vai apelar para alguns (por causa de seu viés Inglês-centric e sua falta de testes estatísticos / empírica), mas funcionou muito bem para mim, especialmente para processamento carregado dados CSV:

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

Vantagens:

  • detecção BOM built-in
  • Padrão / fallback de codificação personalizada
  • bastante confiável (na minha experiência) para oeste-europeia baseada arquivos contendo alguns dados exóticos (por exemplo, nomes franceses) com uma mistura de arquivos UTF-8 e 1-estilo Latina - basicamente, a maior parte dos ambientes da Europa Ocidental EUA e .

Nota: Eu sou o único que escreveu essa classe, por isso, obviamente, levá-la com um grão de sal! :)

Notepad ++ tem esta característica out-of-the-box. Ele também suporta mudá-lo.

À procura de solução diferente, descobri que

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

esta solução é meio pesado.

Eu precisava de algum detecção básica de codificação, baseado em 4 primeiros bytes e provavelmente xml charset detecção - Então eu tomei algum código fonte de amostra de internet e acrescentou versão ligeiramente modificada

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 primeiros 1024 bytes de arquivo, mas eu estou carregando arquivo inteiro.

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();
        }
    }
}

Eu tenho feito algo semelhante em Python. Basicamente, você precisa de grandes quantidades de dados de amostras de várias codificações, que são discriminados por uma janela de dois bytes deslizante e armazenados em um dicionário (haxixe), fechado em bytes pares fornecendo valores de listas de codificações.

Dado que dicionário (haxixe), você levar o seu texto de entrada e:

  • se ele começa com qualquer personagem BOM ( '\ xfe \ xff' para UTF-16-BE, '\ xff \ xfe' para UTF-16-LE, '\ XEF \ xbb \ XBF' para UTF-8 etc ), eu tratá-lo como sugerido
  • Se não, em seguida, tomar uma amostra grande o suficiente do texto, todas as byte-pares de amostra e escolha a codificação que é o menos comum sugeriu do dicionário.

Se você também já experimentou UTF codificado textos que fazem não começar com qualquer BOM, o segundo passo irá abranger aqueles que escorregou da primeira etapa.

Até agora, ele funciona para mim (os dados de amostras e dados de entrada subseqüentes são legendas em várias línguas), com a diminuição das taxas de erro.

O construtor da classe StreamReader tem um parâmetro 'Detectar codificação'.

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

No Ubuntu, você só apt-get install uchardet.

Em outros sistemas, obter o código fonte, uso e docs aqui: https://github.com/BYVoid/uchardet

Se você pode conectar-se a uma biblioteca C, você pode usar libenca. Consulte http://cihar.com/software/enca/ . A partir da página man:

Enca lê arquivos de texto indicadas ou entrada padrão quando nenhum são dadas, e usa o conhecimento sobre a sua língua (deve ser apoiado por você) e uma mistura de análise, a análise estatística, supondo e mágica preto para determinar suas codificações.

É de GPL v2.

Got o mesmo problema, mas não encontrou uma solução boa ainda para detectá-lo automaticamente. Agora im usando PSPad (www.pspad.com) para isso;) Funciona bem

Uma vez que basicamente se resume a heurística, ele pode ajudar a usar a codificação de arquivos anteriormente recebidos da mesma fonte como um primeiro sinal.

A maioria das pessoas (ou aplicações) fazer coisas em praticamente da mesma ordem a cada vez, muitas vezes na mesma máquina, de modo que a sua muito provável que quando Bob cria um arquivo .csv e envia para Mary que vai sempre estar usando o Windows -1252 ou o que seus padrões da máquina a.

Onde pouco um possível de treinamento do cliente nunca é demais, quer: -)

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

Então, onde eu primeiro foi fazendo: arquivo StreamReader = File.OpenText (FullFileName);

Eu tive que mudá-lo para: StreamReader arquivo = new StreamReader (FullFileName, System.Text.Encoding.UTF7);

OpenText assume-lo de UTF-8.

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

Abrir arquivo no AkelPad (ou apenas copiar / colar um texto ilegível), vá para Editar -> Selection -> Recode ... -.> Seleção "Autodetect"

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

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

MSDN

Obrigado @ Erik Aronesty para mencionar uchardet.
Enquanto isso existe o (mesmo?) Ferramenta para linux:. chardet
Ou, em cygwin você pode querer usar:. chardetect

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

Isto irá heurística detectar (suposição) a codificação de caracteres para cada arquivo dado e irá relatar o nome e nível de confiança para codificação de caracteres detectado de cada arquivo.

10Y se passaram desde que este foi pedido, e ainda não vejo nenhuma menção de boa do MS, a solução não-GPL (!): IMultiLanguage2 API.

A maioria das bibliotecas já mencionadas são baseadas em UDE da Mozilla - e parece razoável que os navegadores já abordou problemas semelhantes. Eu não sei o que é a solução de cromo, mas desde IE 5.0 MS lançaram deles, e é:

  1. Livre de GPL-and-a-like problemas de licenciamento,
  2. Backed e mantida provavelmente para sempre,
  3. Dá saída rico - todos os candidatos válidos para codificação / codepages juntamente com dezenas de confiança,
  4. Surpreendentemente fácil de usar (é uma única chamada de função).

É uma chamada COM nativa, mas aqui vai uma trabalho muito bom por Carsten Zeumer, que lida com a bagunça de interoperabilidade para uso .net. Existem alguns outros ao redor, mas em geral esta biblioteca não recebe a atenção que merece.

Eu uso este código para detectar Unicode e janelas padrão ANSI página de código ao ler um arquivo. Para outras codificações é necessária uma verificação de conteúdo, manualmente ou por programação. Isso pode De usado para salvar o texto com a mesma codificação como 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()
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top