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


295

Nella nostra applicazione, riceviamo i file di testo ( .txt, .csv, etc.) provenienti da fonti diverse. Durante la lettura, questi file a volte contengono immondizia, poiché i file sono stati creati in una tabella codici diversa / sconosciuta.

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

La detectEncodingFromByteOrderMarks, sul StreamReadercostruttore, lavora per UTF8 e altri file Unicode segnato, ma sto cercando un modo per rilevare le pagine di codice, come ibm850, windows1252.


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

I file che riceviamo provengono da utenti finali, non hanno la minima idea di codici pagina. Anche i destinatari sono utenti finali, questo è quello che sanno sulle tabelle codici: le tabelle codici esistono e sono fastidiose.

Soluzione:

  • Apri il file ricevuto in Blocco note, guarda un pezzo di testo confuso. Se qualcuno si chiama François o qualcosa del genere, con la tua intelligenza umana puoi indovinarlo.
  • Ho creato una piccola app che l'utente può utilizzare per aprire il file e inserire un testo che l'utente sa che verrà visualizzato nel file, quando viene utilizzata la tabella codici corretta.
  • Scorri tutte le tabelle codici e visualizza quelle che forniscono una soluzione con il testo fornito dall'utente.
  • Se viene visualizzata più di una tabella codici, chiedere all'utente di specificare più testo.

Risposte:


260

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

Ad ogni modo, questo è ciò che devi leggere: il minimo assoluto che ogni sviluppatore di software deve assolutamente conoscere positivamente su Unicode e set di caratteri (senza scuse!) .

In particolare Joel dice:

Il singolo fatto più importante sulle codifiche

Se dimentichi completamente tutto ciò che ho appena spiegato, ricorda un fatto estremamente importante. Non ha senso avere una stringa senza sapere quale codifica utilizza. Non puoi più mettere la testa nella sabbia e fingere che il testo "normale" sia ASCII. Non esiste una cosa come il testo normale.

Se hai una stringa, in memoria, in un file o in un messaggio e-mail, devi sapere in quale codifica si trova o non puoi interpretarla o visualizzarla correttamente per gli utenti.


43
Ho votato verso il basso questa risposta per due motivi. In primo luogo, dire che "devi sentirti dire" non è utile. Chi me lo direbbe e attraverso quale mezzo lo farebbe? Se sono quello che ha salvato il file, a chi chiederei? Me stessa? In secondo luogo, l'articolo non è particolarmente utile come risorsa per rispondere alla domanda. L'articolo parla più di una storia di codifica scritta in stile David Sedaris. Apprezzo la narrazione, ma non risponde semplicemente / direttamente alla domanda.
geneorama,

9
@geneorama, penso che l'articolo di Joel affronti le tue domande meglio di quanto potessi mai fare, ma qui va ... Il mezzo dipende sicuramente dall'ambiente in cui il testo è ricevuto. Meglio che il file (o qualunque altra cosa) contenga quell'informazione (sto pensando HTML e XML). In caso contrario, la persona che invia il testo dovrebbe essere autorizzata a fornire tali informazioni. Se tu fossi quello che ha creato il file, come puoi non sapere quale codifica utilizza?
JV.

4
@geneorama, continua ... Infine, suppongo che il motivo principale per cui l'articolo non risponde alla domanda è semplicemente perché non esiste una risposta semplice a quella domanda. Se la domanda fosse "Come posso immaginare ...", avrei risposto diversamente.
JV.

1
@JV In seguito ho appreso che xml / html può specificare la codifica dei caratteri, grazie per aver menzionato quell'utile tidbit.
geneorama,

1
@JV "Crea un file" potrebbe essere una cattiva scelta di parole. Presumo che un utente possa specificare la codifica di un file generato dall'utente. Recentemente ho "creato" un file da un cluster Hadoop usando Hive e l'ho passato a un FTP prima di scaricarlo su vari computer client. Il risultato conteneva un po 'di immondizia unicode, ma non so quale passaggio abbia creato il problema. Non ho mai specificato esplicitamente la codifica. Vorrei poter controllare la codifica ad ogni passaggio.
geneorama,

