.net:如何在不手动指定编码的情况下,在c#中获得字符串的一致字节表示?

如何在.NET (c#)中将string转换为byte[]而无需手动指定特定的编码?

我要加密字符串。我可以加密它不转换,但我仍然想知道为什么编码在这里发挥作用。

另外,为什么要考虑编码?我不能简单地获取字符串存储在哪些字节中吗?为什么要依赖字符编码?


与这里的答案相反,如果不需要解释字节,则不需要担心编码!

正如您提到的,您的目标很简单,就是"获取字符串存储在哪些字节中"。(当然,还能够从字节重新构造字符串。)

对于这些目标,我真的不明白为什么人们总是告诉你需要编码。您当然不需要为此担心编码。

就这样做吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
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,…)。

例如:

1
2
byte[] b1 = System.Text.Encoding.UTF8.GetBytes (myString);
byte[] b2 = System.Text.Encoding.ASCII.GetBytes (myString);

为什么编码如此重要的一个小例子:

1
2
3
4
5
6
7
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类:

1
2
3
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);

如果没有必要,就不要重新发明轮子。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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());


您需要考虑编码,因为一个字符可以由一个或多个字节表示(最多6个字节),不同的编码将以不同的方式对待这些字节。

Joel发表了一篇文章:

The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)


这是一个很普遍的问题。理解作者所问的问题是很重要的,而且它与最常见的需求是不同的。为了防止在不需要的地方滥用代码,我先回答了后面的问题。

共同需要

每个字符串都有一个字符集和编码。当您将System.String对象转换为System.Byte数组时,您仍然有一个字符集和编码。对于大多数用法,您都知道需要哪个字符集和编码,. net使"通过转换复制"变得很简单。只需选择适当的Encoding类。

1
2
// using System.Text;
Encoding.UTF8.GetBytes(".NET String to byte array")

转换可能需要处理目标字符集或编码不支持源中的字符的情况。您有一些选择:异常、替换或跳过。默认策略是替换"?"。

1
2
3
// 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#规范:

Character and string processing in C# uses Unicode encoding. The char
type represents a UTF-16 code unit, and the string type represents a
sequence of UTF-16 code units.

因此,我们知道如果我们要求null转换(即。,从UTF-16到UTF-16),得到我们想要的结果:

1
Encoding.Unicode.GetBytes(".NET String to byte array")

但是为了避免提到编码,我们必须用另一种方法来做。如果中间数据类型是可接受的,有一个概念上的捷径:

1
".NET String to byte array".ToCharArray()

这并没有得到我们想要的数据类型,但是Mehrdad的答案显示了如何使用BlockCopy将这个Char数组转换为字节数组。但是,这将复制字符串两次!而且,它还显式地使用特定于编码的代码:数据类型System.Char

获取字符串实际存储字节的唯一方法是使用指针。fixed语句允许获取值的地址。来自c#规范:

[For] an expression of type string, ... the initializer computes the
address of the first character in the string.

为此,编译器使用RuntimeHelpers.OffsetToStringData编写代码跳过string对象的其他部分。因此,要获得原始字节,只需创建一个指向字符串的指针并复制所需的字节数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 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所指出的,结果取决于机器的endianness。但问题的作者并不关心这个。


为了证明Mehrdrad的合理答案是有效的,他的方法甚至可以保留未配对的代理字符(许多人对我的答案提出了反对意见,但每个人都犯了同样的错误,例如System.Text.Encoding.UTF8.GetBytesSystem.Text.Encoding.Unicode.GetBytes;例如,这些编码方法不能持久化高代理字符d800,而只是用值fffd替换高代理字符):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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);
    }        
}

输出:

1
2
3
4
5
6
7
8
9
T 54
e 65
s 73
t 74
? d800
T 54
e 65
s 73
t 74

用system . text . ending . utf8试试。GetBytes或System.Text.Encoding.Unicode。GetBytes,它们将仅仅用值fffd替换高代理字符

每当这个问题发生变化时,我仍然在考虑一个序列化器(无论是来自Microsoft还是来自第三方组件),它可以持久化字符串,即使它包含未配对的代理字符;我不时地抛出这个:序列化非配对代理字符。net。这并没有让我失眠,但是偶尔会有人评论我的答案是有缺陷的,这有点烦人,但是当涉及到未配对的代理角色时,他们的答案也是有缺陷的。

