ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

每个字符串都存储为一个字节数组,对吗?为什么我不能简单地拥有这些字节?
编码是将字符映射到字节的原因。例如,在 ASCII 中,字母“A”映射到数字 65。在不同的编码中,它可能不一样。不过,.NET 框架中对字符串采用的高级方法使得这在很大程度上无关紧要(本例除外)。
扮演魔鬼的拥护者:如果您想获取内存中字符串的字节(因为 .NET 使用它们)并以某种方式操纵它们(即 CRC32),并且永远不想将其解码回原始字符串......它不是直截了当为什么你会关心编码或你如何选择使用哪一个。
很惊讶还没有人给出这个链接:joelonsoftware.com/articles/Unicode.html
一个字符不是一个字节,一个字节也不是一个字符。 char 既是字体表的键,又是词汇传统。字符串是一个字符序列。 (单词、段落、句子和标题也有它们自己的词汇传统来证明它们自己的类型定义是正确的——但我离题了)。与整数、浮点数和其他所有内容一样,字符被编码为字节。曾经有一段时间,编码是一对一的简单编码:ASCII。然而,为了适应所有人类符号系统,一个字节的 256 个排列是不够的,并且设计了编码以选择性地使用更多字节。

u
user541686

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

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

对于这些目标,老实说,我不明白为什么人们一直告诉你你需要编码。您当然不需要为此担心编码。

只需这样做:

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);
}

只要您的程序(或其他程序)不尝试以某种方式解释字节,您显然没有提到您打算这样做,那么这种方法就没有错!担心编码只会无缘无故地让你的生活变得更加复杂。

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

它将被相同地编码和解码,因为您只是在查看字节。

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


这个的丑陋之处在于,GetStringGetBytes 需要在具有相同字节顺序的系统上执行才能工作。因此,您不能使用它来获取要在其他地方转换为字符串的字节。所以我很难想出一个我想使用它的情况。
@CodeInChaos:就像我说的那样,如果您想在相同类型的系统上使用它,并且具有相同的功能集。如果没有,那么你不应该使用它。
-1 我保证有人(不了解字节与字符)想要将他们的字符串转换为字节数组,他们会用谷歌搜索并阅读这个答案,他们会做错事,因为几乎所有情况下,编码是相关的。
@artbristol:如果他们懒得阅读答案(或其他答案......),那么对不起,我没有更好的方式与他们交流。我通常选择回答 OP 而不是试图猜测其他人可能会对我的回答做什么——OP 有权知道,仅仅因为有人可能滥用刀并不意味着我们需要隐藏世界上所有的刀为我们自己。虽然如果你不同意那也没关系。
这个答案在很多层面上都是错误的,但最重要的是因为它的声明是“你不需要担心编码!”。这两种方法,GetBytes 和 GetString 是多余的,因为它们只是重新实现 Encoding.Unicode.GetBytes() 和 Encoding.Unicode.GetString() 已经做的事情。声明“只要您的程序(或其他程序)不尝试解释字节”也从根本上存在缺陷,因为它们隐含地意味着应该将字节解释为 Unicode。
P
Peter Mortensen

这取决于您的字符串的编码(ASCIIUTF-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 (...)

有关详细信息,请参阅 Character Encoding in the .NET Framework (MSDN)。


但是,为什么要考虑编码呢?为什么我不能简单地获取字节而不必查看正在使用的编码?即使它是必需的,字符串对象本身不应该知道正在使用什么编码并简单地转储内存中的内容吗?
.NET 字符串始终编码为 Unicode。所以使用 System.Text.Encoding.Unicode.GetBytes();获取 .NET 用来表示字符的字节集。但是,您为什么要那样做?我推荐 UTF-8,尤其是当大多数字符都在西方拉丁语集中时。
另外:如果检索它们的系统不处理该编码或将其作为错误编码处理,则字符串内部使用的确切字节无关紧要。如果它都在.Net 中,为什么要转换为字节数组。否则,最好明确编码
@Joel,请注意 System.Text.Encoding.Default 因为它在运行的每台机器上都可能不同。这就是为什么建议始终指定编码,例如 UTF-8。
除非您(或其他人)实际上打算解释数据,而不是将其视为通用的“字节块”,否则您不需要编码。对于压缩、加密等事情,担心编码是没有意义的。有关无需担心编码的方法,请参阅 my answer。 (我可能给了一个 -1 表示你需要担心编码,但我今天并不觉得特别刻薄。:P)
V
Vlad

公认的答案非常非常复杂。为此使用包含的 .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);

