Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在不手动指定编码的情况下获得C中字符串的一致字节表示?_C#_.net_String_Character Encoding - Fatal编程技术网

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

C# 如何在不手动指定编码的情况下获得C中字符串的一致字节表示?,c#,.net,string,character-encoding,C#,.net,String,Character Encoding,如何在不手动指定特定编码的情况下将.NET C中的字符串转换为字节[] 我要加密这个字符串。我可以在不转换的情况下对其进行加密,但我仍然想知道为什么在这里使用编码 还有,为什么还要考虑编码呢?我不能简单地获取字符串存储在哪个字节中吗?为什么依赖于字符编码?它取决于字符串的编码 byte[] strToByteArray(string str) { System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding(); re

如何在不手动指定特定编码的情况下将.NET C中的字符串转换为字节[]

我要加密这个字符串。我可以在不转换的情况下对其进行加密,但我仍然想知道为什么在这里使用编码


还有,为什么还要考虑编码呢?我不能简单地获取字符串存储在哪个字节中吗?为什么依赖于字符编码?

它取决于字符串的编码

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用于表示字符串,因此,如果您只是想获得.NET使用的确切字节,请使用System.Text.Encoding.Unicode.GetBytes


有关更多信息,请参见MSDN。

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

Joel在这上面有个帖子:


关键问题是,字符串中的标志符号需要32位16位来表示字符代码,但一个字节只有8位可用。一对一映射不存在,除非您将自己限制为只包含ASCII字符的字符串。System.Text.Encoding有很多方法可以将字符串映射到字节[],您需要选择一种可以避免信息丢失的方法,并且当您的客户需要将字节[]映射回字符串时,可以方便地使用这种方法


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

我不确定,但我认为字符串将其信息存储为字符数组,这对字节效率很低。具体来说,字符的定义表示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命名空间

我将回答您的后续问题:为什么需要选择编码?为什么不能从string类本身得到呢

答案分为两部分

首先,string类内部使用的字节并不重要,只要你认为它们是重要的,你就很可能会引入一个bug

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

另一方面,如果将这些字节发送到某个无法保证将从.Net序列化流中提取数据的位置,该怎么办?在这种情况下,您确实需要担心编码,因为显然这个外部系统关心编码。因此,字符串使用的内部字节也无关紧要:您需要选择一种编码,以便能够在接收端明确说明这种编码,即使它与.Net内部使用的编码相同

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

这就引出了第二部分。。。选择Unicode编码告诉.Net使用底层字节。您确实需要选择这种编码,因为当一些新型的Unicode Plus问世时,.Net运行时需要免费使用这种更新、更好的编码模型,而不会破坏您的程序。但是,就目前和可预见的未来而言,只需选择Unicode编码即可满足您的需求

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

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());
另外,请解释为什么应该考虑编码。 我不能简单地获取字符串存储在哪个字节中吗? 为什么要依赖编码

因为不存在字符串的字节

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

编码只是将逻辑字符转换为物理字节的约定。最简单和最著名的编码是ASCII码,如果你用英语写的话,这就是你所需要的。对于其他语言,您将需要更完整的编码,任何Unicode风格都是当今最安全的选择

因此,简而言之,不使用编码就获取字符串的字节与不使用任何语言编写文本一样是不可能的

顺便说一句,我强烈建议你和任何人,在这方面阅读这篇小智慧:

最快的方式

编辑 正如Makotosan所评论的,这是现在最好的方式:

Encoding.UTF8.GetBytes(text)

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

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

所以我在这种情况下使用字节数组编码,但是在互联网上,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);
}

试试这个,代码要少得多:

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

公认的答案非常非常复杂。为此使用包含的.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);
".NET String to byte array".ToCharArray()
如果不需要,就不要重新发明轮子…

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

正如您提到的,您的目标很简单,就是获取字符串存储的字节数。 当然,为了能够从字节中重新构造字符串

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

只需这样做:

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);
}
只要您的程序或其他程序不试图以某种方式解释字节(您显然没有提到您打算这么做),那么这种方法就没有错!担心编码只会让你的生活变得更加复杂

