如何在.NET(C#)中将字符串转换为字节[]而不手动指定特定编码?
我要加密字符串。我可以在不进行转换的情况下对其进行加密,但我仍然想知道为什么编码会在这里发挥作用。
此外,为什么还要考虑编码?我不能简单地获取字符串存储的字节数吗?为什么依赖字符编码?
如何在.NET(C#)中将字符串转换为字节[]而不手动指定特定编码?
我要加密字符串。我可以在不进行转换的情况下对其进行加密,但我仍然想知道为什么编码会在这里发挥作用。
此外,为什么还要考虑编码?我不能简单地获取字符串存储的字节数吗?为什么依赖字符编码?
当前回答
LINQ的简单代码
string s = "abc"
byte[] b = s.Select(e => (byte)e).ToArray();
编辑:如下所述,这不是一个好方法。
但您仍然可以使用它来理解LINQ,并使用更合适的编码:
string s = "abc"
byte[] b = s.Cast<byte>().ToArray();
其他回答
如何在.NET(C#)中将字符串转换为字节[]而不手动指定特定编码?
NET中的字符串将文本表示为UTF-16代码单元的序列,因此字节已经在UTF-16中的内存中编码。
Mehrad的回答
您可以使用Mehrad的答案,但它实际上使用了编码,因为字符是UTF-16。它调用ToCharArray,通过查看源代码创建一个char[]并将内存直接复制到它。然后,它将数据复制到同样分配的字节数组中。因此,在后台,它复制了两次底层字节,并分配了一个在调用后不使用的字符数组。
Tom Blodget的回答
Tom Blodget的答案比Mehrad快20-30%,因为它跳过了分配一个字符数组并将字节复制到其中的中间步骤,但它需要使用/safe选项进行编译。如果你绝对不想使用编码,我认为这是正确的方法。如果将加密登录放在固定块中,甚至不需要分配单独的字节数组并将字节复制到其中。
此外,为什么要考虑编码?我不能简单地获取字符串存储的字节数吗?为什么依赖字符编码?
因为这是正确的方法。字符串是一个抽象。
如果“字符串”包含无效字符,使用编码可能会给您带来麻烦,但这不应该发生。如果将数据输入到字符串中包含无效字符,则说明操作错误。您可能应该首先使用字节数组或Base64编码。
如果使用System.Text.Encoding.Unicode,代码将更具弹性。您不必担心运行代码的系统的端序。您不必担忧下一版本的CLR是否会使用不同的内部字符编码。
我认为问题不在于你为什么要担心编码,而是你为什么要忽略它而使用其他东西。编码旨在表示字节序列中字符串的抽象。System.Text.Encoding.Unicode将为您提供一个小端字节顺序编码,并将在现在和将来的每个系统上执行相同的编码。
嗯,我读过所有的答案,它们都是关于使用编码或关于删除未配对代理的序列化。
例如,如果字符串来自SQL Server,它是从存储例如密码哈希的字节数组构建的,这就很糟糕了。如果我们从中删除任何内容,它将存储一个无效的哈希,如果我们想将其存储在XML中,我们希望保持它的完整性(因为XML编写器会在它找到的任何未配对代理上删除一个异常)。
所以我在这种情况下使用了字节数组的Base64编码,但是在互联网上,只有一种解决方案是C#,而且它有bug,而且只有一种方法,所以我已经修复了bug并编写了返回过程。给你,未来的谷歌人:
public static byte[] StringToBytes(string str)
{
byte[] data = new byte[str.Length * 2];
for (int i = 0; i < str.Length; ++i)
{
char ch = str[i];
data[i * 2] = (byte)(ch & 0xFF);
data[i * 2 + 1] = (byte)((ch & 0xFF00) >> 8);
}
return data;
}
public static string StringFromBytes(byte[] arr)
{
char[] ch = new char[arr.Length / 2];
for (int i = 0; i < ch.Length; ++i)
{
ch[i] = (char)((int)arr[i * 2] + (((int)arr[i * 2 + 1]) << 8));
}
return new String(ch);
}
与这里的答案相反,如果不需要解释字节,则不需要担心编码!
正如您提到的,您的目标很简单,就是“获取字符串存储的字节数”。(当然,还要能够根据字节重新构造字符串。)
对于这些目标,我真的不明白为什么人们总是告诉你你需要编码。你当然不需要担心编码。
只需执行以下操作:
static byte[] GetBytes(string str)
{
byte[] bytes = new byte[str.Length * sizeof(char)];
System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
// Do NOT use on arbitrary bytes; only use on GetBytes's output on the SAME system
static string GetString(byte[] bytes)
{
char[] chars = new char[bytes.Length / sizeof(char)];
System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
return new string(chars);
}
只要你的程序(或其他程序)不试图以某种方式解释字节,而你显然没有提到你打算这样做,那么这种方法就没有错!担心编码只会让你的生活变得更加复杂,没有真正的原因。
这种方法的额外好处:字符串是否包含无效字符无关紧要,因为您仍然可以获取数据并重建原始字符串!
它将以相同的方式进行编码和解码,因为您只是在查看字节。
但是,如果您使用特定的编码,则会给您带来编码/解码无效字符的麻烦。
当被问及您打算如何处理字节时,您回答:
我将对它进行加密。我可以在不进行转换的情况下对其进行加密,但我仍然想知道为什么编码会在这里发挥作用。给我字节就是我说的。
无论您是否打算通过网络发送加密数据,稍后将其加载回内存,或将其流式传输到另一个进程,您显然都打算在某个时刻对其进行解密。在这种情况下,答案是您正在定义通信协议。通信协议不应根据编程语言及其相关运行时的实现细节来定义。这有几个原因:
您可能需要与以不同语言或运行时实现的流程进行通信。(例如,这可能包括在另一台机器上运行的服务器或将字符串发送到JavaScript浏览器客户端。)该程序将来可以用不同的语言或运行时重新实现。.NET实现可能会更改字符串的内部表示形式。您可能会觉得这听起来有些牵强,但这实际上发生在Java9中,以减少内存使用。.NET没有理由不能效仿。Skeet表明,UTF-16在今天可能不是最佳的,因为表情符号和其他Unicode块也需要2个以上的字节来表示,这增加了内部表示在未来可能发生变化的可能性。
为了进行通信(无论是与完全不同的进程还是将来与相同的程序),您需要严格定义协议,以最大限度地减少使用协议或意外创建错误的难度。依赖于.NET的内部表示并不是一个严格的、明确的,甚至不能保证是一致的定义。标准编码是一个严格的定义,将来不会让您失望。
换句话说,如果不指定编码,就无法满足一致性要求。
如果您发现由于.NET在内部使用了UTF-16或出于其他原因,您的进程的性能明显更好,那么您当然可以选择直接使用UTF-16,但您需要显式选择编码,并在代码中显式执行这些转换,而不是依赖于.NET的内部实现。
因此,选择一种编码并使用它:
using System.Text;
// ...
Encoding.Unicode.GetBytes("abc"); # UTF-16 little endian
Encoding.UTF8.GetBytes("abc")
正如您所看到的,与实现您自己的读写器方法相比,仅使用内置的编码对象实际上也是更少的代码。
计算机只理解原始二进制数据,原始比特。一位是二进制数字:0或1。8位数字是一个字节。一个字节是介于0和255之间的数字。
ASCII是一种将数字转换为字符的表格。0到31之间的数字是控件:制表符、换行符和其他。32到126之间的数字为可打印字符:字母a,数字1,%符号,下划线_
因此,对于ASCII,有33个控制字符和95个可打印字符。
ASCII是当今最常用的字符编码。Unicode表的第一个条目是ASCII,并与ASCII字符集匹配。
ASCII是一个7位字符集。介于0和127之间的数字。使用8位,我们可以达到255位。
ASCII最常见的替代品是EBCDIC,它与ASCII不兼容,今天仍然存在于IBM计算机和数据库中。
1字节,因此8位数字是当今计算机科学中最常用的单位。1字节是介于0和255之间的数字。
ASCII为0到127之间的每个数字定义了一个含义。
与128和255之间的数字相关联的字符取决于所使用的字符编码。目前广泛使用的两种字符编码是windows1252和UTF-8。
在windows1252中,欧元符号对应的数字是128。1字节:[A0]。在Unicode数据库中,欧元符号是数字8364。
现在我给你电话8364。两个字节:[20,AC]。在UTF-8中,欧元符号是数字14844588。三个字节:[E282AC]。
现在我给你一些原始数据。假设20AC。是两个windows1252字符:£还是一个Unicode€符号?
我给你一些原始数据。e282交流。82是windows1252中未分配的字符,因此它可能不是windows1252。它可能是macRoman“”C“”或OEM 437“”或UTF-8“€”符号。
根据字符编码的特性和统计数据,可以猜测原始字节流的编码,但没有可靠的方法。128到255之间的数字在UTF-8中是无效的。é在某些语言(法语)中很常见,因此如果您看到许多字节的值E9被字母包围,那么它可能是一个windows1252编码字符串,E9字节表示é字符。
当您有一个表示字符串的原始字节流时,了解匹配的编码比猜测要好得多。
下面是曾经被广泛使用的各种编码中的一个原始字节的屏幕截图。