如何在.NET(C#)中将字符串转换为字节[]而不手动指定特定编码?
我要加密字符串。我可以在不进行转换的情况下对其进行加密,但我仍然想知道为什么编码会在这里发挥作用。
此外,为什么还要考虑编码?我不能简单地获取字符串存储的字节数吗?为什么依赖字符编码?
如何在.NET(C#)中将字符串转换为字节[]而不手动指定特定编码?
我要加密字符串。我可以在不进行转换的情况下对其进行加密,但我仍然想知道为什么编码会在这里发挥作用。
此外,为什么还要考虑编码?我不能简单地获取字符串存储的字节数吗?为什么依赖字符编码?
当前回答
这是一个流行的问题。重要的是要了解作者所问的问题,以及它与最常见的需求不同。为了防止在不需要的地方滥用代码,我首先回答了后者。
共同需求
每个字符串都有一个字符集和编码。将System.String对象转换为System.Byte数组时,仍有字符集和编码。对于大多数用途,您可以知道需要哪个字符集和编码,.NET使“复制并转换”变得简单。只需选择适当的encoding类即可。
// using System.Text;
Encoding.UTF8.GetBytes(".NET String to byte array")
转换可能需要处理目标字符集或编码不支持源中的字符的情况。您有一些选择:异常、替换或跳过。默认策略是替换“?”。
// using System.Text;
var text = Encoding.ASCII.GetString(Encoding.ASCII.GetBytes("You win €100"));
// -> "You win ?100"
显然,转换不一定是无损的!
注意:对于System.String,源字符集是Unicode。
唯一令人困惑的是,.NET使用字符集的名称作为该字符集的一个特定编码的名称。编码。Unicode应称为Encoding.UTF16。
这就是大多数用法。如果这正是你所需要的,请停止阅读这里。如果您不了解编码是什么,请参阅有趣的Joel Spolsky文章。
特定需求
现在,作者提出的问题是,“每个字符串都存储为一个字节数组,对吗?为什么我不能简单地拥有这些字节?”
他不想改变信仰。
根据C#规范:
C#中的字符和字符串处理使用Unicode编码。字符类型表示UTF-16代码单元,字符串类型表示UTF-16代码单元序列。
因此,我们知道,如果我们请求空转换(即,从UTF-16到UTF-16),我们将得到所需的结果:
Encoding.Unicode.GetBytes(".NET String to byte array")
但为了避免提及编码,我们必须采用另一种方式。如果可以接受中间数据类型,则有一个概念上的快捷方式:
".NET String to byte array".ToCharArray()
这并不能为我们提供所需的数据类型,但Mehrad的答案显示了如何使用BlockCopy将此Char数组转换为Byte数组。然而,这将复制字符串两次!而且,它也显式地使用特定于编码的代码:数据类型System.Char。
获取存储字符串的实际字节的唯一方法是使用指针。fixed语句允许获取值的地址。根据C#规范:
[对于]字符串类型的表达式。。。初始值设定项计算字符串中第一个字符的地址。
为此,编译器使用RuntimeHelpers.OffsetToStringData跳过字符串对象的其他部分编写代码。因此,要获取原始字节,只需创建一个指向字符串的指针并复制所需的字节数。
// using System.Runtime.InteropServices
unsafe byte[] GetRawBytes(String s)
{
if (s == null) return null;
var codeunitCount = s.Length;
/* We know that String is a sequence of UTF-16 code units
and such code units are 2 bytes */
var byteCount = codeunitCount * 2;
var bytes = new byte[byteCount];
fixed(void* pRaw = s)
{
Marshal.Copy((IntPtr)pRaw, bytes, 0, byteCount);
}
return bytes;
}
正如@CodesInChaus所指出的,结果取决于机器的端序。但问题的作者并不关心这一点。
其他回答
为了证明Mehrrad的声音回答有效,他的方法甚至可以持久化未配对的代理字符(其中许多人对我的答案持反对态度,但每个人都有同样的错误,例如System.Text.Encoding.UTF8.GetBytes、System.Text.Encding.Unicode.GetBytes;例如,这些编码方法不能持久化高代理字符d800,而这些方法只是用值fffd替换高代理字符):
using System;
class Program
{
static void Main(string[] args)
{
string t = "爱虫";
string s = "Test\ud800Test";
byte[] dumpToBytes = GetBytes(s);
string getItBack = GetString(dumpToBytes);
foreach (char item in getItBack)
{
Console.WriteLine("{0} {1}", item, ((ushort)item).ToString("x"));
}
}
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;
}
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);
}
}
输出:
T 54
e 65
s 73
t 74
? d800
T 54
e 65
s 73
t 74
尝试使用System.Text.Encoding.UTF8.GetBytes或System.Text.Encding.Unicode.GetBytes,它们只会用值fffd替换高代理项字符
每当这个问题发生变化时,我仍然在想一个序列化程序(无论是来自Microsoft还是来自第三方组件),它可以持久化字符串,即使它包含不成对的代理字符;我时不时地在谷歌上搜索这个:序列化不成对的代理角色.NET。这不会让我失眠,但偶尔有人评论我的答案,说它有缺陷,但他们的答案在不成对的替代角色方面同样有缺陷,这让我很烦。
Darn,Microsoft应该在BinaryFormatter中使用System.Buffer.BlockCopyツ
谢谢!
代码如下:
// Input string.
const string input = "Dot Net Perls";
// Invoke GetBytes method.
// ... You can store this array as a field!
byte[] array = Encoding.ASCII.GetBytes(input);
// Loop through contents of the array.
foreach (byte element in array)
{
Console.WriteLine("{0} = {1}", element, (char)element);
}
公认的答案非常非常复杂。为此,请使用包含的.NET类:
const string data = "A string with international characters: Norwegian: ÆØÅæøå, Chinese: 喂 谢谢";
var bytes = System.Text.Encoding.UTF8.GetBytes(data);
var decoded = System.Text.Encoding.UTF8.GetString(bytes);
如果你不需要。。。
可以使用以下代码在字符串和字节数组之间进行转换。
string s = "Hello World";
// String to Byte[]
byte[] byte1 = System.Text.Encoding.Default.GetBytes(s);
// OR
byte[] byte2 = System.Text.ASCIIEncoding.Default.GetBytes(s);
// Byte[] to string
string str = System.Text.Encoding.UTF8.GetString(byte1);
对于串行通信项目,我必须将字符串转换为字节数组-我必须处理8位字符,而且我无法找到使用框架转换器的方法,这样既不会添加两个字节条目,也不会错误地转换具有第八位集的字节。所以我做了以下工作:
string message = "This is a message.";
byte[] bytes = new byte[message.Length];
for (int i = 0; i < message.Length; i++)
bytes[i] = (byte)message[i];