这种方法的额外好处是:字符串是否包含无效字符无关紧要,因为您仍然可以获取数据并重建原始字符串

它将被编码和解码,因为你只是在看字节


但是,如果您使用特定的编码方式,它会给您编码/解码无效字符带来麻烦。

仅为了证明Mehrdrad的声音是有效的,他的方法甚至可以坚持许多人对我的答案所持的观点,但每个人都同样有罪,例如System.Text.encoding.UTF8.GetBytes,System.Text.Encoding.Unicode.GetBytes;例如,那些编码方法不能持久化高代理字符d800,而那些编码方法只是用值fffd替换高代理字符:

输出:

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

每当这个问题出现变化时,我仍然会想到一个来自微软或第三方组件的serializer,它可以保存字符串,即使它包含未配对的代理字符;我不时用谷歌搜索:序列化未配对的代理角色.NET。这并不会让我失眠,但时不时有人评论我的答案有缺陷,这有点令人恼火,但他们的答案同样有缺陷,当涉及到未配对的代理角色时

该死,微软应该在其二进制格式化程序中使用System.Buffer.BlockCopyツ


谢谢!

以下是我的字符串到字节[]转换的不安全实现:

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
    }
}
C将字符串转换为字节数组:


您可以使用以下代码在.NET中将字符串转换为字节数组

使用:

结果是:

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

这是一个流行的问题。重要的是要理解作者所问的问题,以及它与最常见的需求不同。劝阻 在不需要的地方使用代码,我先回答了后面的问题

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

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

// using System.Text;
var text = Encoding.ASCII.GetString(Encoding.ASCII.GetBytes("You win €100")); 
                                                      // -> "You win ?100"
显然,转换不一定是无损的

注意:对于System.String,源字符集是Unicode

唯一令人困惑的是.NET使用字符集的名称作为该字符集的一种特定编码的名称。Unicode应称为Encoding.UTF16

大多数用法都是这样。如果这是你需要的,就别在这里读了。如果你不明白什么是编码,那就去看看它的乐趣吧

特殊需要
现在,作者提出的问题是,每个字符串都存储为字节数组,对吗?为什么我不能简单地拥有这些字节

他不想做任何改变

从:

C中的字符和字符串处理使用Unicode编码。煤焦 类型表示UTF-16代码单元,字符串类型表示 UTF-16代码单元的顺序

因此,我们知道,如果我们要求进行空转换,即从UTF-16到UTF-16,我们将得到所需的结果:

Encoding.Unicode.GetBytes(".NET String to byte array")
但是为了避免提及编码,我们必须用另一种方式来做。如果可以接受中间数据类型,则有一个概念上的快捷方式:

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);
".NET String to byte array".ToCharArray()
这并没有得到我们想要的数据类型,但展示了如何使用将这个字符数组转换为字节数组。但是,这会复制字符串两次!而且,它也显式地使用编码特定的代码:datatype System.Char

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

[对于]字符串类型的表达式。。。初始值设定项计算 字符串中第一个字符的地址

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

// 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指出的,结果取决于机器的端部。但问题作者并不关心这个问题。

由于以下事实,字符串可以用几种不同的方式转换为字节数组:.NET支持Unicode,Unicode标准化了几种称为UTF的不同编码。它们具有不同长度的字节表示,但在这种意义上是等效的,即当对字符串进行编码时,可以将其编码回字符串,但如果使用一个UTF对字符串进行编码,并在假设不同UTF的情况下进行解码,则可能会出错

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

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

参考号

用于使用System.Text.Encoding.GetBytes对字节数组进行序列化。对于反向操作,请使用System.Text.Encoding.GetChars。此函数返回一个字符数组,因此要获取字符串,请使用字符串构造函数System.Stringchar[]。

例如:

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? :-)

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

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 can中的字节表示形式,并调用

System.Text.Encoding.Unicode.GetString()
简单地说:

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

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

