it-swarm.it

Come posso rilevare la codifica/codepage di un file di testo

Nella nostra applicazione, riceviamo file di testo (.txt, .csv, ecc.) Da diverse fonti. Durante la lettura, questi file a volte contengono dati inutili, perché i file creati in una codepage diversa/sconosciuta.

C'è un modo per (automaticamente) rilevare la codepage di un file di testo? 

Il detectEncodingFromByteOrderMarks, nel costruttore StreamReader, funziona con UTF8 e altri file contrassegnati Unicode, ma sto cercando un modo per rilevare le code page, come ibm850, windows1252


Grazie per le tue risposte, questo è quello che ho fatto.

I file che riceviamo provengono dagli utenti finali, non hanno idea delle codepage. I ricevitori sono anche utenti finali, ormai questo è ciò che sanno sulle codepage: esistono codici dei codici e sono fastidiosi.

Soluzione:  

  • Apri il file ricevuto in Blocco note, guarda una parte di testo confusa. Se qualcuno si chiama François o qualcosa del genere, con la tua intelligenza umana puoi immaginarlo.
  • Ho creato una piccola app che l'utente può utilizzare per aprire il file con e inserire un testo che l'utente sa che apparirà nel file, quando viene utilizzata la codepage corretta. 
  • Passare in rassegna tutte le codepage e visualizzare quelle che forniscono una soluzione con il testo fornito dall'utente. 
  • Se viene visualizzato più come una codepage, chiedere all'utente di specificare più testo.
283
GvS

Non è possibile rilevare la tabella codici, è necessario informarlo. È possibile analizzare i byte e indovinarli, ma ciò può dare risultati bizzarri (a volte divertenti). Non riesco a trovarlo ora, ma sono sicuro che il Blocco note può essere indotto a visualizzare testo inglese in cinese. 

Ad ogni modo, questo è quello che devi leggere: Il minimo assoluto Ogni sviluppatore di software deve assolutamente conoscere positivamente Unicode e Set di caratteri (nessuna scusa!) .

Nello specifico, Joel dice:

Il singolo dato più importante sulle codifiche

Se dimentichi completamente tutto ciò che ho appena spiegato, ti preghiamo di ricordare un fatto estremamente importante. Non ha senso avere una stringa senza sapere quale codifica usa. Non puoi più attaccare la testa alla sabbia e fingere che il testo "semplice" sia ASCII . Non c'è nessuna cosa come testo normale.

Se si dispone di una stringa, in memoria, in un file o in un messaggio di posta elettronica, è necessario sapere quale codifica è presente o non è possibile interpretarla o visualizzarla correttamente agli utenti.

255
JV.

Se stai cercando di rilevare le codifiche non UTF (cioè senza BOM), sei fondamentalmente verso il basso per l'euristica e l'analisi statistica del testo. Potresti dare un'occhiata al documento Mozilla sul rilevamento charset universale ( stesso link, con una migliore formattazione tramite Wayback Machine ).

30
Tomer Gabel

Hai provato Porta C # per Mozilla Universal Charset Detector

Esempio da 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.");
        }
    }
}    
21
ITmeze

Non è possibile rilevare la tabella codici

Questo è chiaramente falso. Ogni browser Web ha una sorta di rilevatore di caratteri universale per trattare pagine che non hanno alcuna indicazione di codifica. Firefox ne ha uno. Puoi scaricare il codice e vedere come lo fa. Vedi qualche documentazione qui . Fondamentalmente, è un euristico, ma funziona davvero bene.

Data una quantità ragionevole di testo, è persino possibile rilevare la lingua.

Eccone un altro Ho appena trovato usando Google:

15
shoosh

So che è molto tardi per questa domanda e questa soluzione non piacerà ad alcuni (a causa del suo bias incentrato sull'inglese e della mancanza di test statistici/empirici), ma ha funzionato molto bene per me, soprattutto per l'elaborazione dei dati CSV caricati:

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

Vantaggi:

  • Rilevamento delle BOM integrato
  • Codifica predefinita/fallback personalizzabile
  • abbastanza affidabile (nella mia esperienza) per i file basati sull'Europa occidentale che contengono alcuni dati esotici (ad esempio nomi francesi) con una combinazione di file UTF-8 e di stile Latin-1 - fondamentalmente la maggior parte degli ambienti statunitensi e dell'Europa occidentale.

Nota: sono io quello che ha scritto questa lezione, quindi ovviamente prendilo con un pizzico di sale! :)

8
Tao

Alla ricerca di una soluzione diversa, l'ho trovato 

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

questa soluzione è piuttosto pesante.

Avevo bisogno di alcuni rilevamenti di codifica di base, basati su 4 primi byte e probabilmente il rilevamento di charset xml - quindi ho preso qualche codice sorgente di esempio da internet e ho aggiunto una versione leggermente modificata di

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

scritto per 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
    }

È sufficiente leggere probabilmente i primi 1024 byte dal file, ma sto caricando l'intero file.

7
TarmoPikaro

Notepad ++ ha questa funzionalità pronta all'uso. Supporta anche cambiarlo.

7
hegearon

Se qualcuno sta cercando una soluzione al 93,9%. Questo funziona per me:

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