如果不需要,就不要重新发明轮子...


如果接受的答案发生变化,出于记录目的,这是 Mehrdad 在当前时间和日期的答案。希望OP将重新审视这一点并接受更好的解决方案。
原则上很好,但是编码应该是 System.Text.Encoding.Unicode 以等同于 Mehrdad 的答案。
自原始答案以来,这个问题已经被编辑了无数次,所以,也许我的答案有点过时了。我从未打算给出与 Mehrdad 的答案相当的 exace,而是给出一种明智的做法。但是,你可能是对的。但是,原始问题中的短语“获取字符串存储在哪些字节中”非常不精确。存放在哪里?在记忆中?在磁盘上?如果在内存中,System.Text.Encoding.Unicode.GetBytes 可能会更精确。
@AMissico,您的建议是错误的,除非您确定您的字符串与系统默认编码兼容(系统默认旧字符集中仅包含 ASCII 字符的字符串)。但是OP没有任何地方说明这一点。
@AMissico 虽然它可能导致程序在不同的系统上给出不同的结果。这从来都不是一件好事。即使它是用于制作散列或其他东西(我认为这就是 OP 对“加密”的含义),相同的字符串仍应始终给出相同的散列。
M
Michael Buen
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());

您可以对所有这些操作使用相同的 BinaryFormatter 实例
很有意思。显然它会丢弃任何高代理 Unicode 字符。请参阅 [BinaryFormatter 上的文档]
Z
Zhaph - Ben Duguid

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

乔尔对此有一个帖子:

每个软件开发人员绝对、绝对必须了解 Unicode 和字符集的绝对最低要求(没有借口!)


“1 个字符可以用 1 个或多个字节表示”我同意。我只想要这些字节,而不管字符串的编码是什么。字符串可以存储在内存中的唯一方法是以字节为单位。甚至字符也存储为 1 个或多个字节。我只想掌握它们的字节。
除非您(或其他人)实际上打算解释数据,而不是将其视为通用的“字节块”,否则您不需要编码。对于压缩、加密等事情,担心编码是没有意义的。有关无需担心编码的方法,请参阅 my answer
@Mehrdad - 完全如此,但是正如我最初回答时所说的那样,最初的问题并没有警告在转换这些字节后会发生什么 OP,对于未来的搜索者来说,相关信息是相关的 - 这是Joel's answer 很好地涵盖了 - 正如您在回答中所说:只要您坚持 .NET 世界,并使用您的方法进行转换,您就会很高兴。一旦你走出这一步,编码就会很重要。
一个代码点最多可以用 4 个字节表示。 (一个 UTF-32 代码单元、一个 UTF-16 代理对或 4 个字节的 UTF-8。)UTF-8 需要超过 4 个字节的值超出了 Unicode 的 0x0..0x10FFFF 范围。 ;-)
C
Community

这是一个流行的问题。重要的是要了解作者提出的问题,以及它可能与最常见的需求不同。为了阻止在不需要的地方滥用代码,我首先回答了后者。

共同需求

每个字符串都有一个字符集和编码。当您将 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 article

特殊需要

现在,问题作者问,“每个字符串都存储为一个字节数组,对吗?为什么我不能简单地拥有这些字节?”

他不想要任何转变。

C# spec

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's answer 显示了如何使用 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 指出的那样,结果取决于机器的字节序。但问题作者并不关心这一点。


