【问题标题】:C#: Cycle through encodingsC#:循环编码
【发布时间】:2009-03-19 10:43:06
【问题描述】:

我正在阅读各种格式和语言的文件,我目前正在使用一个小型编码库来尝试检测正确的编码 (http://www.codeproject.com/KB/recipes/DetectEncoding.aspx)。

还不错,只是偶尔还是会错过。 (多语言文件)

我的大多数潜在用户对编码知之甚少(我最希望的是“它与字符有关”)并且不太可能能够在列表中选择正确的编码,所以我想让他们循环使用不同的编码,直到通过单击按钮找到正确的编码。

显示问题?单击此处尝试不同的编码! (反正就是这个概念)

实现类似的最佳方式是什么?


编辑:看来我表达得不够清楚。通过“循环编码”,我不是指“如何循环编码?”

我的意思是“如何让用户在不重新加载文件的情况下依次尝试不同的编码?”

这个想法更像是这样:假设文件以错误的编码加载。显示一些奇怪的字符。用户将单击“下一个编码”或“上一个编码”按钮,字符串将被转换为不同的编码。用户只需要继续点击,直到找到正确的编码。 (任何对用户来说看起来不错的编码都可以)。只要用户可以点击“下一步”,他就有合理的机会解决他的问题。

到目前为止,我发现涉及使用当前编码将字符串转换为字节,然后将字节转换为下一个编码,将这些字节转换为字符,然后将字符转换为字符串...可行,但我想知道如果没有更简单的方法可以做到这一点。

例如,如果有一个方法可以读取字符串并使用不同的编码返回它,例如“render(string, encoding)”。


非常感谢您的回答!

【问题讨论】:

  • 从技术上讲,UTF-8 编码文件不需要 BOM。甚至不鼓励这样做,因为它会干扰期望 ASCII 数据以某些字符开头的应用程序,例如“#!”在 Unix shell 脚本的开头。

标签: c# utf-8 character-encoding


【解决方案1】:

以字节为单位读取文件,然后使用 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(...).GetString(data) 的结果进行进一步处理。

【讨论】:

    【解决方案2】:

    (在问题更新后删除了原始答案)

    例如,如果有一个方法 这将读取一个字符串并返回它 使用不同的编码,某事 比如“渲染(字符串,编码)”。

    我认为您不能重复使用字符串数据。事实是:如果编码错误,这个字符串可以被认为是损坏的。它可能很容易在看起来很可能的字符中包含胡言乱语。特别是,许多编码可能会原谅 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();
                    }
                }
            };
        }
    }
    

    【讨论】:

      【解决方案3】:

      这样的事情怎么样:

      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)?
      • 可能 - 只是在记事本里写的 :)
      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

        提防臭名昭著的“Notepad bug”。不过,无论您尝试什么,它都会让您吃尽苦头……您可以在 MSDN(和其他地方)上找到一些关于编码及其挑战的 good discussions

        【讨论】:

          【解决方案6】:

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

          【讨论】:

            猜你喜欢
            • 2015-04-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-12-11
            • 1970-01-01
            相关资源
            最近更新 更多