Ho fatto qualcosa di simile in Python. Fondamentalmente, sono necessari molti dati di esempio da varie codifiche, che sono suddivisi da una finestra scorrevole a due byte e memorizzati in un dizionario (hash), con chiave su coppie di byte che forniscono valori di liste di codifiche.

Dato quel dizionario (hash), prendi il tuo testo di input e:

  • se inizia con qualsiasi carattere BOM ('\ xfe\xff' per UTF-16-BE, '\ xff\xfe' per UTF-16-LE, '\ xef\xbb\xbf' per UTF-8 ecc., I trattalo come suggerito
  • in caso contrario, prendi un campione abbastanza grande del testo, prendi tutte le coppie di byte del campione e scegli la codifica che è la meno comune suggerita dal dizionario.

Se hai anche campionato testi con codifica UTF che fanno non inizia con una distinta base, il secondo passaggio coprirà quelli che sono scivolati dal primo passaggio.

Finora, funziona per me (i dati di esempio e i dati di input successivi sono sottotitoli in varie lingue) con tassi di errore decrescenti.

4
tzot

Lo strumento "uchardet" funziona bene utilizzando i modelli di distribuzione della frequenza dei caratteri per ogni set di caratteri. File più grandi e più file "tipici" hanno più fiducia (ovviamente).

Su ubuntu, hai solo apt-get install uchardet

Su altri sistemi, procurati la fonte, l'utilizzo e i documenti qui: https://github.com/BYVoid/uchardet

3
Erik Aronesty

Il costruttore della classe StreamReader accetta un parametro 'detect encoding'.

3
leppie

Se è possibile collegare a una libreria C, è possibile utilizzare libenca. Vedi http://cihar.com/software/enca/ . Dalla pagina man:

Enca legge i file di testo dati o lo standard input quando non ne viene dato nessuno, e usa la conoscenza della loro lingua (deve essere supportata da te) e una miscela di analisi, analisi statistica, ipotesi e magia nera per determinare le loro codifiche.

È GPL v2.

1
Kundor

Ho avuto lo stesso problema ma non ho ancora trovato una buona soluzione per rilevarlo automaticamente . Ora sto usando PsPad (www.pspad.com) per quello;) Funziona bene

0
DeeCee

Grazie @ Erik Aronesty per aver menzionato uchardet.

Nel frattempo esiste lo stesso (lo stesso?) Strumento per linux: chardet.
Oppure, su cygwin puoi usare: chardetect.

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

Ciò rileva euristicamente (indovina) la codifica dei caratteri per ogni dato file e riporta il nome e il livello di confidenza per la codifica dei caratteri rilevata di ogni file.

0
Schlacki

In realtà stavo cercando un modo generico, non di programmazione per rilevare la codifica dei file, ma non l'ho ancora trovato . Ciò che ho trovato testando con diverse codifiche era che il mio testo era UTF-7.

Quindi, dove prima stavo facendo: StreamReader file = File.OpenText (fullfilename);

Ho dovuto cambiarlo in: StreamReader file = new StreamReader (fullfilename, System.Text.Encoding.UTF7);

OpenText presume che sia UTF-8.

puoi anche creare lo StreamReader come thisnew StreamReader (fullfilename, true), il secondo parametro che significa che dovrebbe provare a rilevare la codifica dal byteordermark del file, ma che nel mio caso non ha funzionato.

0
Intraday Tips

Come addon al post di ITmeze, ho usato questa funzione per convertire l'output della porta C # per Mozilla Universal Charset Detector

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

MSDN

0
PrivatePyle

Apri il file in AkelPad (o copia/incolla semplicemente un testo confuso), vai su Modifica -> Selezione -> Ricodifica ... -> seleziona "Rileva automaticamente".

0
plavozont

Poiché fondamentalmente si riduce all'euristica, può essere utile utilizzare la codifica dei file ricevuti in precedenza dalla stessa origine di un primo suggerimento.

La maggior parte delle persone (o delle applicazioni) fa più o meno lo stesso ordine ogni volta, spesso sulla stessa macchina, quindi è abbastanza probabile che quando Bob crea un file .csv e lo manda a Mary userà sempre Windows-1252 o qualunque sia la sua macchina predefinita.

Ove possibile, un po 'di allenamento per i clienti non guasta mai :-)

0
devstuff

10Y (!) Era passato da quando questo è stato chiesto, e ancora non vedo alcuna menzione della buona soluzione di MS, non GPL: IMultiLanguage2 API.

La maggior parte delle librerie già menzionate sono basate su UDE di Mozilla - e sembra ragionevole che i browser abbiano già affrontato problemi simili. Non so quale sia la soluzione di Chrome, ma dal momento che IE 5.0 MS hanno rilasciato il loro, ed è:

  1. Privo di problemi di licenza GPL e simili,
  2. Sostenuto e mantenuto probabilmente per sempre,
  3. Fornisce un output ricco: tutti i candidati validi per codifiche/codepage insieme a punteggi di confidenza,
  4. Sorprendentemente facile da usare (è una chiamata a funzione singola).

È una chiamata COM nativa, ma ecco un lavoro molto bello di Carsten Zeumer, che gestisce il pasticcio di interfaccie per l'utilizzo di .net. Ce ne sono altri in giro, ma nel complesso questa biblioteca non ottiene l'attenzione che merita.

0
Ofek Shilon