该死的,微软应该只是用System.Buffer.BlockCopy BinaryFormatter

谢谢!


您的问题的第一部分(如何获取字节)已经由其他人回答:查看System.Text.Encoding名称空间。

我将回答您接下来的问题:为什么需要选择编码?为什么不能从string类本身获取呢?

答案分两部分。

首先,string类在内部使用的字节并不重要,无论什么时候,只要您假定使用了这些字节,就很可能引入了一个bug。

如果您的程序完全在. net世界中,那么您根本不需要担心为字符串获取字节数组,即使您正在通过网络发送数据。相反,使用. net序列化来担心数据的传输。您不再需要担心实际的字节:序列化格式化程序将为您完成。

另一方面,如果您将这些字节发送到某个无法保证将从.Net序列化流中拉入数据的地方,会发生什么情况?在这种情况下,您肯定需要担心编码,因为显然这个外部系统关心编码。同样,字符串使用的内部字节并不重要:您需要选择一种编码,这样您就可以在接收端显式地说明这种编码,即使它与. net内部使用的编码相同。

我理解,在这种情况下,您可能更愿意在可能的情况下使用字符串变量在内存中存储的实际字节,这样可以节省创建字节流的一些工作。但是,我告诉您,与确保您的输出在另一端被理解以及确保您的编码必须显式相比,这并不重要。此外,如果您真的想匹配您的内部字节,您可以选择Unicode编码,并获得性能上的节省。

这就引出了第二部分……选择Unicode编码是在告诉. net使用底层字节。您确实需要选择这种编码,因为当一些新的Unicode-Plus出现时,. net运行时需要在不破坏程序的情况下自由地使用这种更新、更好的编码模型。但是,目前(以及可预见的将来),只要选择Unicode编码就可以得到您想要的结果。

理解您的字符串必须重新编写为wire也很重要,这至少涉及到位模式的一些转换,即使使用匹配编码也是如此。计算机需要考虑诸如大端与小端、网络字节顺序、分组、会话信息等因素。


试试这个,代码更少:

1
System.Text.Encoding.UTF8.GetBytes("TEST String");


嗯,我已经阅读了所有的答案,它们都是关于使用编码的,或者是关于删除未配对代理的序列化的。

例如,当字符串来自SQL Server(它是由存储字节数组(例如密码散列)的字节数组构建的)时,这就不好了。如果我们从它删除任何内容,它将存储一个无效的散列,如果我们想将它存储在XML中,我们希望它保持完整(因为XML编写器会在它找到的任何未配对的代理上抛出异常)。

在这种情况下,我用Base64编码字节数组,但是在互联网上,只有一个解决方案,在c#中,它有bug,而且只有一种方法,所以我修复了这个bug,写回了过程。给你们,未来的谷歌人:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 &amp; 0xFF);
        data[i * 2 + 1] = (byte)((ch &amp; 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);
}


Also please explain why encoding should be taken into consideration.
Can't I simply get what bytes the string has been stored in?
Why this dependency on encoding?!!!

因为不存在"字符串的字节数"这样的东西。

字符串(或者更一般地说,文本)由字符组成:字母、数字和其他符号。这是所有。然而,计算机对字符一无所知;它们只能处理字节。因此,如果要使用计算机存储或传输文本,需要将字符转换为字节。你是怎么做到的?这就是编码出现的地方。

编码只不过是将逻辑字符转换为物理字节的约定。最简单和最著名的编码是ASCII,如果您用英语编写,它就是您所需要的全部。对于其他语言,您将需要更完整的编码,成为当今Unicode风格中最安全的选择之一。

因此,简而言之,试图"不使用编码就获得字符串的字节数"与"不使用任何语言就编写文本"一样不可能。

顺便说一下,我强烈建议您(以及任何人)阅读这条小智慧:每个软件开发人员绝对、肯定地必须了解Unicode和字符集的绝对最小值(没有借口!)


c#将string转换为byte数组:

1
2
3
4
5
public static byte[] StrToByteArray(string str)
{
   System.Text.UTF8Encoding  encoding=new System.Text.UTF8Encoding();
   return encoding.GetBytes(str);
}