"Can't I simply get what bytes the string has been stored in?"
当然,但这就是问题的根本错误所在。字符串是一个可能具有有趣数据结构的对象。我们已经知道了,因为它允许存储未配对的代理。它可以存储长度。它可能会保留一个指向每个“配对”代理的指针,以允许快速计数。等等。所有这些额外字节都不是字符数据的一部分

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

但为什么要让“编码”由魔术来完成呢?为什么不指定编码,这样你就知道你将得到什么字节

"Why is there a dependency on character encodings?"
在此上下文中,编码只是指表示字符串的字节。不是字符串对象的字节。您需要字符串存储在其中的字节-这就是天真地提出问题的地方。您需要的是表示字符串的连续数组中的字符串字节,而不是字符串对象可能包含的所有其他二进制数据

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

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);
我喜欢Tom Bloget的回答,因为他将您带向了“字符串对象的字节”方向。不过,它依赖于实现,而且因为他正在查看内部,所以可能很难重新构建字符串的副本

Mehrdad的回答是错误的,因为它在概念层面上具有误导性。您仍然有一个已编码的字节列表。他的特殊解决方案允许保留未配对的代理-这取决于实现。如果默认情况下GetBytes以UTF-8格式返回字符串,那么他的特定解决方案将无法准确生成字符串的字节

我已经改变了我对这个Mehrdad解决方案的想法-这不是获取字符串的字节;而是获取从字符串创建的字符数组的字节。不管编码方式如何,c中的字符数据类型都是固定大小的。这允许生成长度一致的字节数组,并允许根据字节数组的大小复制字符数组。因此,如果编码是UTF-8,但每个字符有6个字节来容纳最大的utf8值,那么它仍然可以工作。所以事实上,字符的编码并不重要

但是使用了一种转换——每个字符都被放入一个固定大小的框c的字符类型中。然而,这种表达方式是什么并不重要,从技术上来说,这就是OP的答案。所以,如果你无论如何都要转换。。。为什么不“编码”?

这取决于您希望字节用于什么 这是因为,正如泰勒所说,字符串不是纯数据。他们也有。在本例中,信息是创建字符串时假定的编码

假设您有二进制数据,而不是字符串中存储的文本 这是基于OP对自己问题的评论,如果我理解OP对用例的提示,这是正确的问题

由于上面提到的假定编码,以字符串形式存储二进制数据可能是错误的方法!无论哪个程序或库将二进制数据存储在字符串中而不是更合适的字节[]数组中,这场战斗在开始之前就已经失败了。如果他们在REST请求/响应或任何必须传输字符串的内容中向您发送字节,这将是正确的方法

如果您有一个编码未知的文本字符串 其他人都错误地回答了这个错误的问题

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

随着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需要安装

不管怎么说,实际的原始问题和后续评论都意味着底层内存没有被解释,我假设这意味着没有修改或读取超出按原样编写的需要,这表明应该使用流类的一些实现,而不是将数据作为字符串进行推理

如何将字符串转换为byt e[]在.NET C中,而不手动指定特定编码

NET中将文本表示为UTF-16代码单元的序列,因此字节已经在UTF-16的内存中编码

梅尔达德的回答

您可以使用,但它实际上使用了编码,因为字符是UTF-16。它调用ToCharray,ToCharray创建一个char[],并将内存直接复制到其中。然后,它将数据复制到同样分配的字节数组中。因此,在后台,它将底层字节复制两次,并分配一个在调用后不使用的字符数组

汤姆·布卢吉特的回答

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

还有,为什么要考虑编码?我不能简单地获取字符串存储在哪个字节中吗?为什么对字符编码有依赖性

byte[] strToByteArray(string str)
{
    System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
    return enc.GetBytes(str);
}
因为这是正确的方法。字符串是一种抽象

如果您有包含无效字符的“字符串”,使用编码可能会给您带来麻烦,但这不应该发生。如果使用无效字符将数据输入字符串,则是错误的。您可能应该首先使用字节数组或Base64编码

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


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

当被问及您打算如何处理字节时,您:

