2009-03-19 19 views
6

Sto leggendo i file in vari formati e lingue e attualmente sto utilizzando una piccola libreria di codifica per tentare di rilevare la codifica corretta (http://www.codeproject.com/KB/recipes/DetectEncoding.aspx).C#: Cycle through encodings

È abbastanza buono, ma a volte manca ancora. (File multilingue)

La maggior parte dei miei potenziali utenti ha una comprensione molto limitata della codifica (il meglio che posso sperare è "ha qualcosa a che fare con i caratteri") ed è molto improbabile che sia in grado di scegliere la giusta codifica in una lista, quindi mi piacerebbe lasciarli scorrere attraverso diverse codifiche fino a trovare quello giusto semplicemente cliccando su un pulsante.

Problemi di visualizzazione? Clicca qui per provare una codifica diversa! (Beh, questo è comunque il concetto)

Quale sarebbe il modo migliore per implementare qualcosa del genere?


Modifica: Sembra che non mi sia espresso abbastanza chiaramente. "Ciclando attraverso la codifica", non intendo "come eseguire il loop delle codifiche?"

Quello che intendevo era "come consentire all'utente di provare diverse codifiche in sequenza senza ricaricare il file?"

L'idea è più simile a questa: diciamo che il file è caricato con la codifica errata. Vengono visualizzati alcuni caratteri strani. L'utente farebbe clic sul pulsante "Codifica successiva" o "codifica precedente" e la stringa verrebbe convertita in una codifica diversa. L'utente deve solo continuare a fare clic finché non viene trovata la codifica corretta. (qualunque codifica appaia valida per l'utente andrà bene). Finché l'utente può fare clic su "Avanti", ha una ragionevole possibilità di risolvere il suo problema.

Quello che ho trovato finora comporta la conversione della stringa in byte utilizzando la codifica corrente, quindi la conversione dei byte nella prossima codifica, la conversione di quei byte in caratteri, quindi la conversione del carattere in una stringa .... Doable, ma I mi chiedo se non c'è un modo più semplice per farlo.

Ad esempio, se esistesse un metodo che leggesse una stringa e la restituisse utilizzando una codifica diversa, qualcosa come "render (string, encoding)".


Grazie mille per le risposte!

+0

Tecnicamente, un file con codifica UTF-8 non ha bisogno di un BOM. È persino scoraggiato, poiché interferirà con le applicazioni che prevedono che i dati ASCII inizino con determinati caratteri, come "#!" all'inizio degli script di shell Unix. –

risposta

14

Leggere il file come byte e utilizzare quindi il metodo Encoding.GetString.

 byte[] data = System.IO.File.ReadAllBytes(path); 

     Console.WriteLine(Encoding.UTF8.GetString(data)); 
     Console.WriteLine(Encoding.UTF7.GetString(data)); 
     Console.WriteLine(Encoding.ASCII.GetString(data)); 

Quindi è necessario caricare il file solo una volta.È possibile utilizzare ogni codifica in base ai byte originali del file. L'utente può selezionare quello corretto e puoi usare il risultato di Encoding.GetEncoding (...). GetString (dati) per ulteriori elaborazioni.

0

Si può consentire all'utente di immettere alcune parole (con caratteri "speciali") che si suppone debbano verificarsi nel file?

È possibile cercare personalmente tutte le codifiche per vedere se queste parole sono presenti.

0

Attenzione al famigerato "Notepad bug". Ti morderà tutto ciò che proverai, anche se ... Puoi trovare qualche good discussions sulle codifiche e le loro sfide su MSDN (e in altri luoghi).

0

È necessario conservare i dati originali come una matrice di byte o MemoryStream che è possibile convertire nella nuova codifica, dopo aver convertito i dati in una stringa non è possibile tornare alla rappresentazione originale in modo affidabile.

0

Che ne dite di qualcosa di simile:

public string LoadFile(string path) 
{ 
    stream = GetMemoryStream(path);  
    string output = TryEncoding(Encoding.UTF8); 
} 

