【问题标题】:Convert.FromBase64String returns unicode sometimes, or UTF-8Convert.FromBase64String 有时返回 unicode 或 UTF-8
【发布时间】:2014-02-11 17:48:18
【问题描述】:

字节数组 b64 有时是 UTF-8,有时是 UTF-16。我一直在网上读到 C# 字符串总是 UTF-16,但我这里不是这种情况。为什么会发生这种情况,我该如何解决?我有一个将base64字符串转换为普通字符串的简单方法:

public static string FromBase64(this string input)
{
    String corrected = new string(input.ToCharArray());
    byte[] b64 = Convert.FromBase64String(corrected);
    if (b64[1] == 0)
    {
        return System.Text.Encoding.Unicode.GetString(b64);
    }
    else
    {
        return System.Text.Encoding.UTF8.GetString(b64);
    }

}

我的 base 64 编码器也发生了同样的事情:

public static string ToBase64(this string input)
{
    String b64 = Convert.ToBase64String(input.GetBytes());
    return b64;
}

public static byte[] GetBytes(this string str)
{
    byte[] bytes = new byte[str.Length * sizeof(char)];
    System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
    return bytes;
}

示例: 在我的电脑上,“cABhAHMAcwB3AG8AcgBkADEA”解码为:

'p','\0','a','\0','s','\0','s','\0','w','\0','o','\0','r','\0','d','\0','1','\0'

但在我同事的电脑上却是:

'p','a','s','s','w','o','r','d','1'

编辑:

我知道我创建的字符串来自一个文本框,并且我保存它的文件总是 UTF-8,所以一切都指向导致我的编码切换的 Convert 方法。

更新:

在进一步挖掘之后,我的同事似乎在他的代码版本中注释了一个非常重要的行,该行将从文件读取的值保存到哈希表中。我使用的默认值是 UTF-8 base64 值,所以我将把默认值更正为 utf-16 值,然后我可以清理删除任何 UTF8 引用的代码。

另外,我一直在天真地使用从网站检索到的 UTF-8 base64 编码,没有意识到自己在做什么。有趣的是,如果我的同事没有评论保存文件中的值的行,我永远不会发现这个事实。

代码的最终版本:

public static string FromBase64(this string input)
{
    byte[] b64 = Convert.FromBase64String(input);
    return System.Text.Encoding.Unicode.GetString(b64);
}

public static string ToBase64(this string input)
{
    String b64 = Convert.ToBase64String(input.GetBytes());
    return b64;
}

public static byte[] GetBytes(this string str)
{
    return System.Text.Encoding.Unicode.GetBytes(str);
}

【问题讨论】:

  • String corrected = new string(input.ToCharArray()); 应该做什么?这没有任何意义。
  • 这是为了让输出播放得很好。我以为它对输出没有影响,但没有,我只是在发布问题之前没有删除它。

标签: c# unicode encoding utf-8 base64


【解决方案1】:

首先,我想揭穿问题的标题:

Convert.FromBase64String() 有时会返回 Unicode,或 UTF-8

事实并非如此。然后给出相同的输入,有效的 base64 编码文本,Convert.FromBase64String() 总是返回相同的输出。

继续前进,您无法仅通过检查有效负载来确定字符串使用的编码。您尝试使用

if (b64[1] == 0)
    // encoding must be UTF-16

事实并非如此。绝大多数 UTF-16 字符元素未通过该测试。不管你如何尝试编写这个测试,它注定会失败。那是因为存在被解释为不同编码时定义良好的字符串的字节数组。换句话说,例如,可以构造在视为 UTF-8 或 UTF-16 时有效的字节数组。

因此,您必须先验地知道有效负载是编码为 UTF-16、UTF-8 还是其他编码。

解决方案是在 base64 编码之前跟踪原始编码。将该信息与 base64 编码的有效负载一起传递。那么当你解码的时候,你就可以决定使用哪个Encoding来解码回字符串了。