我要加密它。我可以在不转换的情况下对其进行加密,但我仍然想知道为什么在这里使用编码。我就是这么说的

无论您是打算通过网络发送加密数据,稍后将其加载回内存,还是将其传输到另一个进程,您显然打算在某个时候对其进行解密。在这种情况下,答案是您正在定义一个通信协议。不应根据编程语言及其相关运行时的实现细节来定义通信协议。这有几个原因:

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);
".NET String to byte array".ToCharArray()
您可能需要与用不同语言或运行时实现的进程通信。例如,这可能包括在另一台机器上运行的服务器或将字符串发送到JavaScript浏览器客户端。 该程序将来可能会以不同的语言或运行时重新实现。 NET实现可能会更改字符串的内部表示形式。您可能认为这听起来有些牵强,但这是为了减少内存使用。没有理由.NET不能效仿。UTF-16在今天可能不是最佳的,这导致了表情符号和其他Unicode块的兴起,它们也需要超过2个字节来表示,这增加了内部表示在未来可能发生变化的可能性。 为了在将来与完全不同的进程或同一程序进行通信,您需要严格定义协议,以最大限度地降低使用它或意外创建错误的难度。取决于.NET的内部表示,它并不是一个严格、清晰、甚至不能保证一致的定义。标准编码是一个严格的定义,将来不会让您失望

换句话说,如果不指定编码,就无法满足一致性要求

如果您发现由于.NET在内部使用UTF-16或出于任何其他原因,您的进程的性能显著提高,那么您当然可以选择直接使用UTF-16,但您需要显式选择该编码,并在代码中显式执行这些转换,而不是依赖于.NET的内部实现

因此,请选择一种编码并使用它:

using System.Text;

// ...

Encoding.Unicode.GetBytes("abc"); # UTF-16 little endian
Encoding.UTF8.GetBytes("abc")
正如您所看到的,与实现您自己的读写器方法相比,仅使用内置编码对象实际上代码更少。

如果您使用.NET Core或.NET Framework的System.Memory,有一种非常有效的封送机制,可通过Span和内存将字符串内存有效地重新解释为字节跨度。一旦有了一个字节跨度,您就可以自由地封送回另一种类型,或者将该跨度复制到数组以进行序列化

总结一下什么 她说:

存储这种序列化的表示形式对系统端性、编译器优化以及对正在执行的.NET运行时中字符串的内部表示形式的更改非常敏感。 避免长期储存 避免在其他环境中反序列化或解释字符串 这包括其他机器、处理器架构、.NET运行时、容器等。 这包括比较、格式化、加密、字符串操作、本地化、字符转换等。 避免对字符编码进行假设 默认编码在实践中倾向于UTF-16LE,但编译器/运行时可以选择任何内部表示 实施 公共静态类封送处理扩展 { public static ReadOnlySpan AsBytesthis string value=>MemoryMarshal.AsBytesvalue.AsSpan; 公共静态字符串AsStringthis ReadOnlySpan value=>new stringMemoryMarshal.Castvalue; } 实例 静态环[]args { 字符串str1=你好,世界; ReadOnlySpan=str1.AsBytes; 字符串str2=span.AsString; 字节[]字节=span.ToArray; Debug.Assertbytes.Length>0; Debug.Assertstr1==str2; } 进一步洞察 在C++中,这大致相当于RealType的转换,而C这相当于一个强制转换到系统的单词类型char。 在.NET Core Runtime CoreCLR的最新版本中,跨域上的操作可以有效地调用编译器内部函数和各种优化,这些优化有时可以消除边界检查,从而在保持内存安全的同时实现优异的性能,假设您的内存是由CLR分配的,并且跨度不是从非托管内存分配器的指针派生的

警告
这使用了CLR支持的机制,该机制从字符串返回ReadOnlySpan;此外,此范围不一定包含完整的内部字符串布局。ReadOnlySpan意味着如果需要执行变异,就必须创建一个副本,因为字符串是不可变的。