@Jan 这是正确的,但字符串长度已经给出了代码单元的数量(不是代码点)。
感谢您指出了这一点!来自 MSDN:“Length 属性 [of String] 返回此实例中 Char 对象的数量,而不是 Unicode 字符的数量。”因此,您的示例代码在编写时是正确的。
@supercat “char 类型代表一个 UTF-16 代码单元,而 string 类型代表一个 UTF-16 代码单元序列。”—_C# 5 Specification._ 虽然,是的,没有什么可以防止无效的 Unicode 字符串: new String(new []{'\uD800', '\u0030'})
@TomBlodget:有趣的是,如果使用 Globalization.SortKey 的实例,提取 KeyData,然后将每个字节的结果字节打包到 String [每个字符两个字节,MSB 优先],调用在生成的字符串上调用 String.CompareOrdinal 将比在 SortKey 的实例上调用 SortKey.Compare 甚至在这些实例上调用 memcmp 快得多。鉴于此,我想知道为什么 KeyData 返回 Byte[] 而不是 String
唉,正确的答案,但为时已晚,永远不会有那么多的选票被接受。由于 TL;DR 人们会认为接受的答案很摇滚。 copyenpastit 并对其进行投票。
J
Joel Coehoorn

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

我将解决您的后续问题:为什么需要选择编码?为什么你不能从字符串类本身得到它?

答案分为两部分。

首先,字符串类内部使用的字节无关紧要,无论何时假设它们确实如此,您都可能会引入错误。

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

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

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

这将我带到第二部分...选择 Unicode 编码 告诉 .Net 使用底层字节。您确实需要选择这种编码,因为当一些新奇的 Unicode-Plus 出现时,.Net 运行时需要自由地使用这种更新、更好的编码模型,而不会破坏您的程序。但是,目前(以及可预见的未来),只需选择 Unicode 编码即可满足您的需求。

了解您的字符串必须重新写入线路也很重要,即使您使用匹配的编码,这也至少涉及位模式的一些转换。计算机需要考虑大字节序与小字节序、网络字节顺序、数据包化、会话信息等。


在 .NET 中有一些区域,您必须获取字符串的字节数组。许多 .NET Cryptorography 类包含接受字节数组或流的方法,例如 ComputeHash()。您别无选择,只能先将字符串转换为字节数组(选择编码),然后可以选择将其包装在流中。但是,只要您选择一种编码(即 UTF8)并坚持使用它,就没有问题。
当我不知道编码是关于什么并且由于懒惰而拒绝学习时,我的心情与 OP 完全相同(只需给我字节......)你的答案是第一个(在顶部)小心给予明确的警告。我很高兴在我的 PC 上编写和读取二进制文件……直到我不得不处理 MAC/Linux 用户、网络、将应用程序升级到最新的操作系统、更多地了解字节序、自定义编码(电子 ROM 和数据)。那天 .Net 将用 4 个字节编码 Unicode,UTF8 最多 8 个字节。我学会了尽可能避免绕过本机方法的艰难方法。
5
5 revs