在我看来,您的字符串都来自 UTF-16 .net 字符串。在这种情况下,您将永远不会有 UTF-8 字符串,并且应该始终使用UTF-16 进行解码。那就是你使用Encoding.Unicode.GetString()

此外,您代码中的GetBytes 方法也很差。应该是:

public static byte[] GetBytes(this string str)
{
    return Encoding.Unicode.GetBytes(str);
}

另一个怪事:

String corrected = new string(input.ToCharArray());

这是一个空操作。

最后,当编码为 UTF-8 时,您的文本很可能会更紧凑。所以也许你应该在应用 base64 编码之前考虑这样做。


关于您的更新,您所说的不正确。这段代码:

string str = Encoding.Unicode.GetString(
    Convert.FromBase64String("cABhAHMAcwB3AG8AcgBkADEA"));

password1 分配给str,无论它在哪里运行。

【讨论】:

  • 字符串最初是内部的,除非我将它们保存到文件或读回它们。所以我不明白为什么我从一台机器到另一台机器得到不同的编码。
  • 你必须预先知道编码是什么。您无法从有效负载中确定它。您的代码的整个前提是有缺陷的。
  • 如果字符串始终是 unicode,为什么它不在运行相同代码的其他笔记本电脑上?出于这个原因,我不得不添加 UTF8 解码。
  • .net 字符串始终为 UTF-16。你对这个事实有异议吗?
  • 这就是我想要弄清楚的。是传入的字符串正在改变格式,还是只是 Convert.FromBase64String() 方法的结果让我感到困惑。有关我得到的结果,请参阅我添加的示例。
【解决方案2】:

尝试修改代码以使其更具可读性/准确性。正如我的评论和大卫赫弗曼的回答中提到的那样,您正在尝试做以下事情:

A) 什么都不做

B) 展示有缺陷的逻辑

以下基于您的代码可以正常工作:

class Program
{
    static void Main(string[] args)
    {
        string original = "password1";
        string encoded = original.ToBase64();
        string decoded = encoded.FromBase64();
        Console.WriteLine("Original: {0}", original);
        Console.WriteLine("Encoded: {0}", encoded);
        Console.WriteLine("Decoded: {0}", decoded);
    }
}

public static class Extensions
{
    public static string FromBase64(this string input)
    {
        return System.Text.Encoding.Unicode.GetString(Convert.FromBase64String(input));
    }

    public static string ToBase64(this string input)
    {
        return Convert.ToBase64String(input.GetBytes());
    }

    public static byte[] GetBytes(this string str)
    {
        return System.Text.Encoding.Unicode.GetBytes(str);
    }
}

【讨论】:

    【解决方案3】:

    您所做的与在 EBCDIC 或 ASCII 中编码数据没有什么不同,然后试图找出在解码过程中使用了哪个。正如您已经发现的那样,这不会很好地工作。

    使其正常工作的唯一方法是让所有参与者都使用单一编码格式。这是通信的基本概念。

    选择一种编码 - 比如说 UTF-8 - 并将其用于Stringbyte[] 之间的所有转换。这将确保您准确了解有效负载的格式以及如何处理它,正如 David Tanner 一直在告诉您的那样。

    这是基本形式:

    public static string ToBase64(this string self)
    {
        byte[] bytes = Encoding.UTF8.GetBytes(self);
        return Convert.ToBase64String(bytes);   
    }
    
    public static string FromBase64(this string self)
    {
        byte[] bytes = Convert.FromBase64String(self);
        return Encoding.UTF8.GetString(bytes);
    }
    

    无论您的计算机之间发生什么奇怪的事情,这段代码都会产生相同的编码字符串。

    【讨论】:

      猜你喜欢
      • 2013-11-19
      • 1970-01-01
      • 2014-06-24
      • 1970-01-01
      • 2010-12-26
      • 2016-05-03
      • 2014-02-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多