如何在不手动指定特定编码的情况下将 a 转换为 .NET (C#) 中的 a string
?byte[]
我要加密字符串。我可以在不转换的情况下对其进行加密,但我仍然想知道为什么编码会在这里发挥作用。
另外,为什么还要考虑编码?我不能简单地获取字符串存储在哪些字节中吗?为什么依赖于字符编码?
如何在不手动指定特定编码的情况下将 a 转换为 .NET (C#) 中的 a string
?byte[]
我要加密字符串。我可以在不转换的情况下对其进行加密,但我仍然想知道为什么编码会在这里发挥作用。
另外,为什么还要考虑编码?我不能简单地获取字符串存储在哪些字节中吗?为什么依赖于字符编码?
与此处的答案相反,如果不需要解释字节,则无需担心编码!
就像您提到的那样,您的目标很简单,就是“获取字符串存储在哪些字节中”。
(当然,能够从字节重新构造字符串。)
对于这些目标,老实说,我不明白为什么人们一直告诉你你需要编码。您当然不需要为此担心编码。
只需这样做:
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);
}
只要您的程序(或其他程序)不尝试以某种方式解释字节,您显然没有提到您打算这样做,那么这种方法就没有错!担心编码只会无缘无故地让你的生活变得更加复杂。
这种方法的额外好处:字符串是否包含无效字符并不重要,因为您仍然可以获取数据并重建原始字符串!
它将以相同的方式进行编码和解码,因为您只是在查看 bytes。
但是,如果您使用特定的编码,它会给您编码/解码无效字符带来麻烦。
这取决于您的字符串的编码(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 框架使用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 编码。char 类型代表一个 UTF-16 代码单元,而 string 类型代表一个 UTF-16 代码单元序列。
因此,我们知道如果我们要求进行空转换(即,从 UTF-16 到 UTF-16),我们会得到想要的结果:
Encoding.Unicode.GetBytes(".NET String to byte array")
但是为了避免提及编码,我们必须以另一种方式来做。如果中间数据类型是可接受的,则有一个概念上的快捷方式:
".NET String to byte array".ToCharArray()
这并没有为我们提供所需的数据类型,但Mehrdad 的回答显示了如何使用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 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 指出的那样,结果取决于机器的字节序。但问题作者并不关心这一点。
其他人已经回答了您问题的第一部分(如何获取字节):查看System.Text.Encoding
命名空间。
我将解决您的后续问题:为什么需要选择编码?为什么你不能从字符串类本身得到它?
答案分为两部分。
首先,字符串类内部使用的字节无关紧要,无论何时假设它们确实存在,您都可能会引入错误。
如果您的程序完全在 .Net 世界中,那么您根本不需要担心获取字符串的字节数组,即使您正在通过网络发送数据。相反,使用 .Net 序列化来担心传输数据。您不再担心实际字节数:序列化格式化程序会为您完成。
另一方面,如果您将这些字节发送到您无法保证会从 .Net 序列化流中提取数据的地方怎么办?在这种情况下,您肯定需要担心编码,因为显然这个外部系统很关心。同样,字符串使用的内部字节无关紧要:您需要选择一种编码,以便您可以在接收端明确说明此编码,即使它与 .Net 内部使用的编码相同。
我知道在这种情况下,您可能更喜欢尽可能使用由字符串变量存储在内存中的实际字节,这样可以节省一些创建字节流的工作。但是,我告诉您,与确保您的输出在另一端被理解并保证您必须明确编码相比,这并不重要。此外,如果您真的想匹配您的内部字节,您已经可以选择Unicode
编码,并获得性能节省。
这让我进入了第二部分……选择Unicode
编码是告诉 .Net 使用底层字节。您确实需要选择这种编码,因为当一些新奇的 Unicode-Plus 出现时,.Net 运行时需要在不破坏程序的情况下自由使用这种更新、更好的编码模型。但是,目前(以及可预见的未来),只需选择 Unicode 编码即可满足您的需求。
了解您的字符串必须重新写入线路也很重要,即使您使用匹配的编码,这也至少涉及位模式的一些翻译。计算机需要考虑大字节序与小字节序、网络字节顺序、数据包化、会话信息等。
只是为了证明 Mehrdrad 的正确答案有效,他的方法甚至可以保留未配对的代理字符(其中许多人反对我的答案,但每个人都同样有罪,例如System.Text.Encoding.UTF8.GetBytes
,System.Text.Encoding.Unicode.GetBytes
;那些编码方法不能保留高代理例如字符d800
,那些只是用 value 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替换高代理字符
每次这个问题有动静时,我仍然在考虑一个序列化程序(无论是来自微软还是来自 3rd 方组件),它可以保留字符串,即使它包含不成对的代理字符;我时不时地在谷歌上搜索:序列化未配对代理字符 .NET。这不会让我失去任何睡眠,但是当不时有人评论我的答案有缺陷时,这有点烦人,但当涉及到不成对的代理字符时,他们的答案同样有缺陷。
该死,微软应该只是用System.Buffer.BlockCopy
在它的BinaryFormatter
ツ</p>
谢谢!</p>
试试这个,代码少了很多:
System.Text.Encoding.UTF8.GetBytes("TEST String");
好吧,我已经阅读了所有答案,它们是关于使用编码或关于丢弃未配对代理的序列化的。
例如,当字符串来自SQL Server时,它是不好的,它是从存储例如密码哈希的字节数组构建的。如果我们从中删除任何内容,它将存储一个无效的散列,如果我们想将它存储在 XML 中,我们希望保持它完整(因为 XML 编写器在它找到的任何未配对的代理项上删除一个异常)。
所以我在这种情况下使用字节数组的Base64编码,但是,在互联网上,C#中只有一种解决方案,而且它有错误并且只有一种方式,所以我已经修复了错误并写回程序。在这里,未来的谷歌人:
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);
}
还请解释为什么要考虑编码。我不能简单地获取字符串存储在哪些字节中吗?为什么要依赖编码?!!!
因为没有“字符串的字节”之类的东西。
字符串(或更一般地,文本)由字符组成:字母、数字和其他符号。就这样。然而,计算机对字符一无所知。他们只能处理字节。因此,如果要使用计算机存储或传输文本,则需要将字符转换为字节。你是怎样做的?这就是编码出现的地方。
编码只不过是将逻辑字符转换为物理字节的约定。最简单和最著名的编码是 ASCII,如果你用英语写作,它就是你所需要的。对于其他语言,您将需要更完整的编码,因为任何 Unicode 风格都是当今最安全的选择。
因此,简而言之,尝试“在不使用编码的情况下获取字符串的字节”与“在不使用任何语言的情况下编写文本”一样不可能。
顺便说一句,我强烈建议您(以及任何人,就此而言)阅读这个小智慧:每个软件开发人员绝对、肯定必须了解 Unicode 和字符集的绝对最低要求(没有借口!)
C# 将 a 转换string
为byte
数组:
public static byte[] StrToByteArray(string str)
{
System.Text.UTF8Encoding encoding=new System.Text.UTF8Encoding();
return encoding.GetBytes(str);
}
byte[] strToByteArray(string str)
{
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
return enc.GetBytes(str);
}
您可以使用以下代码在字符串和字节数组之间进行转换。
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);
随着Span<T>
C# 7.2 的发布,将字符串的底层内存表示捕获到托管字节数组中的规范技术是:
byte[] bytes = "rubbish_\u9999_string".AsSpan().AsBytes().ToArray();
将其转换回来应该是一个非首发,因为这意味着您实际上是在以某种方式解释数据,但为了完整起见:
string s;
unsafe
{
fixed (char* f = &bytes.AsSpan().NonPortableCast<byte, char>().DangerousGetPinnableReference())
{
s = new string(f);
}
}
名称NonPortableCast
和DangerousGetPinnableReference
应该进一步说明您可能不应该这样做。
请注意,使用Span<T>
需要安装System.Memory NuGet 包。
无论如何,实际的原始问题和后续评论暗示底层内存没有被“解释”(我认为这意味着没有修改或读取超出按原样编写的需要),表明Stream
该类的某些实现应该使用而不是将数据作为字符串进行推理。
我不确定,但我认为字符串将其信息存储为一个字符数组,这对字节来说效率低下。具体来说,Char 的定义是“表示 Unicode 字符”。
以这个示例为例:
String str = "asdf éß";
String str2 = "asdf gh";
EncodingInfo[] info = Encoding.GetEncodings();
foreach (EncodingInfo enc in info)
{
System.Console.WriteLine(enc.Name + " - "
+ enc.GetEncoding().GetByteCount(str)
+ enc.GetEncoding().GetByteCount(str2));
}
请注意,Unicode 答案在这两种情况下都是 14 个字节,而 UTF-8 答案对于第一个只有 9 个字节,而对于第二个只有 7 个字节。
因此,如果您只想要字符串使用的字节,只需使用Encoding.Unicode
,但存储空间效率低下。
关键问题是字符串中的字形需要 32 位(字符代码为 16 位),但一个字节只有 8 位可用。除非您将自己限制为仅包含 ASCII 字符的字符串,否则不存在一对一映射。System.Text.Encoding 有很多方法可以将字符串映射到 byte[],您需要选择一种可以避免信息丢失并且在客户需要将 byte[] 映射回字符串时易于使用的方法.
Utf8 是一种流行的编码,它紧凑且无损耗。
采用:
string text = "string";
byte[] array = System.Text.Encoding.UTF8.GetBytes(text);
结果是:
[0] = 115
[1] = 116
[2] = 114
[3] = 105
[4] = 110
[5] = 103
最快的方式
public static byte[] GetBytes(string text)
{
return System.Text.ASCIIEncoding.UTF8.GetBytes(text);
}
编辑 Makotosan 评论这是现在最好的方法:
Encoding.UTF8.GetBytes(text)
最接近 OP 问题的方法是 Tom Blodget,它实际上进入对象并提取字节。我说最接近是因为它取决于字符串对象的实现。
"Can't I simply get what bytes the string has been stored in?"
当然,但这就是问题的根本错误出现的地方。String 是一个可以具有有趣数据结构的对象。我们已经知道它确实如此,因为它允许存储未配对的代理。它可能会存储长度。它可能会保留一个指向每个“配对”代理的指针,从而允许快速计数。等等。所有这些额外的字节都不是字符数据的一部分。
你想要的是数组中每个字符的字节。这就是“编码”的用武之地。默认情况下,您将获得 UTF-16LE。如果除了往返之外您不关心字节本身,那么您可以选择包括“默认”在内的任何编码,并稍后将其转换回来(假设相同的参数,例如默认编码是什么、代码点、错误修复,允许的事情,例如未配对的代理等。
但是为什么要把“编码”交给魔法呢?为什么不指定编码以便您知道要获得哪些字节?
"Why is there a dependency on character encodings?"
编码(在此上下文中)仅表示代表您的字符串的字节。不是字符串对象的字节。您想要存储字符串的字节——这是天真地提出问题的地方。您希望字符串的字节位于表示字符串的连续数组中,而不是字符串对象可能包含的所有其他二进制数据。
这意味着如何存储字符串是无关紧要的。您希望将字符串“编码”成字节数组中的字节。
我喜欢 Tom Bloget 的回答,因为他将您带到了“字符串对象的字节”方向。虽然它依赖于实现,并且因为他正在窥视内部,所以可能很难重建字符串的副本。
Mehrdad 的回答是错误的,因为它在概念层面具有误导性。您仍然有一个经过编码的字节列表。他的特殊解决方案允许保留未配对的代理——这取决于实现。GetBytes
如果默认情况下以 UTF-8 格式返回字符串,他的特定解决方案将无法准确生成字符串的字节。
我已经改变了主意(Mehrdad 的解决方案)——这没有得到字符串的字节;而是获取从字符串创建的字符数组的字节。无论编码如何,c# 中的 char 数据类型都是固定大小的。这允许产生一致长度的字节数组,并且允许基于字节数组的大小来再现字符数组。因此,如果编码是 UTF-8,但每个 char 是 6 个字节以容纳最大的 utf8 值,它仍然可以工作。确实如此——字符的编码并不重要。
但是使用了转换——每个字符都被放入一个固定大小的框(c# 的字符类型)。但是,该表示是什么并不重要,这在技术上是 OP 的答案。所以 - 如果你无论如何都要转换......为什么不'编码'?
如何在不手动指定特定编码的情况下将字符串转换为 .NET (C#) 中的 byte[]?
.NET 中的字符串将文本表示为 UTF-16 代码单元序列,因此字节已在内存中以 UTF-16 编码。
迈赫达德的回答
您可以使用Mehrdad 的答案,但它确实使用了编码,因为字符是 UTF-16。它调用 ToCharArray ,它查看源创建 achar[]
并将内存直接复制到它。然后它将数据复制到也分配的字节数组中。因此,在后台,它复制了两次底层字节并分配了一个在调用后未使用的 char 数组。
汤姆布洛杰特的回答
Tom Blodget 的答案比 Mehrdad 快 20-30%,因为它跳过了分配 char 数组并将字节复制到其中的中间步骤,但它需要您使用该/unsafe
选项进行编译。如果您绝对不想使用编码,我认为这是要走的路。如果你把你的加密登录放在fixed
块中,你甚至不需要分配一个单独的字节数组并将字节复制到它。
另外,为什么要考虑编码?我不能简单地获取字符串存储在哪些字节中吗?为什么依赖于字符编码?
因为这是正确的做法。 string
是一个抽象。
如果您有包含无效字符的“字符串”,使用编码可能会给您带来麻烦,但这不应该发生。如果您使用无效字符将数据放入字符串中,那么您做错了。您可能应该使用字节数组或 Base64 编码开始。
如果您使用System.Text.Encoding.Unicode
,您的代码将更有弹性。您不必担心您的代码将在其上运行的系统的字节顺序。如果下一个版本的 CLR 将使用不同的内部字符编码,您不必担心。
我认为问题不是你为什么要担心编码,而是你为什么要忽略它并使用其他东西。编码旨在以字节序列表示字符串的抽象。 System.Text.Encoding.Unicode
将为您提供一点字节序编码,并将在现在和将来的每个系统上执行相同的操作。
您可以使用以下代码将 a 转换string
为byte array
.NET
string s_unicode = "abcéabc";
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(s_unicode);
如果你真的想要一个字符串的底层字节的副本,你可以使用一个类似下面的函数。但是,您不应该继续阅读以找出原因。
[DllImport(
"msvcrt.dll",
EntryPoint = "memcpy",
CallingConvention = CallingConvention.Cdecl,
SetLastError = false)]
private static extern unsafe void* UnsafeMemoryCopy(
void* destination,
void* source,
uint count);
public static byte[] GetUnderlyingBytes(string source)
{
var length = source.Length * sizeof(char);
var result = new byte[length];
unsafe
{
fixed (char* firstSourceChar = source)
fixed (byte* firstDestination = result)
{
var firstSource = (byte*)firstSourceChar;
UnsafeMemoryCopy(
firstDestination,
firstSource,
(uint)length);
}
}
return result;
}
此函数将很快为您获取字符串底层字节的副本。您将以它们在系统上编码的任何方式获取这些字节。这种编码几乎可以肯定是 UTF-16LE,但这是您不必关心的实现细节。
打电话会更安全、更简单、更可靠,
System.Text.Encoding.Unicode.GetBytes()
这很可能会给出相同的结果,更容易键入,并且字节将往返,以及 Unicode 中的字节表示可以调用
System.Text.Encoding.Unicode.GetString()
这是我String
对Byte[]
转换的不安全实现:
public static unsafe Byte[] GetBytes(String s)
{
Int32 length = s.Length * sizeof(Char);
Byte[] bytes = new Byte[length];
fixed (Char* pInput = s)
fixed (Byte* pBytes = bytes)
{
Byte* source = (Byte*)pInput;
Byte* destination = pBytes;
if (length >= 16)
{
do
{
*((Int64*)destination) = *((Int64*)source);
*((Int64*)(destination + 8)) = *((Int64*)(source + 8));
source += 16;
destination += 16;
}
while ((length -= 16) >= 16);
}
if (length > 0)
{
if ((length & 8) != 0)
{
*((Int64*)destination) = *((Int64*)source);
source += 8;
destination += 8;
}
if ((length & 4) != 0)
{
*((Int32*)destination) = *((Int32*)source);
source += 4;
destination += 4;
}
if ((length & 2) != 0)
{
*((Int16*)destination) = *((Int16*)source);
source += 2;
destination += 2;
}
if ((length & 1) != 0)
{
++source;
++destination;
destination[0] = source[0];
}
}
}
return bytes;
}
它比公认的 anwser 快得多,即使不像它那样优雅。这是我超过 10000000 次迭代的秒表基准测试:
[Second String: Length 20]
Buffer.BlockCopy: 746ms
Unsafe: 557ms
[Second String: Length 50]
Buffer.BlockCopy: 861ms
Unsafe: 753ms
[Third String: Length 100]
Buffer.BlockCopy: 1250ms
Unsafe: 1063ms
为了使用它,您必须在项目构建属性中勾选“允许不安全代码”。根据 .NET Framework 3.5,此方法也可以用作字符串扩展:
public static unsafe class StringExtensions
{
public static Byte[] ToByteArray(this String s)
{
// Method Code
}
}
在被问及您打算如何处理这些字节时,您回答:
我要加密它。我可以在不转换的情况下对其进行加密,但我仍然想知道为什么要在这里使用编码。只要给我字节就是我所说的。
无论您是打算通过网络发送此加密数据,稍后将其加载回内存,还是将其流式传输到另一个进程,您显然都打算在某个时候对其进行解密。在这种情况下,答案是您正在定义一个通信协议。不应根据您的编程语言及其相关运行时的实现细节来定义通信协议。有几个原因:
对于通信(与完全不同的进程或将来与相同的程序),您需要严格定义您的协议,以尽量减少使用它的难度或意外产生错误。依赖于.NET 的内部表示不是一个严格的、清晰的,甚至是不保证是一致的定义。标准编码是一个严格的定义,将来不会让您失望。
换句话说,如果不指定编码,您将无法满足您对一致性的要求。
如果您发现由于 .NET 在内部使用它或出于任何其他原因,您的进程性能明显更好,您当然可以选择直接使用 UTF-16,但您需要显式选择该编码并在代码中显式执行这些转换,而不是依赖关于 .NET 的内部实现。
所以选择一种编码并使用它:
using System.Text;
// ...
Encoding.Unicode.GetBytes("abc"); # UTF-16 little endian
Encoding.UTF8.GetBytes("abc")
如您所见,与实现您自己的读取器/写入器方法相比,仅使用内置编码对象实际上也需要更少的代码。
由于以下事实,可以通过几种不同的方式将字符串转换为字节数组:.NET 支持 Unicode,并且 Unicode 标准化了几种称为 UTF 的不同编码。它们具有不同长度的字节表示,但在这个意义上是等效的,当一个字符串被编码时,它可以被编码回字符串,但是如果字符串是用一个 UTF 编码并在假设不同的 UTF 的情况下解码,如果可以被搞砸向上。
此外,.NET 支持非 Unicode 编码,但它们在一般情况下无效(仅当在实际字符串(例如 ASCII)中使用有限的 Unicode 代码点子集时才有效)。在内部,.NET 支持 UTF-16,但对于流表示,通常使用 UTF-8。它也是 Internet 的标准事实。
毫不奇怪,将 string 序列化为字节数组和反序列化由 class 支持,该 classSystem.Text.Encoding
是一个抽象类;它的派生类支持具体的编码:ASCIIEncoding
和四个 UTF(System.Text.UnicodeEncoding
支持 UTF-16)
参考这个链接。
使用 . 序列化为字节数组System.Text.Encoding.GetBytes
。对于逆运算使用System.Text.Encoding.GetChars
. 此函数返回一个字符数组,因此要获取字符串,请使用字符串构造函数System.String(char[])
。
参考这个页面。
例子:
string myString = //... some string
System.Text.Encoding encoding = System.Text.Encoding.UTF8; //or some other, but prefer some UTF is Unicode is used
byte[] bytes = encoding.GetBytes(myString);
//next lines are written in response to a follow-up questions:
myString = new string(encoding.GetChars(bytes));
byte[] bytes = encoding.GetBytes(myString);
myString = new string(encoding.GetChars(bytes));
byte[] bytes = encoding.GetBytes(myString);
//how many times shall I repeat it to show there is a round-trip? :-)
这是因为,正如泰勒所说的那样,“字符串不是纯粹的数据。它们也有信息。” 在这种情况下,信息是在创建字符串时假定的编码。
这是基于 OP 对他自己的问题的评论,如果我理解 OP 对用例的提示,这是正确的问题。
由于上面提到的假设编码,将二进制数据存储在字符串中可能是错误的方法!无论将二进制数据存储在 a string
(而不是byte[]
更合适的数组)中的任何程序或库都已经在战斗开始之前就输了。如果他们以 REST 请求/响应或任何必须传输字符串的方式向您发送字节,那么Base64将是正确的方法。
其他人都错误地回答了这个不正确的问题。
如果字符串看起来不错,只需选择一种编码(最好是以 UTF 开头的编码),使用相应的System.Text.Encoding.???.GetBytes()
函数,并告诉谁您将字节提供给您选择的编码。
如果您将.NET Core或System.Memory用于.NET Framework,则可以通过Span<T>和Memory<T>获得一种非常有效的封送机制,可以有效地将字符串内存重新解释为字节范围。一旦你有了一个字节跨度,你就可以自由地编组回另一种类型,或者将跨度复制到一个数组中进行序列化。
总结一下其他人所说的话:
public static class MarshalExtensions
{
public static ReadOnlySpan<byte> AsBytes(this string value) => MemoryMarshal.AsBytes(value.AsSpan());
public static string AsString(this ReadOnlySpan<byte> value) => new string(MemoryMarshal.Cast<byte, char>(value));
}
static void Main(string[] args)
{
string str1 = "你好,世界";
ReadOnlySpan<byte> span = str1.AsBytes();
string str2 = span.AsString();
byte[] bytes = span.ToArray();
Debug.Assert(bytes.Length > 0);
Debug.Assert(str1 == str2);
}
在 C++ 中,这大致相当于reinterpret_cast,而在 C 中,这大致相当于转换为系统的单词类型(char)。
在最新版本的.NET Core 运行时 (CoreCLR)中,跨度操作有效地调用编译器内在函数和各种优化,这些优化有时可以消除边界检查,从而在保持内存安全的同时实现卓越的性能,假设您的内存是由 CLR 和跨度不是从非托管内存分配器的指针派生的。
这使用 CLR 支持的机制,该机制从字符串返回ReadOnlySpan<char> ;此外,此跨度不一定包含完整的内部字符串布局。ReadOnlySpan<T>意味着如果您需要执行突变,则必须创建一个副本,因为字符串是不可变的。
bytes[] buffer = UnicodeEncoding.UTF8.GetBytes(string something); //for converting to UTF then get its bytes
bytes[] buffer = ASCIIEncoding.ASCII.GetBytes(string something); //for converting to ascii then get its bytes
只需使用这个:
byte[] myByte= System.Text.ASCIIEncoding.Default.GetBytes(myString);
两种方式:
public static byte[] StrToByteArray(this string s)
{
List<byte> value = new List<byte>();
foreach (char c in s.ToCharArray())
value.Add(c.ToByte());
return value.ToArray();
}
和,
public static byte[] StrToByteArray(this string s)
{
s = s.Replace(" ", string.Empty);
byte[] buffer = new byte[s.Length / 2];
for (int i = 0; i < s.Length; i += 2)
buffer[i / 2] = (byte)Convert.ToByte(s.Substring(i, 2), 16);
return buffer;
}
我倾向于使用底部比顶部更频繁,没有对它们进行速度基准测试。
使用 LINQ 的简单代码
string s = "abc"
byte[] b = s.Select(e => (byte)e).ToArray();
编辑:如下所述,这不是一个好方法。
但您仍然可以使用它通过更合适的编码来理解 LINQ:
string s = "abc"
byte[] b = s.Cast<byte>().ToArray();
字符既是字体表的查找键,又是词汇传统,例如排序、大小写版本等。
因此,字符不是字节(8 位),字节也不是字符。特别是,一个字节的 256 个排列不能容纳某些书面语言中的数千个符号,更不用说所有语言了。因此,已经设计了各种编码字符的方法。一些为特定类别的语言编码(ASCII 编码);使用代码页的多种语言(扩展 ASCII);或者,雄心勃勃地,通过根据需要选择性地包括额外的字节,所有语言,Unicode。
在诸如 .NET 框架之类的系统中,字符串意味着特定的字符编码。在 .NET 中,这种编码是 Unicode。由于框架默认读取和写入 Unicode,因此在 .NET 中通常不需要处理字符编码。
但是,一般来说,要将字符串从字节流加载到系统中,您需要知道源编码才能正确解释并随后正确翻译它(否则代码将被视为已经在系统的默认编码中并因此呈现胡言乱语)。同样,当将字符串写入外部源时,它将以特定编码写入。
我编写了一个类似于接受的答案的 Visual Basic 扩展,但直接使用 .NET 内存和编组进行转换,它支持其他方法不支持的字符范围,例如UnicodeEncoding.UTF8.GetString
或UnicodeEncoding.UTF32.GetString
什至MemoryStream and BinaryFormatter
(无效字符,例如:&
ChrW(55906)
& ChrW(55655)
):
<Extension> _
Public Function ToBytesMarshal(ByRef str As String) As Byte()
Dim gch As GCHandle = GCHandle.Alloc(str, GCHandleType.Pinned)
Dim handle As IntPtr = gch.AddrOfPinnedObject
ToBytesMarshal = New Byte(str.Length * 2 - 1) {}
Try
For i As Integer = 0 To ToBytesMarshal.Length - 1
ToBytesMarshal.SetValue(Marshal.ReadByte(IntPtr.Add(handle, i)), i)
Next
Finally
gch.Free()
End Try
End Function
<Extension> _
Public Function ToStringMarshal(ByRef arr As Byte()) As String
Dim gch As GCHandle = GCHandle.Alloc(arr, GCHandleType.Pinned)
Try
ToStringMarshal = Marshal.PtrToStringAuto(gch.AddrOfPinnedObject)
Finally
gch.Free()
End Try
End Function
要将字符串转换为 byte[],请使用以下解决方案:
string s = "abcdefghijklmnopqrstuvwxyz";
byte[] b = System.Text.UTF32Encoding.GetBytes(s);
我希望它有所帮助。
从byte[]
到string
:
return BitConverter.ToString(bytes);
// C# to convert a string to a byte array.
public static byte[] StrToByteArray(string str)
{
System.Text.ASCIIEncoding encoding=new System.Text.ASCIIEncoding();
return encoding.GetBytes(str);
}
// C# to convert a byte array to a string.
byte [] dBytes = ...
string str;
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
str = enc.GetString(dBytes);
这是代码:
// 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);
}
对于串行通信项目,我必须将字符串转换为字节数组 - 我必须处理 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];
OP 的问题:“如何将 a 转换为.NET (C#)string
中的数组?” byte
[原文如此]
您可以使用以下代码:
static byte[] ConvertString (string s) {
return new byte[0];
}
作为一个好处,编码无关紧要!哦,等等,这是一个编码......它只是微不足道且高度有损。