public string TryEncoding(Encoding e) 
{ 
    stream.Seek(0, SeekOrigin.Begin) 
    StreamReader reader = new StreamReader(stream, e); 
    return reader.ReadToEnd(); 
} 

private MemoryStream stream = null; 

private MemorySteam GetMemoryStream(string path) 
{ 
    byte[] buffer = System.IO.File.ReadAllBytes(path); 
    return new MemoryStream(buffer); 
} 

Uso LoadFile sul vostro primo tentativo; quindi utilizzare TryEncoding successivamente.

+0

Non è necessario reimpostare il flusso di memoria, ad esempio stream.Seek (0, SeekOrigin.Begin)? –

+0

Probabilmente - l'ho scritto solo nel blocco note :) –

4

(risposta iniziale rimosso aggiornamento seguente domanda)

Ad esempio, se ci fosse un metodo che leggere una stringa e restituirlo utilizzando una codifica diversa, qualcosa tipo "rendering (stringa, codifica)".

Non penso che sia possibile riutilizzare i dati di stringa. Il fatto è: se la codifica era sbagliata, questa stringa può essere considerata corrotta. Può facilmente contenere parole senza senso tra i personaggi dall'aspetto probabile. In particolare, molte codifiche potrebbero perdonare la presenza/assenza di una BOM/preambolo, ma dovresti ricodificare con essa? Senza esso?

Se siete felici di rischiare (non vorrei essere), si può solo ri-codificare la stringa locale con l'ultima codifica:

// I DON'T RECOMMEND THIS!!!! 
byte[] preamble = lastEncoding.GetPreamble(), 
    content = lastEncoding.GetBytes(text); 
byte[] raw = new byte[preamble.Length + content.Length]; 
Buffer.BlockCopy(preamble, 0, raw, 0, preamble.Length); 
Buffer.BlockCopy(content, 0, raw, preamble.Length, content.Length); 
text = nextEncoding.GetString(raw); 

In realtà, credo che il meglio che puoi fare è quello di mantenere l'originale byte[] - continuare a offrire diversi rendering (tramite diverse codifiche) fino a quando non lo gradiscono. Qualcosa del tipo:

using System; 
using System.IO; 
using System.Text; 
using System.Windows.Forms; 
class MyForm : Form { 
    [STAThread] 
    static void Main() { 
     Application.EnableVisualStyles(); 
     Application.Run(new MyForm()); 
    } 
    ComboBox encodings; 
    TextBox view; 
    Button load, next; 
    byte[] data = null; 

    void ShowData() { 
     if (data != null && encodings.SelectedIndex >= 0) { 
      try { 
       Encoding enc = Encoding.GetEncoding(
        (string)encodings.SelectedValue); 
       view.Text = enc.GetString(data); 
      } catch (Exception ex) { 
       view.Text = ex.ToString(); 
      } 
     } 
    } 
    public MyForm() { 
     load = new Button(); 
     load.Text = "Open..."; 
     load.Dock = DockStyle.Bottom; 
     Controls.Add(load); 

     next = new Button(); 
     next.Text = "Next..."; 
     next.Dock = DockStyle.Bottom; 
     Controls.Add(next); 

     view = new TextBox(); 
     view.ReadOnly = true; 
     view.Dock = DockStyle.Fill; 
     view.Multiline = true; 
     Controls.Add(view); 

     encodings = new ComboBox(); 
     encodings.Dock = DockStyle.Bottom; 
     encodings.DropDownStyle = ComboBoxStyle.DropDown; 
     encodings.DataSource = Encoding.GetEncodings(); 
     encodings.DisplayMember = "DisplayName"; 
     encodings.ValueMember = "Name"; 
     Controls.Add(encodings); 

     next.Click += delegate { encodings.SelectedIndex++; }; 

     encodings.SelectedValueChanged += delegate { ShowData(); }; 

     load.Click += delegate { 
      using (OpenFileDialog dlg = new OpenFileDialog()) { 
       if (dlg.ShowDialog(this)==DialogResult.OK) { 
        data = File.ReadAllBytes(dlg.FileName); 
        Text = dlg.FileName; 
        ShowData(); 
       } 
      } 
     }; 
    } 
}