只是为了证明 Mehrdrad 的声音 answer 有效,他的方法甚至可以坚持 unpaired surrogate characters(其中许多人反对我的回答,但每个人都同样有罪,例如 System.Text.Encoding.UTF8.GetBytesSystem.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 替换高代理字符

每次这个问题有动静时,我仍然在考虑一个序列化程序(无论是来自微软还是来自 3rd 方组件),它可以保留字符串,即使它包含不成对的代理字符;我时不时地在谷歌上搜索:序列化不成对的代理字符.NET。这并没有让我失眠,但是当不时有人评论我的答案有缺陷时,这有点烦人,但是当涉及到未配对的代理字符时,他们的答案同样有缺陷。

该死,微软应该在其 BinaryFormatter 中使用 System.Buffer.BlockCopy

谢谢!


代理不是必须成对出现才能形成有效的代码点吗?如果是这样的话,我可以理解为什么数据会被破坏。
@dtanders 是的,这也是我的想法,它们必须成对出现,如果您故意将它们放在字符串上并使它们不成对,就会发生不成对的代理字符。我不知道的是为什么其他开发人员一直在强调我们应该使用编码感知方法,因为他们认为序列化方法(my answer,这是 3 年多以来公认的答案)并不能保持未配对的代理字符完好无损。但是他们忘记检查他们的编码感知解决方案是否也不会保留未配对的代理字符,具有讽刺意味的是ツ
如果有一个在内部使用 System.Buffer.BlockCopy 的序列化库,那么所有编码倡导者的论点都将没有实际意义
@MichaelBuen 在我看来,主要问题是您用粗体大字表示无关紧要,而不是说在他们的情况下无关紧要。结果,您鼓励那些查看您的答案的人犯基本的编程错误,这将在未来引起其他人的挫败感。未配对的代理在字符串中无效。它不是 char 数组,因此将字符串转换为另一种格式会导致该字符出现错误 FFFD 是有意义的。如果您想进行手动字符串操作,请按照推荐使用 char[]。
@dtanders:System.StringChar 的不可变序列; .NET 始终允许从任何 Char[] 构造 String 对象并将其内容导出到包含相同值的 Char[],即使原始 Char[] 包含不成对的代理项。
P
Peter Mortensen

试试这个,代码少了很多:

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
@mg30rg:为什么你认为你的例子很奇怪?当然,在可变宽度编码中,并非所有字符都具有相同的字节长度。它出什么问题了?
@Vlad 不过,这里更有效的评论是,作为编码的 unicode 符号(因此,作为字节),包含它们自己的变音符号的字符将给出不同的结果,而不是变音符号分裂成添加到字符的修饰符符号。但是 iirc 在 .net 中有一些方法可以专门将它们分开,以允许获得一致的字节表示。
T
Tshilidzi Mudau

好吧,我已经阅读了所有答案,它们是关于使用编码或关于丢弃未配对代理的序列化的。

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

所以我在这种情况下使用字节数组的 Base64 编码,但是,在 Internet 上,C# 中只有一种解决方案,而且它有 bug,而且只有一种方法,所以我已经修复了 bug 并且回写程序。在这里,未来的谷歌人:

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);
}

无需使用自定义方法将字节数组转换为 base64,您只需使用内置转换器:Convert.ToBase64String(arr);
@Makotosan 谢谢,但我确实使用 Convert.ToBase64String(arr); 进行 base64 转换 byte[] (data) <-> string (serialized data to store in XML file)。但要获得最初的 byte[] (data),我需要对包含 binary 数据的 String 做一些事情(这是 MSSQL 将它返回给我的方式)。所以上面的函数是用于String (binary data) <-> byte[] (easy accessible binary data)的。
K
Konamiman

还请解释为什么要考虑编码。我不能简单地获取字符串存储在哪些字节中吗?为什么要依赖编码?!!!

因为没有“字符串的字节”之类的东西。

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

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

因此,简而言之,尝试“在不使用编码的情况下获取字符串的字节”与“在不使用任何语言的情况下编写文本”一样不可能。

顺便说一句,我强烈建议您(以及任何人)阅读这篇小智慧:The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)


请允许我澄清一下:已使用一种编码将“hello world”转换为物理字节。由于字符串存储在我的计算机上,我确信它必须以字节存储。我只想访问这些字节以将它们保存在磁盘上或出于任何其他原因。我不想解释这些字节。因为我不想解释这些字节,所以此时需要编码就像需要电话线调用 printf 一样错位。
但同样,除非您使用编码,否则没有文本到物理字节转换的概念。当然,编译器以某种方式将字符串存储在内存中 - 但它只是使用您(或编译器开发人员以外的任何人)不知道的内部编码。所以,无论你做什么,你都需要一个编码来从字符串中获取物理字节。
@Agnel Kurian:当然,字符串在某处有一堆字节存储其内容(UTF-16 公平)。但是有一个很好的理由阻止您访问它:字符串是不可变的,如果您可以获得内部 byte[] 数组,您也可以修改它。这打破了不变性,这是至关重要的,因为多个字符串可能共享相同的数据。使用 UTF-16 编码来获取字符串可能只是将数据复制出来。
@Gnafoo,字节的副本就可以了。
i
iliketocode

