C#: 循环遍历编码

7
我正在阅读各种格式和语言的文件,目前使用了一个小型编码库来尝试检测正确的编码(http://www.codeproject.com/KB/recipes/DetectEncoding.aspx)。它相当不错,但有时会错过(多语言文件)。
大多数潜在用户对编码几乎一无所知(我最好能希望的是“它与字符有关”),并且很难从列表中选择正确的编码,因此我想让他们通过单击按钮循环浏览不同的编码,直到找到正确的编码为止。
显示问题?点击这里尝试不同的编码!(这就是概念)
如何实现这样的功能最佳?
编辑:看起来我没有表达清楚。我的意思不是“如何循环编码?”而是“如何让用户在不重新加载文件的情况下尝试不同的编码顺序?”
这个想法更像是这样的:假设使用了错误的编码加载文件,会显示一些奇怪的字符。用户会点击一个按钮“下一个编码”或“上一个编码”,字符串将会被转换成另一种编码。用户只需要持续点击,直到找到正确的编码(任何对用户来说看起来合适的编码都可以)。只要用户可以点击“下一个”,他就有合理的机会解决问题。
到目前为止,我发现的方法涉及将字符串使用当前编码转换为字节,然后将字节转换为下一个编码,将这些字节转换为字符,然后将字符转换为字符串...... 这是可行的,但我想知道是否有更简单的方法。
例如,如果有一种方法可以读取一个字符串并返回使用不同编码的字符串,类似于“render(string, encoding)”。
非常感谢您的答案!

从技术上讲,UTF-8编码的文件不需要BOM。这甚至是不鼓励的,因为它会干扰那些期望ASCII数据以特定字符开头的应用程序,比如Unix shell脚本开头的"#!"。 - Dave Van den Eynde
6个回答

16

以字节方式读取文件,然后使用 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));

所以你只需要加载文件一次。你可以根据文件的原始字节使用任何编码方式。用户可以选择正确的编码方式,然后你可以使用 Encoding.GetEncoding(...) 的结果对数据进行进一步处理。


4
例如,如果有一种方法可以读取一个字符串并使用不同的编码返回它,类似于“render(string, encoding)”。
我认为您不能重复使用该字符串数据。事实是:如果编码错误,则此字符串可能被视为损坏。它很容易包含看起来正确的字符中的乱码。特别是,许多编码可能会原谅存在/不存在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();
                }
            }
        };
    }
}

1

这样怎么样:

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。


你不需要重置内存流吗?即stream.Seek(0, SeekOrigin.Begin)? - Wim Coenen
可能性很大 - 我只是在记事本上写了它 :) - Martin Clarke

0

您能让用户输入一些(带有“特殊”字符的)应该出现在文件中的单词吗?

您可以自行搜索所有编码,以查看这些单词是否存在。


0

0

你必须将原始数据保留为字节数组或MemoryStream,然后可以将其转换为新编码,一旦将数据转换为字符串,就无法可靠地返回到原始表示。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接