2009-03-19 14 views
6

estoy leyendo archivos en varios formatos e idiomas y actualmente estoy usando una pequeña biblioteca de codificación para tomar intento de detectar la codificación correcta (http://www.codeproject.com/KB/recipes/DetectEncoding.aspx).C#: Ciclo a través de codificaciones

Es bastante bueno, pero todavía falla de vez en cuando. (Archivos multilingües)

La mayoría de mis usuarios potenciales tienen muy poca comprensión de la codificación (lo mejor que puedo esperar es "tiene algo que ver con los caracteres") y es poco probable que puedan elegir la codificación correcta en una lista, por lo que me gustaría dejar que pasen por diferentes codificaciones hasta encontrar la correcta con solo hacer clic en un botón.

¿Problemas en la pantalla? ¡Haga clic aquí para probar una codificación diferente! (Bueno, eso es el concepto de todos modos)

¿Cuál sería la mejor manera de poner en práctica algo por el estilo?


Editar: Parece que no me expresé con la suficiente claridad. Al "recorrer la codificación", no me refiero a "¿cómo recorrer las codificaciones?"

Lo que quería decir "cómo hacer que el usuario intente diferentes codificaciones en secuencia sin volver a cargar el archivo?"

La idea es más como esto: Digamos que el archivo está cargado con la codificación incorrecta. Se muestran algunos caracteres extraños. El usuario haría clic en un botón "Siguiente codificación" o "codificación previa", y la cadena se convertiría en una codificación diferente. El usuario solo necesita seguir haciendo clic hasta que se encuentre la codificación correcta. (Cualquier codificación que se vea bien para el usuario funcionará bien). Siempre que el usuario pueda hacer clic en "siguiente", tiene una posibilidad razonable de resolver su problema.

Lo que he encontrado hasta ahora implica convertir la cadena a bytes utilizando la codificación actual, luego convertir los bytes a la siguiente codificación, convertir esos bytes en caracteres, y luego convertir el carácter en una cadena ... Posible, pero yo pregunto si no hay una manera más fácil de hacer eso.

Por ejemplo, si había un método que leer una cadena y lo devuelve usando una codificación diferente, algo así como "render (cadena, codificación)".


Muchas gracias por las respuestas!

+0

Técnicamente, un archivo codificado en UTF-8 no necesita una lista de materiales. Incluso se desaconseja, ya que interferirá con las aplicaciones que esperan que los datos ASCII comiencen con ciertos caracteres, como "#!" al comienzo de los scripts de shell de Unix. –

Respuesta

14

Lea el archivo como bytes y luego utilice el método 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)); 

Así que solo tiene que cargar el archivo una vez.Puede usar todas las codificaciones basadas en los bytes originales del archivo. El usuario puede seleccionar la correcta y puede usar el resultado de Encoding.GetEncoding (...). GetString (data) para su posterior procesamiento.

0

Podría dejar que el usuario introduzca algunas palabras (con caracteres "especiales") que se supone que ocurra en el archivo?

Puede buscar todas las codificaciones usted mismo para ver si estas palabras están presentes.

0

Cuidado con el famoso 'Notepad bug'. Aunque va a morder todo lo que intente ... Puede encontrar good discussions sobre las codificaciones y sus desafíos en MSDN (y en otros lugares).

0

Debe conservar los datos originales como una matriz de bytes o MemoryStream, luego puede traducirlos a la nueva codificación, una vez que haya convertido sus datos a una cadena, no podrá regresar de manera confiable a la representación original.

0

¿Qué tal algo como esto:

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 en su primer intento; luego use TryEncoding posteriormente.

+0

¿No necesita reiniciar la secuencia de la memoria, es decir, stream.Seek (0, SeekOrigin.Begin)? –

+0

Probablemente, simplemente lo escribí en el bloc de notas :) –

4

(eliminado respuesta original actualización siguiente pregunta)

Por ejemplo, si había un método que leer una cadena y lo devuelve utilizando una codificación diferente, algo como "render (cadena, codificación) ".

No creo que pueda volver a utilizar los datos de cadena. El hecho es que si la codificación era incorrecta, esta cadena puede considerarse corrupta. Puede muy fácilmente contener galimatías entre los personajes que probablemente se vean. En particular, muchas codificaciones pueden perdonar la presencia/ausencia de una BOM/preámbulo, pero ¿volvería a codificar con ella? ¿sin ello?

Si usted está dispuesto a correr el riesgo (no estaría), sólo podría volver a codificar la cadena local con la última codificación:

// 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); 

En realidad, creo que lo mejor que puede hacer es mantener el original byte[] - sigue ofreciendo diferentes representaciones (a través de diferentes codificaciones) hasta que les guste uno. Algo como:

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