C# 将 string 转换为 byte 数组:

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

g
gkrogers
byte[] strToByteArray(string str)
{
    System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
    return enc.GetBytes(str);
}

但是,为什么要考虑编码呢?为什么我不能简单地获取字节而不必查看正在使用的编码?即使它是必需的,字符串对象本身不应该知道正在使用什么编码并简单地转储内存中的内容吗?
这并不总是有效。使用这种方法,我发现一些特殊字符可能会丢失。
J
Jarvis Stark

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

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);

VUP这个解决了我的问题(byte[] ff = ASCIIEncoding.ASCII.GetBytes(barcodetxt.Text);)
J
John Rasch

随着随 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);
    }
}

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

请注意,使用 Span<T> 需要安装 System.Memory NuGet package

无论如何,实际原始问题和后续评论暗示底层内存没有被“解释”(我认为这意味着没有修改或读取超出按原样编写的需要),表示应该使用 Stream 类的某些实现,而不是将数据作为字符串进行推理。


new string(f) 是错误的,如果您希望往返所有字符串,您至少需要使用接受显式长度的构造函数重载。
i
iliketocode

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


也就是说,如果你的字符串是英文的 - 是中文的,你最好使用 UTF-16。
H
Hans Passant

关键问题是字符串中的字形需要 32 位(字符代码为 16 位),但一个字节只有 8 位可用。除非您将自己限制为仅包含 ASCII 字符的字符串,否则不存在一对一映射。 System.Text.Encoding 有很多方法可以将字符串映射到 byte[],您需要选择一种可以避免信息丢失并且在客户需要将 byte[] 映射回字符串时易于使用的方法.

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


仅当您的大多数字符都在英语 (ASCII) 字符集中时,UTF-8 才是紧凑的。如果您有一长串汉字,则 UTF-16 将是比 UTF-8 更紧凑的编码。这是因为 UTF-8 使用一个字节来编码 ASCII,否则使用 3(或者可能是 4)。
真的。但是,如果你熟悉处理中文文本,你怎么会不知道编码呢?
P
Peter Mortensen

利用:

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

结果是:

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

OP 特别要求不要指定编码......“不手动指定特定编码”
A
Alessandro Annini

最快的方式

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

编辑 Makotosan 评论这是现在最好的方法:

Encoding.UTF8.GetBytes(text)

不需要ASCIIEncoding.....。最好只使用 Encoding.UTF8.GetBytes(text)。
G
Gerard ONeill

最接近 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 的答案。所以 - 如果你无论如何都要转换......为什么不'编码'?


这些字符不支持 UTF-8 或 UTF-16 甚至 UTF-32 例如:񩱠 & (Char) 55906 & (Char) 55655。所以你可能错了,Mehrdad 的回答是安全转换,而不考虑使用什么类型的编码。
Raymon,字符已经由一些 unicode 值表示——所有 unicode 值都可以用所有 utf 表示。你在说什么有更长的解释吗?这两个值(或 3..)存在于什么字符编码中?
它们是任何编码范围都不支持的无效字符。这并不意味着它们 100% 没用。无论编码如何,将任何类型的字符串转换为其等效的字节数组的代码根本不是错误的解决方案,并且在所需的场合有自己的用途。
好的,那么我认为您没有理解问题所在。我们知道它是一个符合 unicode 的数组——事实上,因为它是 .net,我们知道它是 UTF-16。所以那些字符不会在那里存在。您也没有完全阅读我关于内部表示更改的评论。 String 是一个对象,而不是编码的字节数组。所以我不同意你最后的说法。您希望代码将所有 unicode 字符串转换为任何 UTF 编码。这做你想要的,正确的。
对象是数据序列,最初是描述处于当前状态的对象的位序列。因此,编程语言中的每个数据都可以转换为字节数组(每个字节定义 8 位),因为您可能需要在内存中保留任何对象的某些状态。您可以在文件或内存中保存并保存一系列字节,并在从磁盘读取后将其转换为整数、bigint、图像、Ascii 字符串、UTF-8 字符串、加密字符串或您自己定义的数据类型。所以你不能说对象与字节序列不同。
J
Jason Goemaat

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

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