可以使用以下代码在字符串和字节数组之间进行转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
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);


1
2
3
4
5
byte[] strToByteArray(string str)
{
    System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
    return enc.GetBytes(str);
}


我不确定,但我认为字符串将其信息存储为字符数组,这对字节效率很低。具体来说,Char的定义是"表示Unicode字符"。

举个例子:

1
2
3
4
5
6
7
8
9
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字符的字符串,否则不存在一对一映射。text。编码有很多方法可以将字符串映射到byte[],您需要选择一种避免丢失信息的方法,并且当您的客户端需要将byte[]映射回字符串时,这种方法很容易使用。

Utf8是一种流行的编码方式,它紧凑且无损耗。


随着c# 7.2中发布的Span的出现,将字符串的底层内存表示捕获到托管字节数组的规范技术是:

1
byte[] bytes ="rubbish_\u9999_string".AsSpan().AsBytes().ToArray();

把它转换回来应该是不可能的,因为这意味着你实际上是在以某种方式解释数据,但为了完整性:

1
2
3
4
5
6
7
8
string s;
unsafe
{
    fixed (char* f = &amp;bytes.AsSpan().NonPortableCast<byte, char>().DangerousGetPinnableReference())
    {
        s = new string(f);
    }
}

NonPortableCastDangerousGetPinnableReference这两个名称应该进一步说明,您可能不应该这样做。

注意,使用Span需要安装系统。内存NuGet包。

无论如何,实际的原始问题和后续的评论暗示底层的内存没有被"解释"(我假设意味着不修改或阅读超出了需要编写按原样),表明一些Stream类的实现应该用来代替推理作为字符串的数据。


最快的方式

1
2
3
4
public static byte[] GetBytes(string text)
{
    return System.Text.ASCIIEncoding.UTF8.GetBytes(text);
}

编辑正如马库托桑所言,这是现在最好的方法:

1
Encoding.UTF8.GetBytes(text)


使用:

1
2
    string text ="string";
    byte[] array = System.Text.Encoding.UTF8.GetBytes(text);

其结果是:

1
2
3
4
5
6
[0] = 115
[1] = 116
[2] = 114
[3] = 105
[4] = 110
[5] = 103