31

Se stai cercando di rilevare codifiche non UTF (cioè nessuna distinta base), in pratica devi ricorrere all'euristica e all'analisi statistica del testo. Potresti dare un'occhiata al documento di Mozilla sul rilevamento universale dei set di caratteri ( stesso link, con una migliore formattazione tramite Wayback Machine ).


9
Stranamente la mia installazione di Firefox 3.05 rileva quella pagina come UTF-8, mostrando un numero di glifi punto interrogativo in un diamante, sebbene l'origine abbia un meta tag per Windows-1252. La modifica manuale della codifica dei caratteri mostra il documento correttamente.
Devstuff,

5
La frase "Se stai cercando di rilevare codifiche non UTF (ovvero nessuna distinta base)" è leggermente fuorviante; lo standard unicode non raccomanda di aggiungere una DBA ai documenti utf-8! (e questa raccomandazione, o la sua mancanza, è la fonte di molti mal di testa). rif: en.wikipedia.org/wiki/Byte_order_mark#UTF-8
Tao

Questo viene fatto in modo da poter concatenare le stringhe UTF-8 senza accumulare DBA ridondanti. Inoltre, per UTF-8 non è necessario un contrassegno di ordine di byte, a differenza di UTF-16 per esempio.
sashoalm,

26

Hai provato la 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.");
        }
    }
}    

1
Ha funzionato perfettamente per il tipo Windows-1252.
seebiscuit,

E come puoi usarlo per leggere un file di testo su stringa usando quello? CharsetDetector restituisce il nome della codifica in formato stringa e basta ...
Bartosz,

@Bartosz private Encoding GetEncodingFromString(string encoding) { try { return Encoding.GetEncoding(encoding); } catch { return Encoding.ASCII; } }
PrivatePyle

15

Non è possibile rilevare la tabella codici

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

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

Eccone un altro che ho appena trovato usando Google:


39
"euristica" - quindi il browser non lo rileva del tutto, sta facendo un'ipotesi colta. "funziona davvero bene" - quindi non funziona sempre? Mi sembra che siamo d'accordo.
JV.

10
Lo standard per HTML impone che, se il set di caratteri non è definito dal documento, dovrebbe essere considerato codificato come UTF-8.
Jon Trauntvein,

5
Il che è bello a meno che non stiamo leggendo documenti HTML non standard. O documenti non HTML.
Kos,

2
Questa risposta è sbagliata, quindi ho dovuto sottovalutare. Dire che sarebbe falso che non riesci a rilevare la tabella codici, è sbagliato. Puoi indovinare e le tue ipotesi possono essere piuttosto buone, ma non puoi "rilevare" una tabella codici.
z80crew

1
@JonTrauntvein Secondo le specifiche HTML5 a character encoding declaration is required even if the encoding is US-ASCII - una dichiarazione mancante si traduce nell'uso di un algoritmo euristico, non nel ricorrere a UTF8.
z80crew

9

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

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

vantaggi:

  • Rilevazione distinta componenti integrata
  • Codifica predefinita / fallback personalizzabile
  • abbastanza affidabile (secondo la mia esperienza) per file basati sull'Europa occidentale contenenti alcuni dati esotici (ad esempio nomi francesi) con una combinazione di file UTF-8 e in stile latino-1 - fondamentalmente la maggior parte degli ambienti statunitensi ed europei occidentali.

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



7

Alla ricerca di una soluzione diversa, l'ho trovato

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

questa soluzione è piuttosto pesante.

Avevo bisogno di un rilevamento di codifica di base, basato su 4 primi byte e probabilmente sul rilevamento del set di caratteri xml, quindi ho preso un 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

Se qualcuno è alla ricerca di 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();
        }
    }
}

Soluzione molto bella. Si può facilmente avvolgere il corpo di ReadAsString () in un ciclo di codifiche consentite se dovrebbero essere consentite più di 2 codifiche (UTF-8 e ASCI 1252).
ViRuSTriNiTy