迈赫达德的回答

您可以使用 Mehrdad's answer,但它确实使用了编码,因为字符是 UTF-16。它调用 ToCharArray,它查看 the source 创建一个 char[] 并将内存直接复制到它。然后它将数据复制到也分配的字节数组中。所以在底层它复制底层字节两次并分配一个在调用后不使用的字符数组。

汤姆布洛杰特的回答

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

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

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

如果您有包含无效字符的“字符串”,使用编码可能会给您带来麻烦,但这不应该发生。如果您使用无效字符将数据放入字符串中,那么您做错了。您可能应该使用字节数组或 Base64 编码开始。

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

我认为问题不是你为什么要担心编码,而是你为什么要忽略它并使用其他东西。编码旨在以字节序列表示字符串的抽象。 System.Text.Encoding.Unicode 将为您提供一点字节序编码,并且在现在和将来的每个系统上都将执行相同的操作。


实际上,C# 中的字符串不仅限于 UTF-16。真实的是它包含一个 16 位代码单元的向量,但这些 16 位代码单元并不限于有效的 UTF-16。但由于它们是 16 位的,您需要一个编码(字节顺序)将它们转换为 8 位。然后字符串可以存储非Unicode 数据,包括二进制代码(例如位图图像)。它仅在进行此类解释的 I/O 和文本格式化程序中被解释为 UTF-16。
因此,在 C# 字符串中,您可以安全地存储像 0xFFFF 或 0xFFFE 这样的代码单元,即使它们是 UTF-16 中的非字符,并且您可以在 0xDC00..0xDFFF(即在 UTF-16 中无效的未配对代理)。同样的评论适用于 Javascript/ECMAscript 和 Java 中的字符串。
当您使用“GetBytes”时,您当然没有指定编码,但您假设一个字节顺序来获取本地存储在字符串中的每个代码单元的两个字节。当你从字节构建一个新字符串时,你还需要一个转换器,不一定是 UTF-8 到 UTF-16,你可以在高字节中插入额外的 0,或者打包两个字节(以 MSB 优先或 LSB 优先顺序)相同的 16 位代码单元。然后,字符串是 16 位整数数组的紧凑形式。与“字符”的关系是另一个问题,在 C# 中它们不是实际类型,因为它们仍然表示为字符串
İ
İlker Elçora

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

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

J
Jodrell

如果你真的想要一个字符串的底层字节的副本,你可以使用一个类似下面的函数。但是,您不应该继续阅读以找出原因。

[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() 不会对所有 .NET string 实例进行往返。
@BenVoigt,我调整了答案。这些天,我会做一些不那么特定于 Windows 的事情。
您可能会考虑避免使用 p/invoke,Marshal.Copy 可以很好地从指针复制到字节数组。 stackoverflow.com/a/54453180/103167
i
iliketocode

这是我对 StringByte[] 转换的不安全实现:

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
    }
}

在 Itanium 版本的 .NET 上,RuntimeHelpers.OffsetToStringData 的值是否是 8 的倍数?因为否则这将由于未对齐的读取而失败。
调用 memcpy 不是更简单吗? stackoverflow.com/a/27124232/659190
j
jpmc26

当被问及您打算如何处理这些字节时,您responded

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

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

您可能需要与以不同语言或运行时实现的流程进行通信。 (例如,这可能包括在另一台机器上运行的服务器或将字符串发送到 JavaScript 浏览器客户端。)

该程序将来可能会以不同的语言或运行时重新实现。

.NET 实现可能会更改字符串的内部表示。您可能认为这听起来有些牵强,但这实际上发生在 Java 9 中以减少内存使用。 .NET 没有理由不效仿。 Skeet 认为 UTF-16 今天可能不是最优的,因为 emoji 和其他 Unicode 块也需要超过 2 个字节来表示,这增加了内部表示在未来可能发生变化的可能性。