但是,为什么要考虑编码呢?为什么我不能简单地获取字节而不必查看正在使用的编码?即使它是必需的,字符串对象本身不应该知道正在使用什么编码,而只是转储内存中的内容吗?这并不总是有效的。在使用这种方法时,一些特殊字符可能会丢失。我发现这是一条艰难的道路。但是,为什么要考虑编码呢?为什么我不能简单地获取字节而不必查看正在使用的编码?即使它是必需的,字符串对象本身不应该知道正在使用什么编码,而只是转储内存中的内容吗?.NET字符串总是编码为Unicode。因此,请使用System.Text.Encoding.Unicode.GetBytes;获取.NET用于表示字符的字节集。然而,你为什么要这样做?我推荐UTF-8,尤其是当大多数字符都是西拉丁字符集时。另外:如果检索它们的系统不处理该编码或将其作为错误编码处理,则字符串内部使用的确切字节并不重要。如果都在.Net中,为什么还要转换成字节数组呢。否则,最好对您的encoding@Joel,请小心使用System.Text.Encoding.Default,因为它在运行的每台计算机上可能会有所不同。这就是为什么建议始终指定编码,例如UTF-8。除非您或其他人确实打算解释数据,而不是将其视为通用字节块,否则您不需要编码。对于压缩、加密等,担心编码是毫无意义的。请参阅,了解一种不用担心编码的方法。我可能会给a-1,因为我说你需要担心编码,而你不需要,但我今天感觉不是特别刻薄每个字符串都存储为字节数组,对吗?为什么我不能简单地拥有这些字节呢?编码就是将字符映射到字节的方式。例如,在ASCII中,字母“A”映射到数字65。在不同的编码中,它可能不同。在.NET framework中对字符串采用的高级方法使得这一点在很大程度上无关紧要,但在本例中除外。扮演魔鬼代言人:如果您想在.NET使用时获取内存中字符串的字节,并以某种方式对其进行操作,即CRC32,从来都不想将其解码回原始字符串…这不是你为什么要关心编码或如何选择使用哪种编码的直接原因。令人惊讶的是,还没有人给出这个链接:字符不是字节,字节也不是字符。字符既是字体表的键,也是词法传统。字符串是字符序列。单词、段落、句子和标题也有自己的词汇传统,可以证明自己的类型定义是正确的——但我离题了。像整数、浮点数和其他所有东西一样,字符被编码成字节。曾经有一段时间,编码是简单的一对一:ASCII。霍维夫

