【问题标题】:Binary to strings as binary?二进制到字符串作为二进制?
【发布时间】:2012-08-27 07:50:09
【问题描述】:

Redis keys are binary safe。我想搞砸并使用 C# 将二进制文件放入 redis。我选择的客户不支持编写二进制密钥,它使用密钥,这很有意义。但是我只是在胡闹,所以告诉我我该怎么做。

如何将原始字节[] 转换为字符串?起初我正在考虑将 byte[] 转换为 utf8 字符串,但是 unicode 有一些检查来查看它是否有效。所以原始二进制文件应该会失败。

其实我试过了。我没有失败,而是得到了一个奇怪的结果。我的主要问题是如何将原始 byte[] 转换为等效字符串?就像将原始字节 [] 作为字符串而不是编码为 base32/64/hex/whatever 一样。我不重要的问题是为什么我得到一个 512 字节的字符串而不是一个异常说这不是一个有效的 UTF8 字符串?

代码

var rainbow = new byte[256];
for (int i = 0; i < 256; i++)
{
    rainbow[i] = (byte)i;
}
var sz = Encoding.UTF8.GetString(rainbow);
var szarr = Encoding.UTF8.GetBytes(sz);
Console.WriteLine("{0} {1} {2}", ByteArraysEqual(szarr, rainbow), szarr.Length, rainbow.Length);

输出

假 512 256

【问题讨论】:

  • @dbaseman:不,我不想将二进制编码为文本。我想要字符串结构中的原始二进制文件。现在我编辑了一行...
  • 我认为接受的答案是危险的错误......另外:如果您使用的是 BookSleeve,我花了很多时间考虑二进制密钥(BookSleeve 使用二进制协议,所以它是微不足道的做) - 问题很简单:我认为绝大多数用户将使用字符串键 - 不确定是否值得加倍 API 来支持两者。有一种厚颜无耻的方式,我可以在一个 API 上同时支持这两种方式,但这将是一个突破性的变化。
  • @MarcGravell 你可以,但我实际上并不打算这样做。可能在 C 中针对特定问题,但是....我必须进行测试,看看它是否值得。在我考虑之前,它必须是大量的键,我仍然想测试,我仍然需要做前缀(不能使用 bytesForId,需要一个字节或 txt 前缀来评论 VS 帖子 VS 其他一些 id)。我认为它不值得,除非你在一个主要项目中看到它的用途。就像我说的那样,我能想到的唯一生产项目(我正在做 redis 是为了好玩)是在 C 中,而不是 C#

标签: c# .net string binary bytearray


【解决方案1】:

如果您有一个任意字节[],将其作为字符串获取的方法是将其转换为 hex 或 base-64 之类的东西。最简单的:

byte[] key = ...
string s = Convert.ToBase64String(key);

反过来:

key = Convert.FromBase64String();

很有诱惑力使用 System.Text.Encoding 之类的东西,但这是完全不正确的,并且不能用于进行稳健的转换。如果使用Encoding,有两个问题:

  • 许多键无法成功往返
  • 许多不同的 byte[] 键可以成为相同的字符串键

这两个都不好!问题是使用是向后的:编码将任意字符串转换为结构化字节[],允许对任何字符串进行编码/解码。 Base-64 将任意 byte[] 转换为结构化字符串/从结构化字符串转换。非常细微的区别,但非常重要。