Dopo aver provato tonnellate di esempi, finalmente sono arrivato al tuo. Sono in un posto felice in questo momento. lol Grazie !!!!!!!
Sedrick,

Questa potrebbe non essere la risposta a come rilevare 1252 vs 1250, ma dovrebbe assolutamente essere la risposta a "Come rilevare UTF-8" con o senza BOM !!
Chuckc,

4

Ho fatto qualcosa di simile in Python. Fondamentalmente, sono necessari molti dati di esempio da varie codifiche, che sono suddivise da una finestra scorrevole a due byte e memorizzate in un dizionario (hash), digitate su coppie di byte che forniscono valori di elenchi di codifiche.

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

  • se inizia con qualsiasi carattere DBA ('\ 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 è il meno comune suggerito dal dizionario.

Se hai anche campionato i testi con codifica UTF che non iniziano con alcuna DBA, il secondo passaggio riguarderà quelli che sono scivolati dal primo passaggio.

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


4

Lo strumento "uchardet" fa bene usando i modelli di distribuzione della frequenza dei caratteri per ogni set di caratteri. I file più grandi e quelli più "tipici" hanno maggiore sicurezza (ovviamente).

Su Ubuntu, tu solo apt-get install uchardet.

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


Su Mac tramite homebrew:brew install uchardet
Paul B,

3

Il costruttore della classe StreamReader accetta un parametro "rileva codifica".


È solo il link "codifica" qui .. e la descrizione dice che dobbiamo fornire la codifica ..
SurajS

@SurajS: guarda gli altri sovraccarichi.
Leppie,

l'autore originale desidera rilevare la codifica di un file, che potenzialmente non avrebbe il marcatore DBA. StreamReader rileva la codifica dall'intestazione DBA come da firma. public StreamReader (Stream stream, bool detectEncodingFromByteOrderMarks)
ibondre

1

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

Enca legge determinati file di testo o input standard quando non ne viene fornito nessuno e utilizza la conoscenza della loro lingua (deve essere supportata da te) e una combinazione di analisi, analisi statistiche, ipotesi e magia nera per determinare le loro codifiche.

È GPL v2.


0

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

Dato che sostanzialmente si tratta di euristica, potrebbe essere utile utilizzare la codifica di file precedentemente ricevuti dalla stessa fonte come primo suggerimento.

La maggior parte delle persone (o applicazioni) fa cose praticamente nello stesso ordine ogni volta, spesso sullo stesso computer, quindi è molto probabile che quando Bob crea un file .csv e lo invia a Mary, utilizzerà sempre Windows-1252 o qualunque sia l'impostazione predefinita della sua macchina.

Ove possibile, un po 'di formazione del cliente non guasta mai :-)


0

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

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

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

OpenText presuppone che sia UTF-8.

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


@JohnMachin Concordo sul fatto che è raro, ma è obbligatorio ad esempio in alcune parti del protocollo IMAP. Se è lì che sei, non dovresti indovinare, però.
triplo il

0

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


0

Come addon al post 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



-1

Uso questo codice per rilevare la tabella codici ansi predefinita di Unicode e Windows durante la lettura di un file. Per altre codifiche è necessario un controllo del contenuto, manualmente o tramite programmazione. Questo può essere utilizzato per salvare il testo con la stessa codifica di quando è stato aperto. (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()

-1

Erano passati 10 anni (!) Da quando è stato chiesto, e ancora non vedo alcun riferimento alla buona soluzione non GPL di MS: API IMultiLanguage2 .

La maggior parte delle librerie già menzionate si basano sull'UDE di Mozilla e sembra ragionevole che i browser abbiano già affrontato problemi simili. Non so quale sia la soluzione di Chrome, ma da quando IE 5.0 MS ha rilasciato la sua, ed è:

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

Si tratta di una chiamata COM nativa, ma ecco alcuni lavori molto carini di Carsten Zeumer, che gestisce il messaggio di interoperabilità per l'utilizzo di .net. Ce ne sono altri in giro, ma nel complesso questa biblioteca non ottiene l'attenzione che merita.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.