呃,为了适应人类所有的符号学,一个字节的256次排列是不够的,编码被设计成选择性地使用更多的字节。我同意一个字符可以用一个或更多的字节来表示。我只需要这些字节,而不管字符串的编码是什么。字符串存储在内存中的唯一方式是以字节为单位。偶数字符存储为1个或更多字节。我只想得到字节,你不需要编码,除非你或其他人真的打算解释数据,而不是把它当作一个通用的字节块。对于压缩、加密等,担心编码是毫无意义的。请参阅,以找到一种不用担心编码的方法。@Mehrdad-完全可以,但正如我最初回答时所说的,最初的问题并没有说明在转换这些字节后,OP将如何处理这些字节,对于未来的搜索者来说,这方面的信息是相关的——这被很好地涵盖了——正如你在回答中所说的:如果你坚持在.NET世界中,并使用你的方法转换为/从,你会很高兴的。一旦你跳出这一步,编码就很重要。一个代码点最多可以用4个字节表示。一个UTF-32代码单元、一个UTF-16代理项对或4字节的UTF-8。UTF-8需要超过4字节的值超出了Unicode的0x0..0x10FFFF范围-您可以对所有这些操作使用相同的BinaryFormatter实例,这非常有趣。显然,它将删除任何高代理Unicode字符。请参阅[UTF-8]上的文档,仅当您的大多数字符在英语ASCII字符集中时,UTF-8才是紧凑型的。如果你有一个很长的汉字字符串,UTF-16将是一个比UTF-8更紧凑的编码。这是因为UTF-8使用一个字节来编码ASCII,否则使用3或4。正确。但是,如果你熟悉处理中文文本,你怎么会不知道编码呢?请允许我澄清一下:编码已经被用来将hello world翻译成物理字节。由于字符串存储在我的计算机上,我确信它必须以字节存储。我只想访问这些字节以将它们保存在磁盘上或出于任何其他原因。我不想解释这些字节。因为我不想解释这些字节,所以在这一点上需要编码就像需要电话线呼叫printf一样是错误的。但是,除非使用编码,否则没有文本到物理字节转换的概念。当然,编译器会以某种方式将字符串存储在内存中,但它只是使用内部编码,而您或编译器开发人员以外的任何人都不知道。所以,无论你做什么,你都需要一个编码来从字符串中获取物理字节。@Agnel Kurian:当然,字符串的某个地方有一堆字节存储它的内容UTF-16 afair。但是有一个很好的理由阻止您访问它:字符串是不可变的,如果您可以获得内部byte[]数组,您也可以修改它。这打破了不变性,这是至关重要的,因为多个字符串可能共享相同的数据。使用UTF-16编码获取字符串可能只是将数据复制出去。@Gnafoo,字节的副本就可以了。在.NET中,有些区域确实需要获取字符串的字节数组。许多.NET Cryptrography类包含接受字节数组或流的方法,如ComputeHash。您别无选择,只能先选择编码,然后选择将字符串包装到流中,然后将字符串转换为字节数组。但是,只要您选择一种编码,即UTF8,并坚持使用它,这就没有问题。您不必使用自定义方法将字节数组转换为base64,只需使用内置转换器:convert.ToBase64Stringarr@Makotosan谢谢,但我确实使用了Convert.ToBase64Stringarr;对于base64转换,字节[]数据字符串序列化数据存储在XML文件中。但要获得初始字节[]数据,我需要对包含二进制数据的字符串进行处理,这是MSSQL返回给我的方式。因此,上述函数用于字符串二进制数据字节[]易于访问的二进制数据。。。。。这是不需要的。简单地使用Encoding.UTF8.GetBytestext是首选。这一个的丑陋之处在于,GetString和GetBytes需要在具有相同endianness的系统上执行才能工作。所以你不能用它来获取你想在别处转换成字符串的字节。所以我很难想出一个我想要使用它的情况。@CodeInChaos:就像我说的,整个问题是如果你想在相同类型的系统上使用它,使用相同的功能集。如果没有,那么你就不应该使用它。-1我保证不懂字节与字符的人会想将字符串转换成字节数组,他们会用谷歌搜索并读取这个答案,他们会做错误的事情,因为在几乎所有情况下,编码都是相关的。@artbris
托尔:如果他们不屑于阅读答案或其他答案,那么我很抱歉,没有更好的方式让我与他们沟通。我通常选择回答OP,而不是猜测别人会对我的答案做什么——OP有权知道,仅仅因为有人可能滥用刀子并不意味着我们需要为自己隐藏世界上所有的刀子。尽管如果你不同意,也没关系。这个答案在很多方面都是错误的,但最重要的是因为它是一种歧视,你不需要担心编码问题!。GetBytes和GetString这两个方法是多余的,因为它们只是Encoding.Unicode.GetBytes和Encoding.Unicode.GetString已经完成的功能的重新实现。只要您的程序或其他程序不尝试解释字节,该语句也存在根本性的缺陷,因为它们隐含地意味着字节应解释为Unicode。代理项不必成对出现以形成有效的代码点吗?如果是这样的话,我可以理解为什么数据会被破坏。@dtanders是的,这也是我的想法,它们必须成对出现,如果你故意把它们放在字符串上并使它们不成对,就会出现不成对的代理字符。我不知道的是,为什么其他开发人员不断强调我们应该使用编码感知方法,因为他们认为序列化方法(3年多以来被接受的答案)不能保持未配对的代理字符完好无损。但讽刺的是,他们忘了检查他们的编码感知解决方案是否也保留了未配对的代理角色ツ如果有一个序列化库在内部使用System.Buffer.BlockCopy,则所有编码参数都将moot@MichaelBuen在我看来,主要的问题是,你用粗体大字说一些无关紧要的事情,而不是说在他们的情况下这些都无关紧要。因此,您鼓励查看您答案的人犯一些基本的编程错误,这将在将来导致其他人受挫。未配对的代理项在字符串中无效。它不是字符数组,因此将字符串转换为另一种格式会导致该字符出现错误FFFD是有道理的。如果要手动操作字符串,请按建议使用字符[]。@dtanders:System.string是一个不可变的字符序列。NET始终允许从任何Char[]构造字符串对象,并将其内容导出到包含相同值的Char[],即使原始Char[]包含未配对的代理项。如果接受的答案发生更改,出于记录目的,它是Mehrdad在当前时间和日期的答案。希望OP会重新考虑这个问题,并接受更好的解决方案。原则上很好,但编码应该是System.Text.encoding.Unicode,以与Mehrdad的答案等效。这个问题自原始答案以来已编辑了无数次,因此,也许我的答案有点过时。我从来没有打算给出一个与梅尔达德的答案相当的例子,而是给出一个合理的方法。但是,你可能是对的。然而,在原始问题中,短语get字符串存储在什么字节中是非常不精确的。储存在哪里?在记忆中?在磁盘上?如果在内存中,System.Text.Encoding.Unicode.GetBytes可能更精确。@AMissico,您的建议有问题,除非您确定您的字符串与系统默认编码字符串兼容,该字符串在系统默认旧字符集中仅包含ASCII字符。但是OP并没有说它会导致程序在不同的系统上给出不同的结果。这从来都不是一件好事。即使是为了生成散列或其他东西,我认为OP对“encrypt”的意思是一样的,同样的字符串也应该总是给出同样的散列。在安腾版本的.NET上RuntimeHelpers.OffsetOstringData的值是8的倍数吗?因为否则,由于未对齐的读取,这将失败。调用memcpy不是更简单吗@Jan这是正确的,但是字符串长度已经给出了代码单元的数量,而不是代码点。感谢您指出这一点!来自MSDN:Length属性[of String]返回此实例中的字符对象数,而不是Unicode字符数。因此,您的示例代码是正确的。@supercat字符类型表示UTF-16代码单元,字符串类型表示UTF-16代码单元的序列。-\u C 5规范。\虽然,是的,没有任何东西可以阻止无效的Unicode字符串:new Stringnew[]{'\uD800','\u0030'}@TomBlodget:有趣的是,如果获取Globalization.SortKey的实例,提取KeyData,并将每个实例中的结果字节打包成字符串[每个字符两个字节,MSB优先],则对结果字符串调用String.CompareOrdinal将比调用SortKey快得多。对SortKey的实例进行比较,甚至在这些实例上调用memcmp。鉴于此,我想知道为什么KeyData会返回
一个字节[]而不是一个字符串?唉,正确的答案,但为时已晚,永远不会有被接受的选票那么多。由于TL;人民博士会认为公认的答案令人震惊。copyenpsit和up vote it.vupt这一个解决了我的问题byte[]ff=ascienceoding.ASCII.GetBytesbarcodetxt.Text;UTF-8、UTF-16甚至UTF-32都不支持这些字符。例如:Raymon,这些字符已经由某些unicode值表示,而所有unicode值都可以由所有UTF值表示。对你所说的有更详细的解释吗?这两个值或3.使用什么字符编码。。是否存在?它们是任何编码范围都不支持的无效字符。这并不意味着它们是100%无用的。将任何类型的字符串转换为其等效字节数组的代码,而不考虑编码,这完全不是一个错误的解决方案,并且在需要的场合有自己的用法。好的,那么我认为您不理解这个问题。我们知道它是一个符合unicode的数组-事实上,因为它是.net,所以我们知道它是UTF-16。因此,这些字符将不存在那里。您也没有完全阅读我关于内部表示更改的评论。字符串是一个对象,而不是编码的字节数组。所以我不同意你最后的说法。您希望代码将所有unicode字符串转换为任何UTF编码。这正是您想要的。对象是数据序列,最初是描述对象当前状态的位序列。因此,编程语言中的每个数据都可以转换为字节数组,每个字节定义8位,因为您可能需要在内存中保留任何对象的某些状态。您可以将字节序列保存在文件或内存中,并在从磁盘读取后将其转换为整数、bigint、image、Ascii字符串、UTF-8字符串、加密字符串或您自己定义的数据类型。所以你不能说对象和字节序列不同,那么试试这个系统。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.GetBytesArvizturo tukorfurogep.Length而Árvíztűrőtükörfúrógép.Length==Arvizturo tukorfurogep。Length@mg30rg字体你为什么认为你的例子很奇怪?当然,在可变宽度编码中,并非所有字符都具有相同的字节长度。有什么问题吗?@Vlad这里有一个更有效的评论,作为编码的unicode符号,因此作为字节,包含自己的变音符号的字符将给出不同于拆分为添加到字符中的修改符号的变音符号的结果。但是,在.net中有一些方法专门将它们分开,以获得一致的字节表示……并丢失跳转范围大于127的所有字符。在我的母语中,写Árvíztűrőtükörfúrógép是完全正确的。。System.Text.ascienceoding.Default.GetBytesÁrvíztűrőtükörfúrógép..ToString;将返回阿尔维茨图罗·图克尔富罗盖普。正在丢失无法检索的信息。我还没有提到亚洲语言会丢失所有字符。OP特别要求不要指定编码。。。在不手动指定特定编码的情况下,实际上C中的字符串不限于UTF-16。事实上,它包含一个16位代码单元的向量,但这些16位代码单元并不限于有效的UTF-16。但由于它们是16位的,所以需要一个编码字节顺序才能将它们转换为8位。然后,字符串可以存储非Unicode数据,包括二进制代码,例如位图图像。只有在进行这种解释的I/O和文本格式化程序中,它才会被解释为UTF-16。因此,在C字符串中,您可以安全地存储类似0xFFFF或0xFFFE的代码单元,即使它们在UTF-16中不是字符,您可以在0xDC00..0xDFFF中存储一个不后跟代码单元的独立0xD800,即在UTF-16中无效的未配对代理。同样的注释也适用于Javascript/ECMAscript和Java中的字符串。当您使用GetBytes时,当然不指定编码,但您假定一个字节顺序来获取字符串中本地存储的每个代码单元的specic中的两个字节。从字节生成新字符串时,还需要一个转换器(不一定是UTF-8到UTF-16),可以在高位字节中插入额外的0,或者在同一个16位代码单元中按MSB first或LSB first顺序打包两个字节。然后,字符串是16位整数数组的压缩形式。与字符的关系是另一个问题,在C中,它们不是实际的类型,因为它们仍然表示为stringsSome注释:尽管这似乎是流行的观点,但这种机制的一个完全有效的用例是运行时加密:提取字节表示,加密字节,并将加密的负载保留在内存中。这最大限度地减少了编码开销,并且只要它没有被序列化并传输到另一个环境,就不会因为inte而受到任何编码特定问题的影响
翻译语义或内部表示。有人认为使用SecureString可以达到这个目的,也有人担心垃圾收集,但在其他方面,这个前提似乎是合理的。至少有一个建议是CoreCLR引入更紧凑的内部表示:new stringf是错误的,如果希望对所有字符串进行往返,至少需要使用接受显式长度的构造函数重载。如许多其他注释所述,Unicode.GetBytes/Unicode.GetString不会对所有.NET字符串实例进行往返。@BenVoigt,我调整了答案。这几天我会做一些没有Windows特定的事情。你可以考虑避免P/INKEKE,MaSal.Read对于从指针到字节数组的复制很有效。如果你的字符串是英文的,或者是中文的,你最好使用UTF-16。
using System.Text;

// ...

Encoding.Unicode.GetBytes("abc"); # UTF-16 little endian
Encoding.UTF8.GetBytes("abc")