【讨论】:

  • many keys cannot be successfully round-tripped 是什么意思。我正在考虑使用 16 位值进行测试,但决定 0-255 很好。我愚蠢地接受了,忘记了作者没有纠正他的ASCII 解决方案。我接受是因为我的评论和他对我为什么得到字节的解释(真的 .NET 应该抛出:()。many keys cannot be successfully round-tripped 可以用我的评论/解决方案我打算接受并愚蠢地忘记他留下了不正确的一个。出了什么问题与
  • var encoding = Encoding.GetEncoding("iso-8859-1"); var sz = encoding.GetString(rainbow); var szarr = encoding.GetBytes(sz); iso-8859-1 是一个 8 位编码器(它是否是拉丁文并不重要,我相信任何其他 iso-8859-X 编码)。它成功地将 0-255 转换为字符串并返回。 Redis 处理二进制密钥,所以.. 除了忘记在字节前面加上标签、用户、评论等标识符之外,我没有看到问题。如果容器保证其二进制安全,IMO base64 也是不可取的。电子邮件、网站等不保证这一点,我大部分时间都在使用 base64
  • 感谢您的澄清,特别是二进制数据的字符串表示(base 64)和字符串的二进制表示(编码)之间的有用区别。但是,由于 iso-8859-1 将字符 1-256 一对一映射到它们的二进制值,我认为它应该适用于 Op 的问题范围......?
  • 调用 encoding.GetString 对于任意 byte[] 来说只是 incorrect - 只有在 byte[] 恰好包含字符串数据时才有意义通过相同的编码进行编码。因此,在任意字节 [] 上调用 GetString 可能会导致随机字符串不返回原始字节 [] - 或者它可能只是抛出异常。容器是二进制安全的:嗯,yes,但这只有在传递 original byte[] 时才重要 - 实际上,基于字符串的密钥已经是去 redis 之前 UTF-8 编码。老实说:在任意集合上调用 GetString
  • in reality, the string-based key will already be UTF-8 encoded before going to redis 等等什么?!? Redis 清楚地说明了它的二进制安全。为什么它会弄乱我的字节?什么改变了UTF8?客户端或redis本身的东西?我也理解你所说的不安全是什么意思,因为它不是原始编码,但我没有将编码用作字符串,只是一种将我的字节放入字符串对象的方法。我也理解实现可能会改变,但是它的 iso 所以我不认为 - 这个 - 实现会改变。它使用 8 位(更多见下文)
【解决方案2】:

您必须使用某种编码将字节转换为字符串。编码 iso-8859-1 会给出正确的结果:

var sz = Encoding.GetEncoding("iso-8859-1").GetString(rainbow);
var szarr = Encoding.GetEncoding("iso-8859-1").GetBytes(sz);
Console.WriteLine("{0} {1} {2}", ByteArraysEqual(szarr, rainbow), szarr.Length, rainbow.Length);

真 256 256

问题是 UTF8 每个字符需要多个字节。它可以用一个字节编码前 128 个字符:

Console.Write(Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(new byte[] { 127 })).Length);

1

但其余的需要三个字节:

Console.Write(Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(new byte[] { 128 })).Length);

3

因此,当您将字节 0-255 转换为字符串并使用 UTF8 返回时,前 128 字节返回为一个字节,但最后 128 字节返回为 3. 128 + 3*128 = 512,因此是您的结果。

ASCII 不知道如何处理超过 128 的字节,因此它们只是被编码为 ?,然后也返回为一个字节。

【讨论】:

  • ascii 编码我是假的......最后 128 是无效的。 -edit- 现在你编辑了。好吧,这有点道理,但我认为它不应该尝试将无效字节 [] 转换为 utf8 并破坏字节 []。这就是为什么我认为会抛出异常。
  • @acidzombie24 啊对。前 128 个字节是相等的,但之后的所有字节都是 ? 63
  • 我想通了。 Encoding.Default 是“iso-8859-1”。 var encoding = Encoding.GetEncoding("iso-8859-1"); var sz = encoding.GetString(rainbow); var szarr = encoding.GetBytes(sz);作品
  • 这实际上是向后使用编码解码:又名:不要这样做。这不可靠
  • Encoding.DefaultEncoding 不存在; Encoding.Default 指的是操作系统默认代码页,可以是很多不同的东西 - 它不是 8859-1
猜你喜欢
  • 2021-11-09
  • 1970-01-01
  • 2021-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多