C #: Ciclo através de codificações
-
20-08-2019 - |
Pergunta
Eu estou lendo arquivos em vários formatos e línguas e eu estou usando atualmente uma pequena biblioteca de codificação para tomar tentativa de detectar a codificação adequada ( http://www.codeproject.com/KB/recipes/DetectEncoding.aspx ).
É muito bom, mas ainda perde de vez em quando. (Arquivos multilingue)
A maioria dos meus potenciais usuários têm muito pouca compreensão de codificação (o melhor que podemos esperar é "que tem algo a ver com caracteres") e são muito pouco provável que seja capaz de escolher a codificação direito em uma lista, então eu gostaria de deixá-los percorrer diferentes codificações até que o caminho certo é encontrado apenas clicando em um botão.
problemas de exibição? Clique aqui para tentar uma codificação diferente! (Bem, isso é o conceito de qualquer maneira)
Qual seria a melhor maneira de implementar algo assim?
Edit: Parece que eu não me expressar com clareza suficiente. Por "bicicleta pela codificação", eu não quero dizer "como fazer um loop através de codificações?"
O que eu quis dizer foi "como permitir que o usuário tente codificações diferentes em sequência sem recarregar o arquivo?"
A idéia é mais assim: digamos que o arquivo é carregado com a codificação errada. Alguns caracteres estranhos são exibidos. O usuário clica em um botão "Next codificação" ou "codificação anterior", ea cadeia seria convertido em uma codificação diferente. O usuário só precisa manter clicando até que a codificação correta é encontrada. (Qualquer codificação parece ser bom para o usuário vai fazer bem). Enquanto o usuário pode clicar em "Next", ele tem uma chance razoável de resolver o seu problema.
O que eu encontrei até agora envolve a conversão da cadeia para bytes utilizando a codificação corrente, em seguida, converter os bytes para o próximo codificação, converter esses bytes em caracteres, em seguida, converter o caractere em uma string ... factível, mas I maravilha se não houver uma maneira mais fácil de fazer isso.
Por exemplo, se houvesse um método que iria ler uma string e devolvê-lo usando uma codificação diferente, algo como "render (string, encoding)".
Muito obrigado pelas respostas!
Solução
Leia o arquivo como bytes e uso, em seguida, o 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));
Então você tem que carregar o arquivo apenas uma vez. Você pode usar todos os codificação com base nos bytes originais do arquivo. O usuário pode selecionar o correto und você pode usar o resultado de Encoding.GetEncoding (...). GetString (dados) para processamento posterior.
Outras dicas
(resposta original removido seguinte atualização questão)
Por exemplo, se havia um método que iria ler uma string e devolvê-lo usando uma codificação diferente, algo como "render (string, encoding)".
Eu não acho que você pode reutilizar os dados de cadeia. O fato é: se a codificação estava errado, essa seqüência pode ser considerado corrupto. Pode muito facilmente conter jargão entre os personagens provável que procuram. Em particular, muitos codificações pode perdoar a presença / ausência de um BOM / preâmbulo, mas você re-codificar com ele? sem ele?
Se você está feliz em arriscar (eu não seria), você pode simplesmente re-codificar a cadeia local, com a última codificação:
// 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);
Na realidade, acredito que o melhor que pode fazer é manter a byte[]
original - continuam oferecendo diferentes representações (via codificações diferentes) até que eles como um. 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();
}
}
};
}
}
Você poderia permitir que o usuário digite algumas palavras (com caracteres "especiais") que devem ocorrer no arquivo?
Você pode pesquisar todas as codificações-se para ver se estas palavras estão presentes.
Cuidado com o infame ' Notepad bug '. Vai a morder-lhe tudo o que você tente, embora ... Você pode encontrar alguns boas discussões sobre codificações e seus desafios no MSDN (e outros lugares).
Você tem que manter os dados originais como um array de bytes ou MemoryStream você pode então traduzir para a nova codificação, uma vez que você já converteu os seus dados para uma string que você não pode voltar com segurança para a representação inicial.
Como sobre algo como isto:
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);
}
Use LoadFile em sua primeira tentativa; em seguida, usar TryEncoding posteriormente.