我如何转换 string
到了 byte[]
在.NET(C#)中没有手动指定特定的编码?
我要加密字符串。我可以在不转换的情况下加密它,但我仍然想知道为什么编码在这里发挥作用。
另外,为什么要考虑编码?我不能简单地得到字符串存储的字节数吗?为什么依赖于字符编码?
我如何转换 string
到了 byte[]
在.NET(C#)中没有手动指定特定的编码?
我要加密字符串。我可以在不转换的情况下加密它,但我仍然想知道为什么编码在这里发挥作用。
另外,为什么要考虑编码?我不能简单地得到字符串存储的字节数吗?为什么依赖于字符编码?
就像你提到的那样,你的目标就是 “获取字符串存储在的字节数”。
(当然,能够从字节中重新构造字符串。)
只需这样做:
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);
}
只要您的程序(或其他程序)不尝试 译 不知怎的,这些字节,你显然没有提到你打算做的,那就有了 没有 这种方法有误!担心编码只会让你的生活更加复杂,没有真正的理由。
它将被编码和解码,因为你是 只看字节。
但是,如果您使用了特定的编码,那么编码/解码无效字符会给您带来麻烦。
这取决于你的字符串的编码(ASCII, UTF-8,...)。
例如:
byte[] b1 = System.Text.Encoding.UTF8.GetBytes (myString);
byte[] b2 = System.Text.Encoding.ASCII.GetBytes (myString);
编码重要的一个小例子:
string pi = "\u03a0";
byte[] ascii = System.Text.Encoding.ASCII.GetBytes (pi);
byte[] utf8 = System.Text.Encoding.UTF8.GetBytes (pi);
Console.WriteLine (ascii.Length); //Will print 1
Console.WriteLine (utf8.Length); //Will print 2
Console.WriteLine (System.Text.Encoding.ASCII.GetString (ascii)); //Will print '?'
ASCII根本没有配备处理特殊字符。
在.NET内部,.NET框架使用 UTF-16 表示字符串,所以如果你只想获得.NET使用的确切字节,请使用 System.Text.Encoding.Unicode.GetBytes (...)
。
看到 .NET Framework中的字符编码 (MSDN)了解更多信息。
接受的答案非常非常复杂。使用包含的.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);
如果你不需要,不要重新发明轮子......
BinaryFormatter bf = new BinaryFormatter();
byte[] bytes;
MemoryStream ms = new MemoryStream();
string orig = "喂 Hello 谢谢 Thank You";
bf.Serialize(ms, orig);
ms.Seek(0, 0);
bytes = ms.ToArray();
MessageBox.Show("Original bytes Length: " + bytes.Length.ToString());
MessageBox.Show("Original string Length: " + orig.Length.ToString());
for (int i = 0; i < bytes.Length; ++i) bytes[i] ^= 168; // pseudo encrypt
for (int i = 0; i < bytes.Length; ++i) bytes[i] ^= 168; // pseudo decrypt
BinaryFormatter bfx = new BinaryFormatter();
MemoryStream msx = new MemoryStream();
msx.Write(bytes, 0, bytes.Length);
msx.Seek(0, 0);
string sx = (string)bfx.Deserialize(msx);
MessageBox.Show("Still intact :" + sx);
MessageBox.Show("Deserialize string Length(still intact): "
+ sx.Length.ToString());
BinaryFormatter bfy = new BinaryFormatter();
MemoryStream msy = new MemoryStream();
bfy.Serialize(msy, sx);
msy.Seek(0, 0);
byte[] bytesy = msy.ToArray();
MessageBox.Show("Deserialize bytes Length(still intact): "
+ bytesy.Length.ToString());
您需要考虑编码,因为1个字符可以用1表示 或者更多 字节(最多约6个),不同的编码将以不同的方式处理这些字节。
乔尔有一个帖子:
这是一个很受欢迎的问题。重要的是要理解作者提出的问题,并且它与最常见的需求不同。为了防止在不需要的地方滥用代码,我先回答了后面的问题。
每个字符串都有一个字符集和编码。当你转换一个 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使用字符集的名称作为该字符集的一个特定编码的名称。 Encoding.Unicode
应该叫 Encoding.UTF16
。
对于大多数用法来说都是如此。如果这就是你需要的,请在这里停止阅读。 看到有趣的 Joel Spolsky的文章 如果你不明白编码是什么。
现在,问题作者问:“每个字符串都存储为一个字节数组,对吧?为什么我不能简单地拥有这些字节?”
他不想要任何转换。
来自 C#规范:
C#中的字符和字符串处理使用Unicode编码。这个角色 type表示UTF-16代码单元,字符串类型表示a UTF-16代码单元序列。
所以,我们知道如果我们要求空转换(即从UTF-16到UTF-16),我们将获得所需的结果:
Encoding.Unicode.GetBytes(".NET String to byte array")
但为了避免提及编码,我们必须采取另一种方式。如果中间数据类型是可接受的,则有一个概念性的快捷方式:
".NET String to byte array".ToCharArray()
这并没有让我们得到所需的数据类型 Mehrdad的回答 演示了如何使用这个Char数组转换为Byte数组 BlockCopy。但是,这会复制两次字符串!而且,它也明确使用特定于编码的代码:数据类型 System.Char
。
获取存储String的实际字节的唯一方法是使用指针。该 fixed
声明允许获取值的地址。来自C#规范:
[For]一个string类型的表达式,...初始化程序计算 字符串中第一个字符的地址。
为此,编译器将代码跳过写入字符串对象的其他部分 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 codeunits
and such codeunits 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;
}
正如@CodesInChaos指出的那样,结果取决于机器的字节顺序。但问题的作者并不关心这一点。
只是为了证明Mehrdrad的声音 回答 作品,他的方法甚至可以坚持下去 不成对的代理人物(其中许多人反对我的答案,但每个人都同样有罪,例如 System.Text.Encoding.UTF8.GetBytes
, System.Text.Encoding.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.Encoding.Unicode.GetBytes,他们只会用价值取代高代理人物 FFFD
每当这个问题发生变化时,我仍然会想到一个序列化器(无论是来自微软还是来自第三方组件),它可以持久化字符串,即使它包含不成对的代理字符;我不时地谷歌这个: 序列化不成对的代理人物.NET。这并没有让我失去任何睡眠,但是当有人评论我的答案它有缺陷时,它会有点烦人,但是当涉及到不成对的代理人物时,他们的答案同样存在缺陷。
Darn,微软应该刚刚使用过 System.Buffer.BlockCopy
在它的 BinaryFormatter
ツ
谢谢!
试试这个,少了很多代码:
System.Text.Encoding.UTF8.GetBytes("TEST String");
你的问题的第一部分(如何获得字节)已经被其他人回答了:看看 System.Text.Encoding
命名空间。
我将解决您的后续问题:为什么需要选择编码?为什么你不能从字符串类本身那里得到它?
答案分为两部分。
首先,字符串类在内部使用的字节 没关系,无论何时你认为他们你可能会引入一个错误。
如果您的程序完全在.Net世界中,那么即使您通过网络发送数据,也不必担心为字符串获取字节数组。相反,使用.Net Serialization来担心传输数据。您不再担心实际的字节:序列化格式化程序会为您执行此操作。
另一方面,如果您将这些字节发送到某个您无法保证的字节会从.Net序列化流中提取数据怎么办?在这种情况下,你肯定需要担心编码,因为显然这个外部系统在乎。同样,字符串使用的内部字节无关紧要:您需要选择一个编码,以便您可以在接收端明确表示此编码,即使它与.Net内部使用的编码相同。
我知道在这种情况下,您可能更愿意在可能的情况下使用字符串变量存储在字节变量中的实际字节,并认为它可能会节省一些创建字节流的工作。但是,我把它告诉你,与确保你的输出在另一端被理解相比并不重要,并保证你 必须 明确你的编码。另外,如果你真的想匹配你的内部字节,你可以选择 Unicode
编码,并节省性能。
这让我想到了第二部分......选择了 Unicode
编码 是 告诉.Net使用底层字节。你确实需要选择这种编码,因为当出现一些新奇的Unicode-Plus时,.Net运行时需要免费使用这种更新,更好的编码模型而不会破坏你的程序。但是,目前(以及可预见的未来),只需选择Unicode编码即可获得所需内容。
理解你的字符串必须重写为连线也很重要,这至少涉及一些位模式的转换 即使你使用匹配的编码。计算机需要考虑Big vs Little Endian,网络字节顺序,打包,会话信息等。