提问人:Agnel Kurian 提问时间:1/23/2009 最后编辑:John SmithAgnel Kurian 更新时间:10/4/2022 访问量:1186731
如何在不手动指定编码的情况下在 C# 中获得字符串的一致字节表示形式?
How do I get a consistent byte representation of strings in C# without manually specifying an encoding?
问:
如何在 .NET (C#) 中将 a 转换为 a,而无需手动指定特定编码?string
byte[]
我将加密字符串。我可以在不转换的情况下对其进行加密,但我仍然想知道为什么编码会在这里发挥作用。
另外,为什么要考虑编码?我不能简单地获取字符串存储在哪些字节中吗?为什么依赖于字符编码?
答:
byte[] strToByteArray(string str)
{
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
return enc.GetBytes(str);
}
评论
例如:
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 Framework 使用 UTF-16 来表示字符串,因此,如果您只想获取 .NET 使用的确切字节,请使用 .System.Text.Encoding.Unicode.GetBytes (...)
有关更多信息,请参见 .NET Framework 中的字符编码 (MSDN)。
评论
// 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);
评论
您需要考虑编码,因为 1 个字符可以由 1 个或多个字节(最多约 6 个)表示,并且不同的编码会以不同的方式处理这些字节。
乔尔对此发表了一篇帖子:
评论
关键问题是字符串中的字形需要 32 位(字符代码为 16 位),但一个字节只有 8 位备用。除非您将自己限制为仅包含 ASCII 字符的字符串,否则不存在一对一映射。System.Text.Encoding 有很多方法可以将字符串映射到 byte[],您需要选择一种能够避免信息丢失的方法,并且当客户端需要将 byte[] 映射回字符串时,该方法易于客户端使用。
Utf8 是一种流行的编码,它紧凑且无损。
评论
我不确定,但我认为字符串将其信息存储为字符数组,这对于字节来说效率低下。具体来说,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
评论
您的问题的第一部分(如何获取字节)已经由其他人回答:查看命名空间。System.Text.Encoding
我将解决您的后续问题:为什么需要选择编码?为什么你不能从字符串类本身得到它?
答案分为两部分。
首先,字符串类内部使用的字节并不重要,每当你认为它们这样做时,你都可能会引入一个错误。
如果你的程序完全在 .Net 世界中,那么你根本不需要担心获取字符串的字节数组,即使你通过网络发送数据也是如此。相反,请使用 .Net 序列化来担心传输数据。您不再担心实际的字节数:序列化格式化程序会为您完成这项工作。
另一方面,如果您将这些字节发送到无法保证会从 .Net 序列化流中提取数据的位置,该怎么办?在这种情况下,您确实需要担心编码,因为显然,这个外部系统很关心。因此,字符串使用的内部字节无关紧要:您需要选择一种编码,以便可以在接收端明确此编码,即使它与 .Net 内部使用的编码相同。
我知道在这种情况下,您可能更愿意在可能的情况下使用字符串变量在内存中存储的实际字节,这样可以节省一些创建字节流的工作。但是,我告诉你,与确保你的输出在另一端被理解,并保证你的编码必须明确相比,这并不重要。此外,如果您真的想匹配您的内部字节,您已经可以选择编码,并获得这些性能节省。Unicode
这就把我带到了第二部分......选择编码是告诉 .Net 使用底层字节。您确实需要选择这种编码,因为当一些新奇的 Unicode-Plus 出现时,.Net 运行时需要自由使用这种更新、更好的编码模型,而不会破坏您的程序。但是,就目前(以及可预见的未来)而言,只需选择 Unicode 编码即可满足您的需求。Unicode
了解字符串必须重写为连线也很重要,即使使用匹配的编码,这至少也涉及位模式的一些转换。计算机需要考虑诸如大端与小端序、网络字节顺序、数据包化、会话信息等内容。
评论
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());
评论
两种方式:
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;
}
我倾向于使用底部的比顶部更频繁,没有对它们的速度进行基准测试。
评论
另外,请解释为什么应该考虑编码。 我不能简单地获取字符串存储在哪些字节中吗? 为什么这种对编码的依赖性?!!!
因为没有“字符串的字节”这样的东西。
字符串(或更一般地说,文本)由字符组成:字母、数字和其他符号。就这样。然而,计算机对字符一无所知;它们只能处理字节。因此,如果要使用计算机存储或传输文本,则需要将字符转换为字节。你是怎么做到的?这就是编码出现的地方。
编码只不过是将逻辑字符转换为物理字节的约定。最简单和最著名的编码是 ASCII,如果你用英语写作,它就是你所需要的。对于其他语言,您将需要更完整的编码,因为任何Unicode风格都是当今最安全的选择。
因此,简而言之,试图“在不使用编码的情况下获取字符串的字节”与“在不使用任何语言的情况下编写文本”一样不可能。
顺便说一句,我强烈建议你(以及任何人,就此而言)阅读这一小段智慧: 绝对最低限度 每个软件开发人员绝对、肯定必须了解 Unicode 和字符集(没有借口!
评论
最快的方式
public static byte[] GetBytes(string text)
{
return System.Text.ASCIIEncoding.UTF8.GetBytes(text);
}
编辑,正如 Makotosan 评论的那样,这是现在最好的方法:
Encoding.UTF8.GetBytes(text)
评论
好吧,我已经阅读了所有答案,它们都是关于使用编码的,或者是关于删除未配对代理项的序列化。
例如,当字符串来自 SQL Server 时,情况很糟糕,因为 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);
}
评论
Convert.ToBase64String(arr);
byte[] (data) <-> string (serialized data to store in XML file)
byte[] (data)
String
String (binary data) <-> byte[] (easy accessible binary data)
试试这个,代码少得多:
System.Text.Encoding.UTF8.GetBytes("TEST String");
评论
System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép);
System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép").Length != System.Text.Encoding.UTF8.GetBytes("Arvizturo tukorfurogep").Length
"Árvíztűrő tükörfúrógép".Length == "Arvizturo tukorfurogep".Length
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
公认的答案非常非常复杂。为此,请使用包含的 .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);
如果你没有必要,就不要重新发明轮子......
评论
System.Text.Encoding.Unicode
System.Text.Encoding.Unicode.GetBytes
与这里的答案相反,如果不需要解释字节,则无需担心编码!
就像你提到的,你的目标很简单,就是“获取字符串存储在哪些字节中”。
(当然,能够从字节重新构造字符串。
对于这些目标,老实说,我不明白为什么人们总是告诉你你需要编码。您当然不需要为此担心编码。
只需这样做:
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);
}
只要你的程序(或其他程序)不尝试以某种方式解释字节,你显然没有提到你打算这样做,那么这种方法就没有错!担心编码只会让你的生活无缘无故地变得更加复杂。
这种方法的其他好处是:字符串是否包含无效字符并不重要,因为您仍然可以获取数据并重建原始字符串!
它的编码和解码方式是一样的,因为你只是在看字节。
但是,如果您使用特定的编码,则会给您编码/解码无效字符带来麻烦。
评论
GetString
GetBytes
为了证明 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
每次这个问题有动静时,我仍然在考虑一个序列化程序(无论是来自 Microsoft 还是来自 3rd 方组件),即使它包含不成对的代理字符,它也可以持久化字符串;我时不时地用谷歌搜索:序列化未配对代理字符 .NET。这不会让我失眠,但是当时不时有人评论我的答案有缺陷时,这有点烦人,但当涉及到未配对的代理字符时,他们的答案同样有缺陷。
的,Microsoft 应该刚刚在其 ツSystem.Buffer.BlockCopy
BinaryFormatter
谢谢!
评论
System.Buffer.BlockCopy
FFFD
System.String
Char
String
Char[]
Char[]
Char[]
使用 LINQ 编写简单代码
string s = "abc"
byte[] b = s.Select(e => (byte)e).ToArray();
编辑:如下所述,这不是一个好方法。
但你仍然可以用它来理解 LINQ 用更合适的编码:
string s = "abc"
byte[] b = s.Cast<byte>().ToArray();
评论
Encoding.Default.GetBytes(s)
Encoding.Default.GetBytes(s)
€
short
byte
这是我对转换的不安全实现: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,此方法也可以用作 String 扩展:
public static unsafe class StringExtensions
{
public static Byte[] ToByteArray(this String s)
{
// Method Code
}
}
评论
RuntimeHelpers.OffsetToStringData
代码如下:
// 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);
}
C# 将 a 转换为数组:string
byte
public static byte[] StrToByteArray(string str)
{
System.Text.UTF8Encoding encoding=new System.Text.UTF8Encoding();
return encoding.GetBytes(str);
}
您可以使用以下代码在 .NET 中将 a 转换为 astring
byte array
string s_unicode = "abcéabc";
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(s_unicode);
OP 的问题:“如何在 .NET (C#) 中将 a 转换为数组?[原文如此]string
byte
您可以使用以下代码:
static byte[] ConvertString (string s) {
return new byte[0];
}
好处是,编码无关紧要!哦,等等,这是一个生态......这只是微不足道的,而且损失很大。
评论
用:
string text = "string";
byte[] array = System.Text.Encoding.UTF8.GetBytes(text);
结果是:
[0] = 115
[1] = 116
[2] = 114
[3] = 105
[4] = 110
[5] = 103
评论
这是一个流行的问题。重要的是要了解作者在问什么,并且它与最常见的需求不同。为了防止在不需要的地方滥用代码,我首先回答了后者。
共同需求
每个字符串都有一个字符集和编码。当您将对象转换为数组时,您仍然具有字符集和编码。对于大多数用法,你会知道需要哪种字符集和编码,而 .NET 使“通过转换进行复制”变得简单。只需选择合适的班级即可。System.String
System.Byte
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"
显然,转换不一定是无损的!
注意:源字符集为 Unicode。System.String
唯一令人困惑的是,.NET 使用字符集的名称作为该字符集的一个特定编码的名称。 应该称为 .Encoding.Unicode
Encoding.UTF16
对于大多数用法来说,就是这样。如果这是您需要的,请停止阅读此处。如果您不了解什么是编码,请参阅有趣的 Joel Spolsky 文章。
特殊需求
现在,作者问的问题是,“每个字符串都存储为字节数组,对吧?为什么我不能简单地拥有这些字节?
他不想要任何转变。
从 C# 规范:
C# 中的字符和字符串处理使用 Unicode 编码。字符 type 表示 UTF-16 代码单元,string 类型表示 UTF-16 代码单元序列。
所以,我们知道,如果我们要求 null 转换(即从 UTF-16 到 UTF-16),我们将得到所需的结果:
Encoding.Unicode.GetBytes(".NET String to byte array")
但为了避免提及编码,我们必须以另一种方式做到这一点。如果中间数据类型是可以接受的,则有一个概念快捷方式:
".NET String to byte array".ToCharArray()
这并不能让我们获得所需的数据类型,但 Mehrdad 的答案显示了如何使用 BlockCopy 将此 Char 数组转换为 Byte 数组。但是,这会将字符串复制两次!而且,它过于明确地使用特定于编码的代码: 数据类型 。System.Char
获取存储 String 的实际字节的唯一方法是使用指针。该语句允许获取值的地址。从 C# 规范:fixed
[对于] 字符串类型的表达式,...初始值设定项计算 字符串中第一个字符的地址。
为此,编译器编写代码时会使用 .因此,要获取原始字节,只需创建一个指向字符串的指针并复制所需的字节数。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;
}
正如@CodesInChaos所指出的,结果取决于机器的字节序。但问题作者并不关心这一点。
评论
Length
String
Char
new String(new []{'\uD800', '\u0030'})
Globalization.SortKey
KeyData
String
String.CompareOrdinal
SortKey.Compare
SortKey
memcmp
KeyData
Byte[]
String
若要将字符串转换为 byte[],请使用以下解决方案:
string s = "abcdefghijklmnopqrstuvwxyz";
byte[] b = System.Text.UTF32Encoding.GetBytes(s);
我希望它有所帮助。
评论
s.Select(e => (byte)e)
char
byte[] b = System.Text.UTF32Encoding.GetBytes(s);
由于以下事实,可以通过几种不同的方式将字符串转换为字节数组:.NET 支持 Unicode,并且 Unicode 标准化了几种称为 UTF 的不同编码。它们具有不同长度的字节表示,但从这个意义上说是等价的,当一个字符串被编码时,它可以被编码回字符串,但如果字符串用一个 UTF 编码并在假设不同的 UTF 的情况下解码,如果可以搞砸。
此外,.NET 支持非 Unicode 编码,但它们在一般情况下无效(仅当在实际字符串中使用 Unicode 码位的有限子集(如 ASCII)时才有效)。在内部,.NET 支持 UTF-16,但对于流表示形式,通常使用 UTF-8。它也是互联网事实上的标准。
毫不奇怪,类支持将字符串序列化为字节数组和反序列化,这是一个抽象类;它的派生类支持具体编码:和四个 UTF(支持 UTF-16)System.Text.Encoding
ASCIIEncoding
System.Text.UnicodeEncoding
参考此链接。
对于使用 序列化为字节数组。对于逆运算,请使用 .此函数返回一个字符数组,因此要获取字符串,请使用字符串构造函数。
参考此页面。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? :-)
字符既是字体表的查找键,又是词汇传统,例如排序、大小写版本等。
因此,字符不是字节(8 位),字节也不是字符。特别是,一个字节的 256 种排列无法容纳某些书面语言中的数千个符号,更不用说所有语言了。因此,已经设计了各种字符编码方法。有些编码针对特定类别的语言(ASCII 编码);使用代码页的多种语言(扩展 ASCII);或者,雄心勃勃地,通过根据需要有选择地包含额外的字节,Unicode,所有语言。
在系统(如 .NET Framework)中,String 表示特定字符编码。在 .NET 中,此编码为 Unicode。由于框架默认读取和写入 Unicode,因此在 .NET 中通常不需要处理字符编码。
但是,通常,要将字符串从字节流加载到系统中,您需要知道源编码,以便正确解释并随后转换它(否则代码将被视为已经在系统的默认编码中,从而呈现乱码)。同样,当字符串写入外部源时,它将以特定的编码写入。
评论
您可以使用以下代码在字符串和字节数组之间进行转换。
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);
评论
如果确实想要字符串基础字节的副本,可以使用如下所示的函数。但是,您不应该继续阅读以找出原因。
[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()
评论
Unicode.GetBytes()
Unicode.GetString()
string
Marshal.Copy
从到:byte[]
string
return BitConverter.ToString(bytes);
只需使用这个:
byte[] myByte= System.Text.ASCIIEncoding.Default.GetBytes(myString);
评论
System.Text.ASCIIEncoding.Default.GetBytes("Árvíztűrő tükörfúrógép.").ToString();
"Árvizturo tukörfurogép."
最接近 OP 问题的方法是 Tom Blodget 的问题,它实际上进入了对象并提取了字节。我说最接近是因为它取决于 String 对象的实现。
"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的回应是错误的,因为它在概念层面上具有误导性。您仍然有一个已编码的字节列表。他的特定解决方案允许保留未配对的代理项 - 这取决于实现。如果默认情况下以 UTF-8 格式返回字符串,他的特定解决方案将无法准确生成字符串的字节。GetBytes
我已经改变了主意(Mehrdad 的解决方案)——这不是获取字符串的字节;相反,它获取从字符串创建的字符数组的字节。无论编码如何,c# 中的 char 数据类型都是固定大小的。这允许生成一致长度的字节数组,并允许根据字节数组的大小复制字符数组。因此,如果编码是 UTF-8,但每个字符是 6 个字节以容纳最大的 utf8 值,它仍然有效。所以确实 -- 字符的编码并不重要。
但是使用了转换 - 每个字符都放在一个固定大小的框中(c# 的字符类型)。然而,这种表示是什么并不重要,从技术上讲,这是 OP 的答案。所以,如果你无论如何都要皈依......为什么不“编码”?
评论
(Char) 55906
(Char) 55655
对于串行通信项目,我必须将字符串转换为字节数组 - 我必须处理 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];
评论
我编写了一个类似于公认的答案的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
这取决于你想要字节的用途
这是因为,正如 Tyler 所说,“字符串不是纯粹的数据。他们也有信息。在本例中,信息是创建字符串时假定的编码。
假设二进制数据(而不是文本)存储在字符串中
这是基于 OP 对他自己的问题的评论,如果我理解 OP 对用例的暗示,这是正确的问题。
将二进制数据存储在字符串中可能是错误的方法,因为上面提到了假设的编码!无论将二进制数据存储在一个(而不是更合适的数组)中的程序或库,在战斗开始之前就已经输掉了战斗。如果他们在 REST 请求/响应或任何必须传输字符串的内容中将字节发送给您,那么 Base64 将是正确的方法。string
byte[]
如果文本字符串的编码未知
其他人都错误地回答了这个错误的问题。
如果字符串看起来不错,只需选择一种编码(最好是以 UTF 开头的编码),使用相应的函数,并告诉谁你把字节交给了你选择的编码。System.Text.Encoding.???.GetBytes()
随着 C# 7.2 发布的 Span<T>
的出现,将字符串的基础内存表示形式捕获到托管字节数组中的规范技术是:
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
评论
new string(f)
是错误的,如果您想要往返所有字符串的希望,您至少需要使用接受显式长度的构造函数重载。
如何在 .NET (C#) 中将字符串转换为 byte[],而无需手动指定特定编码?
.NET 中的字符串将文本表示为 UTF-16 代码单元的序列,因此字节已在内存中以 UTF-16 编码。
Mehrdad's Answer(梅尔达德)的回答
你可以使用 Mehrdad 的答案,但它实际上使用了编码,因为字符是 UTF-16。它调用 ToCharArray,后者查看源创建一个并将内存直接复制到其中。然后,它将数据复制到也分配的字节数组中。因此,在后台,它会复制底层字节两次,并分配一个在调用后未使用的 char 数组。char[]
Tom Blodget's Answer
Tom Blodget 的答案比 Mehrdad 快 20-30%,因为它跳过了分配 char 数组并将字节复制到其中的中间步骤,但它需要您使用该选项进行编译。如果你绝对不想使用编码,我认为这是要走的路。如果你把你的加密登录名放在块里,你甚至不需要分配一个单独的字节数组并将字节复制到它。/unsafe
fixed
另外,为什么要考虑编码?我不能简单地获取字符串存储在哪些字节中吗?为什么依赖于字符编码?
因为这是正确的方法。 是一个抽象。string
如果您的“字符串”包含无效字符,则使用编码可能会给您带来麻烦,但这不应该发生。如果您使用无效字符将数据输入到字符串中,则您做错了。您可能应该首先使用字节数组或 Base64 编码。
如果使用 ,您的代码将更具弹性。您不必担心运行代码的系统的字节序。您无需担心下一版本的 CLR 是否会使用不同的内部字符编码。System.Text.Encoding.Unicode
我认为问题不在于你为什么要担心编码,而在于你为什么要忽略它并使用其他东西。编码旨在表示字节序列中字符串的抽象。 将为您提供一个小的字节序顺序编码,并将在现在和将来的每个系统上执行相同的编码。System.Text.Encoding.Unicode
评论
当被问到你打算用这些字节做什么时,你回答说:
我要加密它。我可以在不转换的情况下对其进行加密,但我仍然想知道为什么编码会在这里发挥作用。只要给我字节就是我说的。
无论您打算通过网络发送此加密数据,稍后将其加载回内存,还是将其流式传输到另一个进程,您显然都打算在某个时候对其进行解密。在这种情况下,答案是您正在定义通信协议。不应根据编程语言及其相关运行时的实现细节来定义通信协议。这有几个原因:
- 您可能需要与以不同语言或运行时实现的进程进行通信。(例如,这可能包括在另一台计算机上运行的服务器或将字符串发送到 JavaScript 浏览器客户端。
- 该程序将来可能会以不同的语言或运行时重新实现。
- .NET 实现可能会更改字符串的内部表示形式。你可能认为这听起来很牵强,但这实际上发生在 Java 9 中,以减少内存使用。.NET 没有理由不效仿。Skeet 认为,UTF-16 在今天可能不是最佳选择,因为表情符号和其他 Unicode 块也需要超过 2 个字节来表示,这增加了内部表示在未来发生变化的可能性。
对于通信(无论是使用完全不同的过程还是将来使用相同的程序),您需要严格定义您的协议,以最大程度地减少使用它或意外创建错误的难度。根据。NET的内部表示不是一个严格、清晰,甚至不能保证一致的定义。标准编码是一个严格的定义,将来不会让您失望。
换言之,如果不指定编码,就无法满足一致性要求。
如果您发现您的进程执行得更好,因为 .NET 在内部或出于任何其他原因使用它,您当然可以选择直接使用 UTF-16,但您需要显式选择该编码并在代码中显式执行这些转换,而不是依赖于 .NET的内部实现。
因此,选择一种编码并使用它:
using System.Text;
// ...
Encoding.Unicode.GetBytes("abc"); # UTF-16 little endian
Encoding.UTF8.GetBytes("abc")
正如你所看到的,与实现你自己的读取器/写入器方法相比,仅使用内置编码对象实际上也要少一些代码。
如果将 .NET Core 或 System.Memory 用于 .NET Framework,则可以通过 Span<T> 和 Memory<T> 获得非常有效的封送处理机制,该机制可以有效地将字符串内存重新解释为字节范围。一旦你有了字节的跨度,你就可以自由地封送回另一种类型,或者将这个跨度复制到数组中进行序列化。
总结一下其他人所说的话:
- 存储此类序列化的表示形式对系统字节序、编译器优化以及对正在执行的 .NET 运行时中字符串的内部表示形式的更改很敏感。
- 避免长期储存
- 避免在其他环境中反序列化或解释字符串
- 这包括其他计算机、处理器体系结构、.NET 运行时、容器等。
- 这包括比较、格式化、加密、字符串操作、本地化、字符转换等。
- 避免对字符编码做出假设
- 在实践中,默认编码往往是 UTF-16LE,但编译器/运行时可以选择任何内部表示形式
实现
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);
}
Furthur 洞察
在 C++ 中,这大致相当于 reinterpret_cast,而 C 这大致相当于对系统词类型 (char) 的强制转换。
在最新版本的 .NET Core 运行时 (CoreCLR) 中,对跨度的操作可有效地调用编译器内部函数和各种优化,这些优化有时可以消除边界检查,从而在保持内存安全的同时实现卓越的性能,前提是内存是由 CLR 分配的,并且跨度不是从非托管内存分配器的指针派生的。
警告
这使用 CLR 支持的机制,该机制从字符串返回 ReadOnlySpan<char>;此外,此范围不一定包含完整的内部字符串布局。ReadOnlySpan<T> 表示如果需要执行变更,则必须创建副本,因为字符串是不可变的。
评论
计算机只理解原始二进制数据,原始位。 一位是二进制数字: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 “'Ç ̈” 或 OEM 437 “ßéó” 或 UTF-8 “€” 符号。
可以根据字符编码的特征和统计数据来猜测原始字节流的编码,但没有可靠的方法可以做到这一点。 在 UTF-8 中,介于 128 和 255 之间的数字本身是无效的。 é 在某些语言(法语)中很常见,因此,如果您看到许多值为 E9 的字节被字母包围,则可能是 windows1252 编码字符串,即表示 é 字符的 E9 字节。
当您有一个表示字符串的原始字节流时,最好知道匹配的编码,而不是试图猜测。
下面是曾经被广泛使用的各种编码中的一个原始字节的屏幕截图。
评论
上一个:如何在 C# 中生成随机整数?
评论