How do I convert a string to a byte[] in .NET (C#) without manually specifying a specific encoding?

. net中的字符串表示文本为UTF-16代码单元的序列,因此字节已经在内存中以UTF-16编码。

人士的回答

您可以使用Mehrdad的答案,但它实际上使用编码,因为字符是UTF-16。它调用ToCharArray,该函数查看源创建一个char[]并将内存直接复制到它。然后,它将数据复制到也已分配的字节数组中。因此,它实际上是复制底层字节两次,并分配一个char数组,该数组在调用之后不使用。

汤姆布的回答

Tom Blodget的答案比Mehrdad快20-30%,因为它跳过了分配char数组并将字节复制到它的中间步骤,但是它需要使用/unsafe选项编译。如果你绝对不想使用编码,我认为这是一条路。如果将加密登录放在fixed块中,甚至不需要分配单独的字节数组并将字节复制到其中。

Also, why should encoding be taken into consideration? Can't I simply get what bytes the string has been stored in? Why is there a dependency on character encodings?

因为这是正确的方法。string是一个抽象。

如果字符串中有无效字符,使用编码可能会给您带来麻烦,但这不应该发生。如果您使用无效字符将数据输入字符串,那么您就做错了。您可能应该首先使用字节数组或Base64编码。

如果您使用System.Text.Encoding.Unicode,您的代码将更具弹性。您不必担心运行代码的系统的endianness。您不必担心下一个版本的CLR是否会使用不同的内部字符编码。

我认为问题不在于为什么要担心编码,而在于为什么要忽略它而使用其他东西。编码意味着用字节序列表示字符串的抽象。System.Text.Encoding.Unicode将给你一个小的字节顺序编码,并将在每个系统上执行相同的,现在和将来。


您可以使用以下代码在.NET中将string转换为byte array

1
2
string s_unicode ="abcéabc";
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(s_unicode);

最接近OP问题的方法是Tom Blodget,它实际进入对象并提取字节。我说最接近是因为它取决于String对象的实现。

1
"Can't I simply get what bytes the string has been stored in?"

当然,但这就是问题的根本错误所在。字符串是一个对象,它可能具有有趣的数据结构。我们已经知道它是这样的,因为它允许存储未配对的代理。它可以存储长度。它可能保留一个指向每个"配对"代理的指针,允许快速计数。等。所有这些额外的字节都不是字符数据的一部分。

您需要的是数组中每个字符的字节。这就是"编码"的用武之地。默认情况下,您将获得UTF-16LE。如果除了往返之外,您不关心字节本身,那么您可以选择任何编码,包括"default",然后稍后将其转换回来(假设相同的参数,比如默认编码是什么、代码点、bug修复、允许的东西,比如未配对的代理,等等)。

但是为什么要把"编码"留给魔法呢?为什么不指定编码,这样您就知道将得到哪些字节?

1
"Why is there a dependency on character encodings?"

编码(在此上下文中)只是表示表示字符串的字节。而不是字符串对象的字节。您想要字符串存储的字节——这就是问题被天真地问到的地方。您希望字符串的字节位于表示字符串的连续数组中,而不是字符串对象可能包含的所有其他二进制数据。

这意味着字符串的存储方式无关紧要。您希望将字符串"编码"为字节数组中的字节。

我喜欢Tom Bloget的答案,因为他将您引向了"string对象的字节"方向。但是,它依赖于实现,而且由于他正在查看内部,所以可能很难重新构造字符串的副本。

迈赫达德的回答是错误的,因为它在概念层面上具有误导性。您仍然有一个字节列表,已编码。他的特殊解决方案允许保留未配对的代理——这依赖于实现。如果GetBytes默认返回UTF-8格式的字符串,他的特解就不能准确地生成字符串的字节。

我改变主意了(Mehrdad的解决方案)——这不是获取字符串的字节;而是获取从字符串创建的字符数组的字节。无论采用何种编码方式,c#中的char数据类型都是固定的大小。这允许生成一致长度的字节数组,并允许根据字节数组的大小复制字符数组。因此,如果编码是UTF-8,但是每个字符都是6字节,以容纳最大的utf8值,那么它仍然可以工作。因此,字符编码并不重要。

但是使用了一个转换——每个字符都放在一个固定大小的框中(c#的字符类型)。但是,这种表示形式是什么并不重要,从技术上讲,这就是选项的答案。为什么不"编码"呢?


以下是我将String转换为Byte[]的不安全实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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 &amp; 8) != 0)
            {
                *((Int64*)destination) = *((Int64*)source);

                source += 8;
                destination += 8;
            }

            if ((length &amp; 4) != 0)
            {
                *((Int32*)destination) = *((Int32*)source);

                source += 4;
                destination += 4;
            }

            if ((length &amp; 2) != 0)
            {
                *((Int16*)destination) = *((Int16*)source);

                source += 2;
                destination += 2;
            }

            if ((length &amp; 1) != 0)
            {
                ++source;
                ++destination;

                destination[0] = source[0];
            }
        }
    }

    return bytes;
}

它比公认的anwser要快得多,即使没有它那么优雅。这里是我的秒表基准超过10000000次迭代:

1
2
3
4
5
6
7
8
9
10
11
[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,这个方法也可以用作字符串扩展名:

1
2
3
4
5
6
7
public static unsafe class StringExtensions
{
    public static Byte[] ToByteArray(this String s)
    {
        // Method Code
    }
}


如果您真的想要字符串底层字节的副本,可以使用如下函数。然而,你不应该继续往下读来找出原因。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[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,但您不应该关心实现细节。

打电话会更安全、更简单、更可靠,

1
System.Text.Encoding.Unicode.GetBytes()

在所有的可能性,这将给出相同的结果,更容易键入,字节将始终与一个调用的往返

1
System.Text.Encoding.Unicode.GetString()


简单地使用这个:

1
byte[] myByte= System.Text.ASCIIEncoding.Default.GetBytes(myString);


两种方式:

1
2
3
4
5
6
7
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();
}

而且,

1
2
3
4
5
6
7
8
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;
}

我更倾向于使用底部的,而不是顶部的,没有以速度为基准。


1
2
3
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

LINQ的简单代码

1
2
string s ="abc"
byte[] b = s.Select(e => (byte)e).ToArray();

