C#:エンコーディングを循環する
-
20-08-2019 - |
質問
私はさまざまな形式と言語でファイルを読んでおり、現在小さなエンコードライブラリを使用して適切なエンコードを検出しようとしています(http://www.codeproject.com/KB/recipes/DetectEncoding.aspx).
かなり良いですが、それでも時々失敗します。(多言語ファイル)
潜在的なユーザーのほとんどはエンコーディングについてほとんど理解しておらず (私が期待できる最善のことは「文字に関係がある」ということです)、リストから適切なエンコーディングを選択できる可能性は非常に低いため、ボタンをクリックするだけで、適切なエンコーディングが見つかるまで、さまざまなエンコーディングを循環させます。
表示の問題?別のエンコードを試すにはここをクリックしてください。(まあ、それがコンセプトです)
そのようなものを実装する最善の方法は何でしょうか?
編集:自分の気持ちを十分に明確に表現できていなかったようです。「エンコーディングを繰り返す」というのは、「エンコーディングをどのようにループするか」という意味ではありません。
私が言いたかったのは、「ファイルを再ロードせずに、ユーザーが異なるエンコーディングを順番に試せるようにするにはどうすればよいか?」ということでした。
アイデアは次のようなものです。ファイルが間違ったエンコーディングでロードされたとします。変な文字が表示されることがあります。ユーザーが「次のエンコーディング」または「前のエンコーディング」ボタンをクリックすると、文字列が別のエンコーディングに変換されます。ユーザーは、正しいエンコードが見つかるまでクリックし続けるだけで済みます。(ユーザーにとって適切と思われるエンコーディングであれば何でも問題ありません)。ユーザーが「次へ」をクリックできる限り、問題を解決できる可能性は十分にあります。
私がこれまでに見つけたことには、現在のエンコーディングを使用して文字列をバイトに変換し、次にバイトを次のエンコーディングに変換し、それらのバイトを文字に変換し、次に文字を文字列に変換することが含まれます。実行可能ですが、もっと簡単な方法はないのかと思います。
たとえば、文字列を読み取り、「render(string, encoding)」のような別のエンコーディングを使用してそれを返すメソッドがあったとします。
ご回答ありがとうございました!
解決
ファイルをバイトとして読み取り、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));
したがって、ファイルをロードする必要があるのは 1 回だけです。ファイルの元のバイトに基づいて、あらゆるエンコーディングを使用できます。ユーザーは正しいものを選択でき、Encoding.GetEncoding(...).GetString(data) の結果をさらなる処理に使用できます。
他のヒント
(質問の更新に伴い元の回答を削除しました)
たとえば、文字列を読み取り、「レンダリング(文字列、エンコード)」のような別のエンコードを使用してそれを返す方法がある場合。
文字列データを再利用することはできないと思います。事実は次のとおりです。エンコードが間違っている場合、この文字列は壊れていると考えられます。ありそうな文字の中に意味不明な内容が含まれている可能性が非常に高いです。特に、多くのエンコーディングでは BOM/プリアンブルの有無が許容されますが、それを使用して再エンコードしますか?それなしで?
危険を冒しても構わないのであれば (私は危険を冒しません)、最後のエンコーディングでローカル文字列を再エンコードすることもできます。
// 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);
実際には、オリジナルを維持することが最善だと思います byte[]
- ユーザーが気に入るまで、(異なるエンコーディングを介して) 異なるレンダリングを提供し続けます。何かのようなもの:
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();
}
}
};
}
}
ファイル内に出現することが想定されている単語 (「特殊」文字を含む) をユーザーに入力させてもらえますか?
すべてのエンコーディングを自分で検索して、これらの単語が存在するかどうかを確認できます。
元のデータをバイト配列または MemoryStream として保持してから、新しいエンコーディングに変換する必要があります。データを文字列に変換してしまうと、確実に元の表現に戻すことはできません。
次のようなものはどうでしょうか。
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);
}
最初の試行では LoadFile を使用してください。その後 TryEncoding を使用します。