首页 > 解决方案 > 如何正确比较字符串和字节?

问题描述

我正在尝试将某些文件读取为字节并将其与“\u0019\u0093\r\n\u001a\n”进行比较。而且我确定我总是会得到 byte[]{ 0x19, 0x93, 0x0d, 0x0a, 0x1a, 0x0a }。

我尝试将这些字节转换为字符串并与字符串进行比较,但总是错误的。

所以我尝试将字符串转换为字节。但当我比较它们时也总是错误的。

(在 Windows 10 上使用 .NET Core 3.0)

我试过像下面的代码

byte[] bytes = new byte[]{ 0x19, 0x93, 0x0d, 0x0a, 0x1a, 0x0a };
string s = "\u0019\u0093\r\n\u001a\n";
System.Console.WriteLine(Encoding.Default.GetString(bytes) == s);
System.Console.WriteLine(s.Length);
foreach (var b in Encoding.Default.GetBytes(s))
{
    System.Console.WriteLine("Byte: "+b);
}
System.Console.WriteLine(Encoding.Default.GetString(bytes) == s);

输出是:

False
6
Byte: 25
Byte: 194
Byte: 147
Byte: 13
Byte: 10
Byte: 26
Byte: 10
False

比较总是返回 false。我发现从字符串转换为字节后,我又多了一个字节,但不知道 194 是从哪里来的。为什么会这样?

我想它们在转换后应该相等。这是错的吗?

如果我想得到我所期望的,我应该怎么做?

标签: c#.netstringencodingbyte

解决方案


在您的原始编码字节中,有问题的字符代码是0x0093.

您遇到的问题是,在Default系统上的编码中(在 Windows 上,无论系统当前的代码页是什么),编码为 as 的字符0x0093都无法识别。因此,当您尝试对其进行解码时,您会得到 UTF16 字符点0xfffd(这是 .NET 解码器对无法识别字符的默认设置)。然后将其编码回您的默认编码0x93c2(您在输出中看到的字节序列,以十进制表示,194后跟147)。

值得一提的是,此行为与您将默认编码设置为 UTF8 一致,可能表明它是 Linux 系统(大多数 Windows 系统将使用某些特定于语言环境的代码页作为默认编码,而不是 UTF8)。

如果您希望原始字节0x93转换为具有基本相同值(即0x0093aka '\u0093')的 UTF16 字符,那么您需要使用文本编码对原始字节进行解码,其中代码点0x93实际上转换为 UTF16 代码点0x0093

幸运的是,实际上有一个网站会告诉我们哪些编码包含此字符,以及它们的值是什么:https ://www.compart.com/en/unicode/charsets/ contains/U+0093

从该表中,我们可以看到大量的编码是这种情况(也有一些编码将 UTF16 字符'\u0093'编码为不同的值,即0x33……显然,我们不想要这些)。列表中的第一个编码——“ISO-8859-1”——看起来很合适,所以让我们尝试使用它来解码你的字节:

byte[] bytes = new byte[] { 0x19, 0x93, 0x0d, 0x0a, 0x1a, 0x0a };
string s = "\u0019\u0093\r\n\u001a\n";
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
System.Console.WriteLine(encoding.GetString(bytes) == s);
System.Console.WriteLine(s.Length);
foreach (var b in encoding.GetBytes(s))
{
    System.Console.WriteLine("Byte: " + b);
}
System.Console.WriteLine(encoding.GetString(bytes) == s);

这只是你想要的输出:

真的
6
字节:25
字节:147
字节:13
字节:10
字节:26
字节:10
真的

显示的字节甚至是bytes数组中的确切字节,我们可以通过将这一行添加到程序末尾来演示:

System.Console.WriteLine(encoding.GetBytes(s).SequenceEqual(bytes));

那也将打印True.

这个故事的寓意是:知道你试图解码的字节的原始编码不是可选的。您必须确切知道使用了哪种编码,因为它就是:一种编码。如果您使用了错误的编码,您不妨尝试对加密数据进行解码。

根据定义,不同的文本编码是不同的。这意味着一种编码中的字节与其他编码中的字节含义完全不同(有点……大多数编码在最低的 128 个代码点中重叠,因为它们都基于 ASCII)。如果您使用错误的编码来解码某些字节,您只会得到随机结果(或者,在这种情况下,解码器将根本无法识别字符并将其转换为表示无法识别字符的占位符)。


推荐阅读