编辑:如下所述,这不是一个好方法。

但你仍然可以用它来理解LINQ与一个更合适的编码:

1
2
string s ="abc"
byte[] b = s.Cast<byte>().ToArray();


由于以下事实,字符串可以用几种不同的方式转换为字节数组:. net支持Unicode, Unicode标准化了几种称为UTFs的不同编码。它们有不同长度的字节表示,但是在这个意义上是等价的,当一个字符串被编码时,它可以被编码回字符串,但是如果这个字符串是用一个UTF编码的,并且在假设有不同的UTF的情况下被解码,就会搞砸。

另外,. net支持非Unicode编码,但在一般情况下它们是无效的(只有在实际字符串(如ASCII)中使用有限的Unicode编码点子集时才有效)。在内部,. net支持UTF-16,但是对于流表示,通常使用UTF-8。它也是互联网事实上的标准。

毫不奇怪,System.Text.Encoding类支持将字符串序列化为字节数组和反序列化,它是一个抽象类;它的派生类支持具体的编码:ASCIIEncoding和四个utf (System.Text.UnicodeEncoding支持UTF-16)

参考这个链接。

用于序列化到使用System.Text.Encoding.GetBytes的字节数组。反操作使用System.Text.Encoding.GetChars。这个函数返回一个字符数组,因此要获得一个字符串,请使用字符串构造函数System.String(char[])。裁判这个页面。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
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对用例的提示,这就是正确的问题。

将二进制数据存储在字符串中可能是错误的方法,因为上面提到了假设的编码!无论将二进制数据存储在string(而不是更合适的byte[]数组)中的哪个程序或库,在开始之前就已经输掉了这场战斗。如果它们以REST请求/响应或任何必须传输字符串的方式向您发送字节,那么Base64将是正确的方法。

如果您有一个编码为未知的文本字符串

其他人都回答错了这个问题。

如果字符串看起来很好,就选择一种编码(最好以UTF开头),使用相应的System.Text.Encoding.???.GetBytes()函数,并告诉您选择哪种编码的字节。


字符既是字体表的查找键,也是词法传统,如排序、大小写版本等。

因此,字符不是字节(8位),字节也不是字符。特别是,一个字节的256种排列不能容纳某些书面语言中的数千个符号,更不用说所有语言了。因此,设计了各种编码字符的方法。一些编码用于特定的语言类(ASCII编码);使用代码页的多种语言(扩展ASCII);或者,大胆地说,所有语言都可以根据需要有选择地包含额外的字节,Unicode。

在系统中,如. net框架中,字符串意味着特定的字符编码。在。net中,这种编码是Unicode。由于框架默认情况下读取和写入Unicode,所以在. net中通常不需要处理字符编码。

但是,一般来说,要从字节流将字符串加载到系统中,您需要知道源编码,以便正确地解释和随后翻译它(否则这些代码将被视为已经在系统的默认编码中,因此呈现的是胡言乱语)。类似地,当将字符串写入外部源时,它将以特定的编码方式写入。


byte[]string:

1
        return BitConverter.ToString(bytes);


我写了一个Visual Basic扩展,类似于已接受的答案,但直接使用.NET内存和编组进行转换,它支持其他方法不支持的字符范围,比如UnicodeEncoding.UTF8.GetStringUnicodeEncoding.UTF32.GetString,甚至是MemoryStream and BinaryFormatter(无效的字符如:?? &ChrW(55906),ChrW(55655)):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<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

要将字符串转换为字节[],请使用以下解决方案:

1
2
string s ="abcdefghijklmnopqrstuvwxyz";
byte[] b = System.Text.UTF32Encoding.GetBytes(s);

我希望这能有所帮助。


1
2
3
4
5
6
7
8
9
10
11
12
13
// 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
2
3
4
5
6
7
8
9
10
11
12
// 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位字符,我无法找到一个方法使用该框架的转换器,不添加两字节条目或mis-translate第八位的字节集。所以我做了以下工作:

1
2
3
4
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的问题:"我如何在。net (c#)中将string转换为byte数组?"(原文如此)

你可以使用以下代码:

1
2
3
static byte[] ConvertString (string s) {
    return new byte[0];
}

作为一个好处,编码并不重要!等等,这是生态…它是平凡的,高度有损的。