如何从不同的代码页中获取显示字符表示的字节?

时间:2016-01-09 23:02:39

标签: c# unicode

我一直在尝试创建一个一致的方法来获取字符的字节并在替代文本代码页中显示字节表示。例如,Windows 1251,KOI-8U等中的十六进制D1。想法是采用看似乱码的文本,因为它被解释并显示在错误的字符集中并将其转换为正确的显示。下面是我用过的代码的缩写部分。我已经把它用于ideone,但是不能让它在powershell中作为add-type工作或者用csc编译。我只是得到问号或不正确的字符。

ideone的以下代码的输出是正确的转换:

D1-00-C1-00

СБ

窗口-1251

使用PowerShell或csc编译时(不正确):

D1-00-C1-00

?A

窗口-1251

有没有办法在Windows环境中使这项工作?

using System;
using System.Text;

public class Test
{
    public static void Main()
    {
        string str = "ÑÁ"
        byte[] bytes = new byte[str.Length * sizeof(char)];

        System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
        Encoding enc = Encoding.GetEncoding(1251);
        char[] ca = enc.GetChars(bytes);
        Console.WriteLine(BitConverter.ToString(bytes));
        Console.WriteLine(ca);
        Console.WriteLine(enc.HeaderName);
    }
}

2 个答案:

答案 0 :(得分:1)

首先,解决此问题的最佳方法是避免它 - 确保当你有字节时,你总是知道用哪个字符集来编码这些字节。

要回答这个问题:你不能。没有一致的方法可以使这项工作在任何地方。这将总是涉及猜测。

您看到的是一个字符串,它使用某种编码编码为字节,然后使用不同的编码进行解码。以下是修复这些字符串的方法:

  1. 弄清楚(或猜测)最初使用什么编码将字符串编码为字节。
  2. 弄清楚(或)显示字符串时使用的编码
  3. 反向操作:使用步骤(2)中的编码对mojibake进行编码,并使用步骤(1)中的编码对字节进行编码
  4. 如果您已经有 bytes ,则只执行步骤(1)并使用该解码将字节解码为字符串。

    执行此操作的程序如下所示:

    using System;
    using System.Text;
    
    public class Test
    {
        public static void Main()
        {
            // our corrupted string
            string str = "ÑÁ"
    
            // encoding from step (2)
            Encoding enc1 = Encoding.GetEncoding(1252);
            byte[] bytes = enc1.GetBytes(str);
    
            // encoding from step (1)
            Encoding enc2 = Encoding.GetEncoding(1251);
            string originalStr = enc.GetString(bytes);
    
            Console.WriteLine(originalStr);
        }
    }
    

答案 1 :(得分:0)

UPDATE /溶液

正如罗兰所说,这涉及到相当多的猜测工作。作为C#解决方案的问题也是Windows上的两个部分。看起来控制台的显示默认编码不会随编码对象自动改变(似乎Mac和Mono Framework)。必须使用SetConsoleCP和SetConsoleOutputCP手动设置控制台显示。我还必须创建多个编码并使用内部循环来获得代码页的正确交集。以下链接指向显示问题的解决方案。

PowerShell的UTF-8输出

以下示例主要关注俄语是疑似语言的情况。

CODE

using System;
using System.Text;
using System.Runtime.InteropServices;

namespace Language
{
   public class Test
   {
      //Imports dll to set console display
       [DllImport("kernel32.dll"]
       public static extern bool SetConsoleCP(int codepage);
       [DllImport("kernel32.dll"]
       public static extern bool SetConsoleOutputCP(int codepage);
   public static void Main()
    {
       string s = "ÑÁÅ";
       byte[] bytes = new byte[s.Length * sizeof(char)];
       System.Buffer.BlockCopy(s.ToCharArray(), 0, bytes, 0,  bytes.Length);
       Console.WriteLine(BitConverter.ToString(bytes);
     //produce possible combinations
       foreach (Encoding encw in Russian.GetCps())
       {
          bool cp = SetConsoleOutputCP(encw.CodePage);
          bool cp2 = SetConsoleCP(encw.CodePage);
          foreach (Encoding enc in Russian.GetCps())
          {
              char[] ca = enc.GetChars(bytes);
              Console.WriteLine(ca);
           }
         }
       }
     }
public class Russian
{
    public static Encoding[] GetCps()
    {
     // get applicable Cyrillic pages
     Encoding[] = russian = new Encoding[8];
     russian[0] = Encoding.GetEncoding(855);  
     russian[1] = Encoding.GetEncoding(866);     
     russian[2] = Encoding.GetEncoding(1251); 
     russian[3] = Encoding.GetEncoding(10007); 
     russian[4] = Encoding.GetEncoding(20866); 
     russian[5] = Encoding.GetEncoding(21866); 
     russian[6] = Encoding.GetEncoding(20880); 
     russian[7] = Encoding.GetEncoding(28595);
     return russian;
    } 
 }
}   

输出很长,但是给出一个字符串,其输出正确,作为列表的一个成员。

我在PowerShell中制作了一个较短的版本,它似乎会自动更改显示代码页,并且需要更少的迭代次数:

function Get-Language ([string]$source) {

$encodings = [System.Text.Encoding]::GetEncoding(855),[System.Text.Encoding]::GetEncoding(866),[System.Text.Encoding]::GetEncoding(1251),[System.Text.Encoding]::GetEncoding(10007),[System.Text.Encoding]::GetEncoding(20866),[System.Text.Encoding]::GetEncoding(21866),[System.Text.Encoding]::GetEncoding(20880),[System.Text.Encoding]::GetEncoding(28595)

$C = ""
$bytes = gc $source -encoding byte
for ($i=0; $i -le $encodings.Length - 1; $i++) {
$bytes | %{$C = $C + $encodings[$i].GetChars($_)}
Write-Host $C
$C = ""
    }
}