对于通信(与完全不同的进程或将来与相同的程序),您需要严格定义您的协议,以尽量减少使用它的难度或意外产生错误。依赖于.NET 的内部表示不是一个严格的、清晰的,甚至是不保证是一致的定义。标准编码是一个严格的定义,将来不会让您失望。

换句话说,如果不指定编码,您将无法满足您对一致性的要求。

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

所以选择一种编码并使用它:

using System.Text;

// ...

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

如您所见,与实现您自己的读取器/写入器方法相比,仅使用内置编码对象实际上也需要更少的代码。


B
Bharat Mane

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

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

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

参考this link.

使用 System.Text.Encoding.GetBytes 序列化为字节数组。对于逆运算,使用 System.Text.Encoding.GetChars。此函数返回一个字符数组,因此要获取字符串,请使用字符串构造函数 System.String(char[])
Ref this page.

例子:

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

N
NH.

这取决于你想要的字节 FOR

这是因为,正如 Tyler 所说的 said,“字符串不是纯数据。它们也有 information。”在这种情况下,信息是在创建字符串时假定的编码。

假设您将二进制数据(而不是文本)存储在字符串中

这是基于 OP 对他自己的问题的评论,如果我理解 OP 对用例的提示,这是正确的问题。

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

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

其他人都错误地回答了这个不正确的问题。

如果字符串按原样看起来不错,只需选择一种编码(最好以 UTF 开头),使用相应的 System.Text.Encoding.???.GetBytes() 函数,并告诉谁您将字节提供给您选择的编码。


C
Chris Hutchinson

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

总结一下其他人所说的话:

存储这种序列化的表示对系统字节顺序、编译器优化以及对正在执行的 .NET 运行时中字符串的内部表示的更改很敏感。避免长期存储 避免在其他环境中反序列化或解释字符串 这包括其他机器、处理器架构、.NET 运行时、容器等。这包括比较、格式化、加密、字符串操作、本地化、字符转换等。避免使关于字符编码的假设默认编码在实践中往往是 UTF-16LE,但编译器/运行时可以选择任何内部表示

避免长期存放

避免在其他环境中反序列化或解释字符串这包括其他机器、处理器架构、.NET 运行时、容器等。这包括比较、格式化、加密、字符串操作、本地化、字符转换等。

这包括其他机器、处理器架构、.NET 运行时、容器等。

这包括比较、格式化、加密、字符串操作、本地化、字符转换等。

避免对字符编码做出假设默认编码在实践中往往是 UTF-16LE,但编译器/运行时可以选择任何内部表示

默认编码在实践中往往是 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);
}

进一步的洞察力

在 C++ 中,这大致相当于 reinterpret_cast,而在 C 中,这大致相当于对系统字类型 (char) 的强制转换。

在最新版本的 .NET Core 运行时 (CoreCLR) 中,跨度操作有效地调用编译器内在函数和各种优化,这些优化有时可以消除边界检查,从而在保持内存安全的同时实现卓越的性能,假设您的内存是由 CLR 和跨度不是从非托管内存分配器的指针派生的。

注意事项

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


一些评论:尽管似乎是流行的观点,但这种机制的一个完全有效的用例是运行时加密:提取字节表示,加密字节,并将加密的有效负载保存在内存中。这最大限度地减少了编码开销,并且只要它没有被序列化并转移到另一个环境,就不会因解释语义或内部表示而遭受任何特定于编码的问题。为此目的使用 SecureString 存在争议,并且担心垃圾收集,但除此之外,前提似乎是合理的。
CoreCLR 至少有一个提议可以引入更紧凑的内部表示:github.com/dotnet/runtime/issues/6612
u
user1120193
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

j
jonsca

只需使用这个:

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

...并失去所有跳跃应付高于 127 的字符。在我的母语中,写“Árvíztűrő tükörfúrógép.”是完全有效的。 System.Text.ASCIIEncoding.Default.GetBytes("Árvíztűrő tükörfúrógép.").ToString(); 将返回 "Árvizturo tukörfurogép." 丢失的无法检索的信息。 (而且我还没有提到你会丢失所有字符的亚洲语言。)