如何在.NET (c#)中将string转换为byte[]而无需手动指定特定的编码?
我要加密字符串。我可以加密它不转换,但我仍然想知道为什么编码在这里发挥作用。
另外,为什么要考虑编码?我不能简单地获取字符串存储在哪些字节中吗?为什么要依赖字符编码?
- 您对编码角色的困惑让我怀疑这是否是正确的问题。为什么要将字符串转换为字节数组?你打算怎么处理字节数组?
- 我要加密它。我可以加密它而不转换,但我仍然想知道为什么编码在这里发挥作用。我说的是字节数。
- 如果要对其进行加密,那么在解密之后仍然需要知道编码是什么,以便知道如何将这些字节重新解释为字符串。
- 每个字符串都存储为字节数组,对吗?为什么我不能简单地拥有这些字节?
- 看看Jon Skeet在一篇帖子中给出的答案吧。它将解释为什么要依赖编码。
- 我认为安东尼是在试图解决<300查尔的根本脱节。假设字符串有某种一致的内部表示,而实际上这种表示可以是任何东西。要创建并最终解码字节流,必须选择要使用的编码。
- "字符串是一个字符数组,而字符在。net世界中不是字节"好的,但不管编码是什么,每个字符都映射到一个或多个字节。我可以不用指定编码就得到这些字节吗?
- 编码将字符映射到字节。例如,在ASCII码中,字母"A"映射到数字65。在不同的编码中,它可能不一样。尽管如此,. net框架中对字符串的高级方法使得这在很大程度上无关紧要(除了在本例中)。
- 你可以采取简单的方法,在两边都使用UTF-8。
- 对于. net,最简单的方法是在两边都使用UTF-16,因为这是. net内部使用的。
- 唱反调:如果您想获得内存字符串的字节(. net使用它们)并以某种方式操作它们(即CRC32),并且永远不想将其解码回原始字符串……为什么要关心编码,或者如何选择使用哪种编码,这并不是很直接的问题。
- 令人惊讶的是还没有人给出这个链接:joelonsoftware.com/articles/Unicode.html
- @Bevan: 2009年1月23日,你来晚了。
- 在。net中如何将字符串转换为字节数组的可能复制
- @AgnelKurian,一个char是一个struct,它恰好将当前的值存储为16位数字(UTF-16)。您真正想要的(获取字符字节)在理论上是不可能的,因为它在理论上并不存在。根据定义,char或string没有编码。如果内存表示改为UTF-32呢?您的"获取字节,将它们推回去"将由于编码而失败,因为您避免了编码。所以"为什么要依赖编码?!!"依赖于编码,所以您的代码是可靠的。
- @Bevan,因为这个链接的必要的错误不适合600个字符的评论,而且作为答案会离题吗?
- @JonHanna开导我们。在这种情况下,一篇博客文章并不是不合适的。
- @AgnelKurian我写hackcraft.net/xmlUnicode的原因之一是看到了不正确的引用,尽管我的目标是它本身是有用的,而不是仅仅争论关于克林根的神话,或者指出EBCDIC仍然在野外被发现。
- 字符不是字节,字节也不是字符。char既是字体表的键,也是词法传统。字符串是字符序列。(单词、段落、句子和标题也有它们自己的词汇传统,这些传统证明了它们自己的类型定义是正确的——但是我跑题了)。与整数、浮点数和其他所有东西一样,字符被编码为字节。曾经有一段时间编码是一对一的:ASCII。然而,为了适应所有的人类符号,一个字节的256种排列是不够的,编码被设计成选择性地使用更多的字节。
- @usr:你只是用你的编辑使几乎所有的答案无效,而且让人们用他们的自然搜索查询更难找到这个问题(但你可能是故意这样做的)。
- @Mehrdad现有的答案已经无效(不是问什么)。你的回答几乎是唯一能回答问题的答案。(不过,我建议您编辑您的答案,以包含一些警告,说明这种方法实际上几乎从来都不是最好的方法。)
- 四年后,我坚持我对这个问题的最初看法。它本质上是有缺陷的,因为我们在讨论一个字符串,这个事实意味着解释。该字符串的编码是序列化契约的隐式部分,否则它只是一堆无意义的位。如果您想要无意义的位,为什么要从字符串生成它们呢?只要写一串0就行了。
- @Greg D,假设我的客户有一些浮点数,以某种奇特的格式存储天文距离。他只用了一种格式。他要我负责写和读这些数字。我不是在解释它们。我的客户解释了这些数字,他需要给我的只是我需要写的字节。当他阅读时,他只需要我写的字节。当他对所有数字只使用一种格式时,除了字节之外,每次都存储一个格式标志是对空间的浪费。
- 如果你要写任意的二进制数据,那就写二进制数据。这与最初的问题(本质上是关于序列化字符串的)无关。
- @GregD,所以你想为1000个不同的字符串存储相同的编码1000次?
- 你在戏弄我吗?那个问题没有意义。我可以推断你的意思是,"……存储1000种不同字符串使用了1000次的编码信息。"但是,从来没有人说过要这样做,而且在前面我声明"该字符串的编码是序列化契约的一个隐式部分……"时明确地否认了这一点,所以您不可能是这个意思。
- @GregD:是否有表示不可变字节序列的内置类型?我很容易理解使用字符串来保存二进制数据是很难看的,但我不确定什么更好?使用Byte[]通常需要防御副本,任何时候数据被传递;相反,因为String是不可变的,所以可以安全地传递引用,而不需要复制任何数据。
- 我首先想到的是内存流。根据场景的不同,将它作为普通流传递也会产生更多的可组合性。我还没有机会使用它,但是如果性能很重要的话,旧的Buffer (msdn.microsoft.com/en-us/library/&hellip)类可能也是一个可行的选择,不过我不认为它支持只读访问。(我可能会问您传递给您不信任的字节[]是什么—ReadOnlyCollection可能会有所帮助)
- 为什么不使用base64编码的字符串?它是一个字符串,因此是不可变的,并且每个字符都是友好显示的。从一种方法到另一种方法有一个简单的转换(不确定它是否是与web或其他相关的扩展方法)。
- @AgnelKurian"他想让我负责写和读这些数字。我不是在解释它们。"-如果你不解释他们,你会有字节,而不是"数字"。然后,你的问题就消失了。如果您有"数字",这意味着您已经解释/解码了它们并丢弃了原始字节数据。现在,您想尝试重新构建数据(编码),这甚至是不可能的。那些以10为基底的数是什么?把它们塞进以2为基底的浮点数中,你就永远毁掉了它们?不想编码?不解码。想要个字节吗?然后使用字节。
- 您是否假设System.Text.Encoding.Unicode.GetBytes(); 正在执行某种您希望避免的昂贵转换?如果是这样,你的假设就错了。
- 第一个注释(引用):每个字符串都存储为字节数组,对吗?为什么我不能简单地拥有这些字节?不,每个字符串(或多或少)存储为一个16位代码单元数组,这些单元对应于UTF-16。如果字符串在平面0之外包含Unicode字符,则会有代理对。您可以很容易地得到这种表示形式:var array1 = yourString.ToCharArray();如果出于某种原因您希望代码单元是UInt16值,那么就执行var array2 = Array.ConvertAll(array1, x => x);。那里有一个yanan4。
- @AgnelKurian"我的客户解释了这些数字,他需要给我的只是我需要写的字节。"然后让客户端给你一个字节数组,而不是字符串!一旦将其放入字符串,就已经决定了编码(ASCII、UTF-8或其他)。另外,请把我引用的句子放到问题文本中,这样问题的用例就更清晰了。
- 编码是必要的,因为代表字符的大小,字节——取决于它,而不是仅仅因为sizeof (char)是不同的。ASCII(1个字节)和WideString(2字节),而是因为它甚至可以改变——在utf - 8字符表示为1到4字节
- 不用担心编码是一回事。不希望指定编码完全是另一回事。如果让您感到沮丧的是应该使用哪种编码,那么就选择一种,并始终使用它进行字符串到字节数组和字节数组到字符串的转换。例如,始终使用Unicode或UTF-8。你的选择。在您选择了编码之后,您就不必再担心了,您的问题就解决了。但是,如果您的挫折来自于需要指定编码,那么您最好习惯它,因为不管您喜欢与否,编码正在发生。
- 您应该始终关注字节数组中表示的字符串编码。字符串在内存中以字节数组表示的假设是任意的。它恰好与当前.net实现中的情况类似。没有人能保证它将来不会变成链表实现(或任何其他奇特的数据结构)。即使您使用相同的系统和程序来读取加密的数据,由于您没有明确指定您所使用的编码方式,.net的未来补丁也总是有可能将所有内容都分解开来
与这里的答案相反,如果不需要解释字节,则不需要担心编码!
正如您提到的,您的目标很简单,就是"获取字符串存储在哪些字节中"。(当然,还能够从字节重新构造字符串。)
对于这些目标,我真的不明白为什么人们总是告诉你需要编码。您当然不需要为此担心编码。
就这样做吧:
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 );
} |
只要您的程序(或其他程序)不尝试以某种方式解释字节(您显然没有提到您打算这么做),那么这种方法就没有什么问题!担心编码只会让你的生活变得更加复杂,没有真正的原因。
这种方法的额外好处是:如果字符串中包含无效字符,这并不重要,因为无论如何,您仍然可以获取数据并重构原始字符串!
它将以相同的方式进行编码和解码,因为您只是在查看字节。
但是,如果使用特定的编码,就会给编码/解码无效字符带来麻烦。
- +1正是我的想法,我不知道这里有些人坚持编码。只需要做一个内存转储/序列化(微软的默认序列化库有缺陷)。我希望我以前知道这个BlockCopy API:-)
- @MichaelBuen:是的。只要您的内存转储/序列化不尝试解释数据,就没有问题。要记住的经验法则是:如果您的程序(或其他程序)需要将GetBytes的输出转换回相同的字符串,那么它可能只使用GetString来完成此操作。只要不违反这一点,就可以完全忽略编码的概念。
- @Mehrdad我同意你的逻辑,但是当我测试它的编码方法稍微快一点时,我很惊讶。我想我原以为你的方法会更快些(尽管里面没有太多)
- @Ian1971:可能是因为ToCharArray()分配了一个新的数组,这个数组随后被丢弃?
- @Ian1971编码方法有其缺陷,它不能保存原始字符串的图像副本;特别是,高代理字符不能用编码方法保存。检查这个测试:stackoverflow.com/a/10384024
- 这个方法的丑陋之处在于,GetString和GetBytes需要在具有相同endianness的系统上执行才能工作。所以你不能用这个来获取字节你想在其他地方变成一个字符串。所以我很难想出一个我想用这个的情况。
- @CodeInChaos在这些字节前加上BOM前缀,表示它来自. net世界(即utf - 16),那么mindprod.com/jgloss/utf.html
- @CodeInChaos:就像我说的,如果你想在相同的系统上使用它,在相同的功能集上使用它,那么这就是关键所在。如果没有,那么你就不应该使用它。
- -1我保证有些人(不懂字节和字符的人)会想把他们的字符串转换成字节数组,他们会谷歌并读取这个答案,他们会做错误的事情,因为几乎在所有情况下,编码都是相关的。
- @artbristol:如果他们懒得看答案(或者其他答案……),那么我很抱歉,那么我就没有更好的方式和他们交流了。我通常会选择回答OP,而不是试图猜测其他人会怎么处理我的答案——OP有权知道,仅仅因为有人可能会滥用一把刀,并不意味着我们需要为自己隐藏世界上所有的刀。不过如果你不同意也没关系。
- 这个问题是三年前提出的,完全是模棱两可的。您没有证据表明OP将如何使用这些字节。其他人也会有同样的问题,但是他们会计划在编码很重要的情况下使用字节,在这种情况下,您的答案将是完全错误的。
- 我的想法是:我不是法官。在回答他之前,我不会要求OP提供"证据"来证明他的情况(这与其他人可能会做的相反)。他清楚地说:"难道我不能简单地获取字符串存储在哪些字节中吗?"为什么要依赖编码?,我的答案是100%准确的,在我看来比这一页上的其他人都要准确。在我看来,他现在已经明白这些警告了。而且,答案来自3年前的事实也无关紧要。但是,如果你想先要"证据",那就是你的风格,你可以随意投反对票。
- 这个答案在很多层面上都是错误的,但最重要的原因是它的解密"您不需要担心编码!"GetBytes()和GetString()这两个方法是多余的,因为它们只是重新实现了编码. unicode .GetBytes()和编码. unicode .GetString()已经实现的功能。"只要您的程序(或其他程序)不尝试解释字节"这句话也存在根本性的缺陷,因为它隐含地表示字节应该被解释为Unicode。
- @David:"……隐式地,它们意味着字节应该被解释为"我不知道您如何读取答案,但它"隐式地"意味着它们可以是任何编码。另外,如果您认为这些方法只是Encoding.Unicode的"重新实现",只是因为它们做了相同的事情,那么您似乎没有正确地理解抽象层。
- @Mehrdad……它"隐含"的意思是它们可以是任何编码"我不明白这个表述,你说这个到底是什么意思?据我所知,您的GetBytes()方法将返回一个Unicode编码的字符串字节数组,而您的GetString()方法(如果传递一个Unicode编码的字符串字节数组表示形式)将返回一个可读的字符串,在任何其他编码中都会返回垃圾。更糟糕的是,如果传递一个包含奇数个字符的字符串的UTF-8编码字节数组,GetString()将崩溃。
- @David:是的,它崩溃在UTF-8数据上,因为GetBytes从来没有返回UTF-8数据。看起来您期望的抽象层与实际存在的抽象层是不同的。如果你不知道如何正确使用它,那就不要;答案可能不适合您的用例。然而,我100%坚持我的回答,即它的用途是正确的,我试图完全清楚。
- 然后我们又回到了原点。GetBytes和GetString是Encoding.Unicode.GetBytes()\GetString()的重新实现。您正在重新构造您的参数,使其偏离您最初的"任何编码"断言。我并不是在争论您提供的OP ' t work(至少对于unicode是这样)代码,我只是不认为它会加深他对编码的理解,不管您如何试图隐藏它,他都在使用编码。
- @David:唉,是的,它们碰巧是重新实现,但是在这个抽象级别上这是不相关的。如果你关心这个事实,那么你就用错了。如果您不知道我的意思,那么请不要使用它,但是它对于OP的用例/抽象级别来说是100%有效的。
- 我只是需要字节我的密码工作,我想你的答案仍然岩石!
- 答案是-1。以上是David和artbristol的评论+1。当然。net中有一个字符串的内存表示。它恰好是小端UTF-16。当你得到字节数组时,你得到的是那个编码。如果您想做的只是将字节数组转换回字符串,那么答案就足够了。但答案是有限的,也是危险的。例如,如果要在HTTP请求中包含字节,则需要知道整个请求的编码。如果您从事将字符转换为字节的业务,您必须了解编码。
- 答案是-1,大卫,阿特布里斯托尔和具体的评论是+1。这个答案并没有提到,只有在同一个平台上执行这两个方法时,它才能工作。此外,它没有增加任何价值。答案的论点是为一个简单的问题提供一个简单的答案,但是答案要比简单地使用Encoding.Unicode复杂得多。如果只是使用这些方法,也不需要担心编码问题,但是无论在什么平台上运行它们,它们都是安全的。
- @ConcreteGannet:我很高兴我们都同意"如果你想做的就是把字节数组转换回字符串,那么答案就足够了。"这就是我的答案。
- 安全并不是唯一的问题。在您的(假设的?)系统中,UTF-16不是内部表示,Encoding.Unicode将会更慢,这对预期的用例没有任何好处(OP已经理解了这一点)。此外,安全只是一个问题,如果你不知道你在做什么。您没有看到C程序员避免指针,尽管它们有多"危险",不是吗?你也不会看到建筑工人避免使用电锯和电钻。仅仅因为你认为某件事很危险并不意味着人们没有权利知道它。
- 经过一些询问,OP说他们打算加密字符串。在转换为字节数组之后的下一步很可能是某种形式的输出。您的答案是否正确取决于读取这些加密字节的是什么。OP并没有说. net应用程序将读取加密的字节。如果要读取其他内容,OP应该确保编码符合读取器的期望。如果字符串很大并且只包含或大部分纯ASCII, UTF-8将更紧凑、更快地加密和更快地输出。
- 在. net中查询string的字节数类似于查询object的字节数。string和char类型的目的是抽象实现细节。通过使用这个答案,您随意地绕过了实现细节,并将得到一个类似于二进制序列化的脆弱解决方案。没有理由使用这个答案,因为使用编码更健壮、更可移植、更符合逻辑,而且最重要的是更容易。说真的,编码的答案只有一行……为什么要做这样疯狂的事?!
- @Travis:只不过这与要求object: . net的字节数不是一回事,它特别阻止您这样做,但并不阻止您这样做。这本身就足以告诉你它们是不同的。
- @Mehrdad,跑题了,你需要意识到,技术上的可行性并不等同于实用上的相关性,也不等同于架构上的合理性。回到主题上,不管您是否意识到,您都在有效地执行System.Text.Encoding.Unicode.GetBytes(str),因为这就是. net在内存中表示string所做的事情。人们说你不懂编码,因为他们知道你无法避免编码。你所做的唯一一件事就是跳过去把它藏起来!你真的还认为这是个好主意吗?
- @Mehrdad,我第二次读到你忽略了我的全部评论。你回应的那一部分,你误解了(akin !=相同)。我真的开始怀疑你为什么要极力推销这个明显有缺陷的答案。
- @Travis:我读了你的全部评论,但它的整个基础是错误的(你声称它类似于读取object的字节)。两者之间没有相似之处。我要告诉您的是,这段代码针对的抽象级别与您想象的不同。说"它就像Encoding.Unicode.GetBytes"是错误的,因为它打破了抽象的障碍。我不知道还能告诉你什么。我的回答已经达到了目的,那就是直接回答OP的问题。如果你不喜欢我的答案,那就投反对票;这就是它的作用!
- 我要告诉你的最后一件事(因为我刚刚注意到)就是阅读下面的答案。我之前已经提到过这个问题,但是因为这个答案确实证明了这一点,所以我想再说一遍:我的答案是有效的。完美地恢复字符串;基于编码的方法无法处理不能正确表示的char序列。
- 因为。net内部使用UTF-16和16位字符,所以本例中的字符串实际上是使用UTF-16编码的,不是吗?如果在本例中创建的字节数组上使用encode . unicode . getstring(),即UTF-16,它将生成原始字符串值。
- 是的,这个答案适用于小众用例。但是其他的答案适用于所有的用例。为什么不用高级的呢?需要你输入编码的技术?因此得到一个大的脂肪-1。
- 不,其他的答案并不适用于所有的用例。你读过迈克尔·布恩的答案吗?他的答案告诉你为什么我的答案能处理其他答案都不能处理的案件。这里没有一个答案处理所有的情况,但是我的答案处理OP的相关情况。
- @Mehrdad:很好。但我还是不喜欢这个解。对他的并不完全确定(目前我不想学习关于未配对代理的知识,但它似乎是沿着陷阱表示的路线进行的)。
- @Thomas:我并不真的关心您是否"喜欢"这个解决方案(见鬼,我也不是特别喜欢),但是您不能否认它是这里给出的OP用例(字符串和字节数组之间的转换)的唯一正确答案。其他的答案会在这个过程中破坏一些char序列,而我的答案不会。保持你的反对票,但请三思而后行,不要随波逐流,散布错误信息。
- @Mehrdad: How do I convert a string to a byte array in .NET (C#)?是OP描述的用例。实际上,任何返回byte[]的答案在技术上都是正确的。但我已经和你聊够了。
- "担心编码只会让你的生活莫名其妙地变得更复杂。"-呃,除了担心编码的答案比这个简单得多。当然,这个答案仍然依赖于一种特定的编码—str.ToCharArray()必须依赖于一种编码,即使代码中没有明确提到这种编码(这只能被认为是不好的)。我很尊敬你,爸爸,但这是个糟糕的回答。
- @BlueRaja-DannyPflughoeft:阅读我上面的评论。这里我们关心的抽象层(即需要对给定系统进行完美的1:1重构)与您担心编码(即与另一个系统的互操作性)时是不一样的。它们是两个完全不相关的关注点,而前者与编码无关(实际上,这里不能对任何编码方案进行处理)。
- 这不能保持编码的完整性。太糟糕了,这是得到最高票数的公认答案,因为我浪费了2个小时试图找出为什么我的字符串被打乱了。将其追查到使用此答案转换字符串->字节的方法[]。
- @user1151923:你能给我举一个实际字符串被打乱的例子吗?如果你不告诉我如何重现这个问题,我就无法解决这个问题。
- var input ="тхис ис а тест"; var ms = new MemoryStream(GetBytes(input)); var sr = new StreamReader(ms); var output = sr.ReadToEnd();输出为B5AB
- 我想补充的是,我不认为"为了那些目标"是一个(有时)把编码弄乱的答案的正当理由。当人们打开这个问题的时候,他们看到的是这个问题。NET字符串到字节数组c#)和一个高评分的答案,声称您不需要担心编码的粗体文本(顺便说一下,这是缺少"为那些目标"的部分)。下面的答案可能更短,也可能一样长,而且无论您在哪里以及如何使用代码,都能保持编码的完整性。
- 老兄,问题出在你的代码上,而不是我的答案!您正在使用GetBytes将字符串转换为字节,但是您并没有使用GetString来执行相反的操作!这些应该成对使用;你不能想做什么就做什么,然后指望它能奏效。如果您不以一种方式使用编码,您还必须在相反的方向忽略它们,但是您忽略了StreamReader是基于编码的事实!请阅读我之前的评论:stackoverflow.com/questions/472906/…
- @user1151923:在您责怪我没有警告您之前,请意识到您的代码中发生的事情完全等同于使用new StreamReader(stream).ReadToEnd()向一个方向移动,而使用Encoding.UTF8向另一个方向移动。这是错误的,因为作者粗心大意,它与可能告诉您使用UTF8的答案无关。如果StreamReader默认使用UTF-16的事实令人困惑,请不要责怪我的回答;设计成那样不是我的错。
- @Mehrad在这种情况下,仅仅因为你的答案在技术上是正确的,并不意味着它就是一个好的答案,原因在我之前很多人都说过。这就像在有更好的选择时推荐goto语句一样,因为"在这种情况下它是有效的,不是吗?"这个站点的目的是为了在问题的范围内为尽可能多的用例提供正确的答案。你在答案的顶部用大的字体宣布"你不需要编码",而在底部留下一个小的附加说明,这可能会导致问题。
- 其他的选择并不是"更好";实际上,它们更糟,因为它们破坏了不能正确编码的字符串(例如包含未配对代理的字符串)。我已经提过无数次了,但是很明显,人们忽视这个事实是很方便的……
- 即使在一个人们表现得很专业的完美世界里,他们也不会花时间做一点调查。我几乎可以肯定,这个解决方案的所有优点和缺点都在这里的评论和其他答案中得到了解决。如果仍然有人没有意识到这一事实,并且觉得继续就已经解决的问题争论下去(即使是1.25年后),那么你和其他人都不值得再争论下去了。这里有适用于"需要编码"和"不需要编码"用例的答案;就是这么简单。
- +1,但是,str.SelectMany(BitConvertor.GetBytes).ToArray();还不够。(是的,我怀疑BlockCopy更快。)
- 你自己回答的。另外,它需要。net 3.5,这是不必要的。
- 这是我见过的最糟糕的代码之一。我看到人们在。net 4中使用数据表!无论是提问者还是发布这个答案的人似乎都不明白编码的真正含义。当然,您使用的编码与此答案…但是你不知道哪个编码!即使您在同一台机器上转换内容,谁会告诉您用户不会更改编码,从而导致字节不可读?
- @SteffenWinkler:是的,答案确实使用了编码,但关键是它并不关心什么。原因是它保证两种方式使用相同的编码。我不确定您认为用户可以如何更改编码,因为这是. net用来存储字符串的编码。我不相信用户可以改变它。如果运行时被更改,那么您将重新启动程序,因此这两个方法仍然使用相同的编码。
- 这不会为我编译;GetBytes()方法中的第一行失败了,"C:Projectsscs手持 release 6-4-0HHSPrintUtils.cs(7??52): sizeof只能在不安全的上下文中使用(考虑使用system . runtime . interopservices . marshall . sizeof)"
- @B。你用的是。net的旧版本吗?只需用2替换sizeof(char)。
- 是的,比瑞普·凡·温克尔还老。为了使用它,我必须使用XP模式和VS 2003;是的,这让它可以编译。
- 下面我们再来举一个例子:例句这句话的意思是:"这是一个令人吃惊的结果,居然是大家公认的最高票数的答案。是的,以字符串存储在内存中的方式获取它的字节可能很有用。是的,如果在具有不同endianness的机器上调用GetString和GetBytes,则失败可能并不重要。但是说"您不需要担心编码!"是如此可怕的邪恶,因为您鼓励人们忽略每个软件开发人员绝对必须知道的最小值。@artbristol是对的:编码是相关的。
- 是的,我鼓励人们忽略与问题无关的事情。真正的"邪恶"是教人们担心错误的事情。我认为编码与问题无关,因为编码在一个完全不同的抽象层次上。你显然不这么认为,所以请继续投反对票,谢谢分享你的想法。
- 不要误解我的意思,简单是好的。但是警察问了一个非常普遍的问题。他会在相同的架构上转换成字符串还是从字符串转换成字符串?他会将字节写入文件并期望使用特定的文本编辑器查看它吗?他没有说这些。因此,任何遇到这个问题的人可能会读到"您当然不需要担心编码",并认为编码在任何情况下都是无关紧要的,也不需要。因此,如果您说"如果您要在同一台机器上解码字节而不需要特定的编码,请使用这个",这可能是一个很好的答案。
- 您必须理解,您希望我给出答案的方法是完全错误的,因为它不是字符串和字节数组之间的双射——例如,它不能保存未配对的代理。我已经说过很多次了。如果它至少能正常工作,我会考虑的。但它不会——它会在任何不符合Unicode编码的字符串上中断。这就是为什么我如此坚持完全避免编码:它们不仅没有必要,而且明显是错误的,而且不能处理任意字符串。
- 那是一个新的。到目前为止,在您所有的评论中,您甚至一次也没有提出我对问题的理解,现在您指责我是一堵砖墙,不理解字符串在c#中的含义。声明一下,我既不是一个砖墙,也不是一个C程序员,如果您在假装非常了解我之前先看看我在C vs. c#中的徽章,就会发现这一点是相当明显的。
- 这个解决方案根本不正确。fatel错误是当字节。Length是奇数,字符的长度不足以复制,rasie ArgumentException说"偏移量和长度超出了数组或count的界限,大于从索引到源集合末尾的元素的数量"。我们最好使用@bmotmans的答案。
- @tandztc:不,我认为是你没有正确使用它。首先如何得到奇数bytes.Length ?如果您正确地遵循了答案(这意味着您正在使用GetBytes来获得bytes),那么该事件是不可能发生的。如果你用另一种方法得到字节数组,那么你必须用同样的方法把它转换回字符串,而不是使用这个答案。
- @Mehrdad:哦,抱歉。我认识到这两种方法必须成对使用。我误解了这种用法,因为我正在寻找将字节数组转换为字符串的解决方案,所以我留下了注释,因为GetString方法不能处理所有字节数组。对不起,又来打扰你了。
- -1用于错误的声明"您不需要担心编码"。这完全忽略了算法主要将字符串转换为字节缓冲区的事实,因为某些流操作需要它。当这种序列化发生时,编码对我们序列化到文件或连接都很重要。由于编码不匹配的问题,业界每年要浪费1000个工作小时,我们最不需要做的就是重申"我们不需要担心编码……"
- @Mehrdad:从一个长度为偶数的Byte[]开始,然后把它转换成一个字符串,然后再把这个字符串转换回Byte[],您会反对吗?我认为字节转换将允许各种"线性"操作(例如,将转换产生的两个字符串连接起来就相当于将两个数组连接起来),而大多数其他编码则不会。我看到的"直接"转换的唯一缺点是String对象的lexiographic顺序与byte[]对象的lexiographic顺序不同(修复需要字节交换对)。
- 如果你能保证它的长度是均匀的,那就不行,否则你就会丢失长度信息。
- @Mehrdad:也许最好说明一下,您的方法适用于String实例的可预测序列化,这些实例可能包含任意二进制数据,而不是已知的包含有效UTF-16字符串的实例。MS没有包含任何其他"不可变的blob"类型,这实在是太糟糕了,因为String经常被使用,而如果存在其他标准的blob类型,则可能更合适。
- @Mehrdad:还有,您知道有什么好方法可以将字节数组转换为字符串吗?字节成对的msb优先,最好将奇数长度的数组解释为零填充的数组。以这种方式将KeyData从SortKey转换为KeyData生成的字符串上使用String.CompareOrdinal将比SortKey.Compare更快,但是生成这样的字符串有点慢。
- 我认为你对字符串在CLR中的存储方式做了假设。您如何知道它实际上是由一个连续的字节序列表示的?它可以表示为一个链表,或者其他东西。不要假设。它会在发怒时咬你一口。
- @ErikA。Brandstadmoen:两件事:(1)如果不是连续的字节序列,那么你不能获得一个指向数据的指针在常数时间通过fixed (char* p = str) { ... }(2)事实是,这个事实是100%无关紧要,因为ToCharArray总是返回一个字符数组,不管底层数据格式,这是所有我们需要和关心。
- 当然,你是对的,@Mehrdad。我读你的答案太快了。我认为您是将自己映射到字符串本身,当然,如果它确实是由内存中的连续字节数组表示的,那么这当然是可以工作的。但是,如果您调用ToCharArray,字符串存储的实现当然是无关紧要的(除了效率……)。
- 这个答案既危险又错误。
- 这个答案之所以如此可怕,是因为假定OP只是想为某个临时操作"获取字节",然后紧接着就会出现一些评论,反复强调使用编码将通过删除未配对的代理程序来销毁无效的字符串。这就引出了一个问题,为什么首先要用字符串表示或存储数据?字符串被设计用来表示文本,而不是一些中断的或非法的字符序列。(继续…)
- 当然,这对迂回的方法在技术上是正确的,因为它满足OP完全没有指定的用例的一些想象的规范,但是对于OP实际想要完成的工作,肯定有更正确的解决方案。由于我们可能永远都不知道那是什么,这个答案不仅是不正确的,而且作为对这个问题的回答和一般情况下的回答都是有害的。
- @F。巴斯特:字符串是用来表示文本的,而不是一些破碎的或非法的字符序列……你过早下结论了。仅仅因为字符串可能不是有效的UTF-16并不意味着它是"坏的"或"非文本"。这只是意味着不能假设编码是UTF-16,所以答案需要独立于字符串可能使用的编码。它是。如果你不喜欢这个问题,那么很抱歉,但这是这个问题的正确答案。
- @Mehrdad:所以答案需要独立于使用<=编码的字符串,这是将表示与抽象合并在一起。字符串,作为一个字符串,已经独立于实现背后使用的编码。将字符串"Hello world"转录到某个字节序列的行为本身就是利用编码的定义。唯一的办法就是堵住耳朵,大喊"啦啦啦!",将内存块重新解释为字节是在隐藏碰巧使用的编码。
- 我编辑了这个答案,去掉了对其正确性的重复防御。我还将在回答开始时一并解释为什么这是正确的。我也稍微改变了一下重点。我认为这对解决这个问题的争论大有帮助。
- Aardvark,你的编辑还不错,但是我并没有真正明白它的意思(我注意到有一点语法/大写的错误),所以我把它回滚了……我觉得原版已经足够好了,这就是我想表达的意思,我宁愿它不被编辑。我认为这个讨论确实有好处,应该继续下去,因为(1)它帮助读者认识到这个答案在共享代码库中可能存在争议,(2)它让我能够强调为什么我认为这个答案是正确的方法。不管怎样,讨论已经结束了,所以不用担心。
- 这正是我要找的。我需要的东西可以发送和接收事件的观察者模式技术演示,仅仅使用一个简单的控制台应用程序,和事件消息被发送和接收的字节数组,所以我想一个更好的方法来显示功能是消息普通老字符串。这对大多数东西都没有太大帮助,但这正是我所需要的!非常感谢:)
- 对于我的具体问题最好的回答,谢谢!用于跟踪编码之间的转换故障,用于诊断,在同一台机器上,同一应用程序,没有网络连接。仅仅因为我们中的大多数人担心有人会使用它来序列化数据,并在平台/数据库之间使用它们,这并不是激发这个答案的有效理由。专门使用这种方法来避免灾难性的编码结果。这就是为什么我这么喜欢:你可以在这里得到非常具体和不寻常的任务的答案。对于初学者关于安全字符串字节转换,请重新阅读MSDN。
- 我使用这个解决方案将密码字符串转换为byte[],然后再对其进行盐析和散列。在这个用例中,我完全不关心编码。我甚至不需要将得到的散列转换回字符串—对于密码验证,我只是直接比较得到的散列byte[]。对于这个特殊的用例,非常优雅和低开销的解决方案。不过,《火焰之战》读起来很有趣。
- 我可以看到这段代码在这样一个简单的情况下崩溃:sizeof(char) == 2 byteArray.Length == 9然后,(byteArray.Length / sizeof(char)) == 4,对BlockCopy的调用抛出异常,因为您超出了界限。我宁愿使用更多的空间,并使用来自System.Convert类的Base64编码的简单解决方案。
- 在这里使用GetString方法时,byte[]的长度究竟怎么会是奇数呢?还要记住,这只是示例代码。在我之前的评论(密码散列)中描述的并不罕见的用例中,甚至不需要将byte[]转换回string。
- 我很高兴你的代码崩溃了,因为它试图告诉你你用错了。不要试图绕过它,要意识到这个答案只是为了解决一个特定的问题,这个问题与你的不同,因此你不应该使用它。
- 当字符串没有存储为UTF-16或任何固定长度编码时,这个答案是错误的。这意味着编码很重要,即使它没有出现在代码中。因为对于UTF-8,您将引入空的"字节"。这还假定字符串的存储和GetBytes将返回相同的编码——如果不是,那么您就不会返回"字符串的字节"。幸运的是,OP只需要字节,这正是这个答案所提供的。
- 只使用Encoding.Unicode.GetBytes ()。这个答案中的函数比Unicode.GetBytes()慢2倍。在释放和放大器中测试;x64的环境。
- 如果您不知道为什么编码很重要,那么最好不要使用IBM的EBCDIC,它的字符与标准ASCII不匹配。
- 关于那个无聊的评论。唯一运行. net而不是little-endian的平台是Xbox360, XNA track(在Xbox360上运行. net软件的主要方法)已经停止。mono有一些变体确实在大端平台上运行,但这只是一个例外,而不是规则。
- @JohnLeidegren不正确!微软正在把。net框架移植到Linux上,Linux运行在一些大端架构上。请看这里的例子。
- @camerondm9我并不怀疑这些平台的存在,但是您必须考虑CoreCLR除了X64汇编程序(little-endian)之外没有JIT。据我所知,微软目前还没有添加对任何其他体系结构的支持,当然也不是IBM PowerPC,因为它没有市场。我不是说这不会发生我只是说这不会很快发生。不管我到目前为止所说的一切,您仍然必须问问您自己,在不久的将来,您的代码是否可能运行在一个big-endian架构上?
- 微软为ARM提供了一个JIT引擎,而ARM是双端(实现定义)的。这可能不太可能,但是如果你的代码可以在移动设备上运行(或者它是一个库),你永远不会知道……
- @NumLock:自我解释。sizeof(char)不是1,而是2。这是c#,不是C。
- 这个答案是错误的,因为不进行编码就不可能将符号序列映射到字节序列。然而,同样真实的是,这个例子没有直接使用编码对象。这是因为它正在秘密地断言字符串的规范编码方案(我相信Unicode 16)对所有解码实现都是正确的。这对于。net是正确的,但对于其他语言或运行时则不正确。重要的是,用户知道他们在这里所做的是导出(已编码的)内部表示,而不是真正避免编码。
- 不要试图以某种方式解释字节<<只是查看字节是一种解释形式
- 这段代码将执行预期的操作,但除此之外,理论参数基本上都是垃圾,尽管在实践中并不重要(可以观察到)。不要忘记,语言和编译器也是抽象的(很少对物理内存做出严格的保证)。char-array是内部表示的语句已经达到,引用指针代码作为证明。可以将字符串观察为一个字符数组,并且可以像您所说的那样观察操作字符指针,但是可以将其实现为另一种物理表示的语法糖。
- @chris"低开销"这比编码更多,也更慢。"…对于密码验证,我只是直接比较得到的散列"如果您为PC和Xbox360编译此代码以使用相同的密码验证,那么这将失败,因为相同密码的散列是不同的
- @BlueRaja-DannyPflughoeft ToCharArray()不依赖于编码,它在.Net源代码中只是字符串内部字节的内部表示形式的副本,因此使用ToCharArray()获得char数组与修复指向string私有成员m_firstChar的指针具有相同的效果
- @yoelhalb:如果不依赖特定的编码(字面意思是定义),就不能将字符串转换为字节数组。在本例中,您使用的编码由"内部表示[..[英语背诵文选
- 是的。当然有。不仅因为Danny说了什么,还因为API doc特别指出:"将这个实例中的字符复制到Unicode字符数组中。"内部表示恰好是Unicode (UTF-16),但这是一个无关的实现细节。
- @BlueRaja-DannyPflughoeft:我突然意识到你(和这里的很多其他人)有语法问题。注意,我写了"您不需要担心编码",而yoel说它"不依赖于编码"。在"编码"之前没有任何文章!约尔并没有说它不依赖于"编码"。我们只是说您不必担心编码任何东西来提取字节。你似乎认为我们在声明这个字符串不具有编码,这显然是疯了,而不是我们所说的。我们只是说编码(作为动词)不应该发生在这里。
- 不,没有混乱。您的答案正确地说明,如果您不打算解释字符串,就不需要担心编码,但是在0种情况下,这可能是有用的。甚至您自己的建议("重构字符串")也依赖于字符串的内部编码不变。同时,初学者看到这个答案,错误地认为他们不必担心编码是什么。这个答案比错误更糟糕,因为它在技术上是正确的,但极其有害。
- @BlueRaja-DannyPflughoeft:"这里正好有0个案例,这可能是有用的。"我已经解释过,即使字符串不是有效的UTF-16,它也可以工作,所以在这种情况下,它对人们很有用。如果你个人觉得它没有用,你就不用它。
- @Mehrdad我一直在争论这个问题,我能看到双方的优点。但是在最后,我想知道为什么需要转换一个带有无效字符的字符串,假设这是使用此解决方案的唯一原因。难道我们不应该拼命避免将字节转换为技术上无效的字符串吗?难道不应该强制这样的数据保留为字节数组,以避免给人留下存在有效字符数据的印象吗?
- @BlueMonkMN:我认为你的错误是,这不是一个把字节转换成字符串再转换回字节的方法。它是一种将字符串转换为字节并返回字符串的方法。这里有一个非常重要的区别。如果您要问为什么用户甚至有一个带有无效字符的字符串,或者为什么string允许这样做,那么这是一个完全不同的问题,我不能也不会在这里回答这个问题。我只是想提供一个不依赖于字符串编码的答案(如果有的话)。
- @Mehrdad这就是我的观点:我不知道,如果不将字符串从字节数组转换为字符串,您怎么会得到一个包含无效字符的字符串。我能想到的所有其他生成字符串的"适当"方法都不允许这样做,因为它将经过编码或由不能返回无效字符的进程生成。所以我的期望是,人们总是可以假设. net字符串只包含有效的字符,除非它们使用您提供的代码。
- @BlueMonkMN:"这就是我的观点:我不知道,如果不将字符串从字节数组转换为字符串,您如何能够得到包含无效字符的字符串。"…这里有一个:"\uD800" +"\uDC00"这两个字符串都是无效的,但是它们的连接是有效的。也许您想将每个字节转换为字节,传输它们,然后将它们转换回来,然后连接起来。也许它们是通过类似地分割一个有效的字符串生成的。有成千上万种方法可以使用无效字符串…
- OP没有说明为什么他想"简单地获取字节",但是我猜他假设System.Text.Encoding.Unicode.GetBytes(); 正在执行某种他想要避免的昂贵的转换。遗憾的是,由于使用了双重副本,您在这里提出的方法效率较低。同样,Endianness也很重要。OP希望加密字符串。他这么做很可能不是为了将加密的stryng保存在内存中。它将被写入磁盘或通过网络传输。如果在一台具有不同endianness的机器上解密,现在解密还是将来解密呢?
- @KrisVandermotten:"如果它被解密在一台不同endianness的机器上,现在还是将来?"……叹息。这个答案有多少?你在发表评论前读过后续讨论吗?从字面上看,第2条投票最多的评论——也就是在你扩展评论之前的第一条评论——和你刚才说的一样,是关于endianness的,而从字面上看,第5条投票最多的评论——也就是在扩展之前的第二条评论——是我对它的回复……他们来自5年前!!
- @Mehrdad,我的第二个观点是,你说的"如果你想在相同的系统上使用它,在相同的功能集上使用它,那么这个观点就是没有意义的。"加密只有在执行IO时才有用,即将加密的流写到其他地方,以便在不同的地方或时间读取。你没有提到这一点。更重要的是,我的第一个观点胜过了这一点:为什么会有人想要使用您的函数?它的效率比内置的低吗?
- 最后,如果用例不同,并且OP确实希望获得字节,那么不安全并将char*转换为byte*将是最直接的答案,而不是复制字符串两次。
- @KrisVandermotten:(一)这是一个东西,(b)不安全代码需要额外的运行时你可能没有特权,(c)如果有人写或者使用不安全代码错误的它会静静地腐败的记忆而不是崩溃,(d)没有,我说这是最快的回答,(e)没OP声称他希望最快的回答,(f)已经有人使用不安全代码公布答案所以去upvote跟我争论,(g)我只是回答问题;如果你不喜欢OP的用例,去和他争论。
- 嗨@Mehrdad,我有两个问题要问你。在你的前两段,你使用术语"解释"和"重构"。你说的"解释"是什么意思?你能举个解释的例子吗?"reconstruct"对我来说是有意义的(bytesto -read string),但是您在重新构造时不需要使用相同的编码吗?我理解在同一个系统上这不应该是一个问题,但它是不同系统之间的潜在问题吗?非常感谢你的回答!
- @emery。诺尔:我的意思是,你在意字节数吗?如果字节不是有效的UTF-16(或UTF-8,或您的代码知道的任何其他内容),您的代码中是否会有任何内容中断?如果是,那么你就是在解释它们。对于不同的系统,是的,这是一个潜在的问题,而这个答案并不适用于那个场景。
- 我已经改变主意了。一些我在原始视图中没有看到的东西——字符在c#中是固定大小的——这实际上只是一个数组拷贝。创建数组可能需要解释;将数组加载回字符串也可以。但是数组本身被重新创建而不需要解释,因为字符的大小是相同的,这允许重新创建原始的字符数组。这取决于。
- 您是否意识到length * sizeof(char)不会以字节为单位给出文本的大小?在UTF-8这样的编码中,字符的大小可以变化。对于UTF-8,它可以是1字节到4字节之间的任何值。
- @chris的评论:"我使用这个解决方案将密码字符串转换为byte[],然后再进行盐化和哈希。在这个用例中,我完全不关心编码"应该最终说服您删除这个答案。显然,如果有人真的相信这一点,足以为其辩护,那么这一点就不够清楚,不足以发挥作用。
- @John Rasch我还是不明白这有什么错。. net字符串总是有相同的固定长度编码(比如UTF-16)。因此,可以安全地假设两个具有相同char序列的. net字符串在内部表示为相同的字节序列。
- 我收回了上面评论中"固定长度"的部分,这显然是不正确的。尽管如此,我还是不明白为什么任何两个相等的。net字符串应该在内存中表示为不同的字节序列。
- @chris: string.Equals("\u0041\u030A","\u00C5", StringComparison.InvariantCulture)是一个例子,但它与我的答案毫无关系,因为如果指定编码,就会出现完全相同的问题。
- 原始数据存储在字符串中的事实已经暗示了编码。它不仅仅是一个字节数组,可以随意摆弄。如果是这样,为什么要将它存储在字符串中?这是……只是愚蠢。这里的断言是人们错误地"解释"了字节,这完全是不正确的,因为原始数据存储在.net字符串中已经解释了字节。结果字节的使用者必须隐式地知道编码是什么,才能使用原始字节。
- 这个答案错得太离谱了,竟然有这么多人投赞成票,真让我震惊。是的,理论上是可行的。但这正是这段代码的可能用例的结尾。任何在生产中使用此代码的人都应该被当场解雇。"字符串是否包含无效字符并不重要"的参数是BS,因为字符串从一开始就不会包含无效字符。
- 这是一种编码。您刚刚发明了自己的编码,而不是使用标准编码。
- 当然,您粗体的超大标题应该是"当平台为您处理编码时,您不需要担心编码"。
- OP声明的目的是加密字符串。我猜如果一个字符串被加密,那么它也会在某个时候被传输。使用这种方法,如果一个系统使用UTF-8,另一个系统使用UTF-16,会发生什么?这会不会抛出sizeof(char)并完全破坏字符串?
- @DrewB:是的,因为那样你就写了错误代码。这段代码很好地完成了它的工作,但仅此而已。
这取决于字符串的编码(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)中的字符编码。
- 但是,为什么要考虑编码呢?为什么我不能简单地获取字节而不需要查看使用了什么编码?即使它是必需的,难道String对象本身不应该知道正在使用什么编码并简单地转储内存中的内容吗?
- . net字符串总是编码为Unicode。所以使用System.Text.Encoding.Unicode.GetBytes ();获取. net用来表示字符的一组字节。但是你为什么想要那样呢?我推荐UTF-8,尤其是当大多数字符都在西方拉丁字符集中的时候。
- 还有System.Text.Encoding.Default
- 另外:如果检索字符串的系统不处理该编码或将其作为错误编码处理,那么字符串内部使用的确切字节并不重要。如果都在。net中,为什么要转换成字节数组呢?否则,最好显式地编码
- @Joel,小心System.Text.Encoding。默认值,因为它在运行的每台机器上可能是不同的。这就是为什么建议总是指定一个编码,比如UTF-8。
- 您不需要编码,除非您(或其他人)真正打算解释数据,而不是将其视为一般的"字节块"。对于压缩、加密等,担心编码是没有意义的。查看我的答案,找到一种不用担心编码的方法。(如果你说你不需要担心编码的话,我可能会给你一个-1,但我今天并不是特别刻薄。:P)
- 很好的讨论,有时候我需要上面的一个选项。而且看起来还像:"一个傻瓜能问的问题比七个聪明人能回答的问题还要多。"
- + 1;@Mehrdad: GetString方法是对GetBytes方法输出的解释。这就是为什么您必须担心在两个方法中使用相同的编码。
- 我认为很重要的一点是,它并不"依赖于您的字符串编码",. net对您隐藏了这一点。据我所知,一个字符串由一个系统序列表示。Chars,表示为UTF-16。重要的是,您必须将字节存储在某种编码中,并知道如何用相同的编码检索它们。不这样做等同于密码保护您的文件,并尝试使用不同的密码来解除保护。
公认的答案非常非常复杂。为此使用包含的.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); |
如果没有必要,就不要重新发明轮子。
- 公认的答案不仅非常复杂,而且会导致灾难。
- 如果所接受的答案发生了更改,出于记录的目的,它是Mehrdad当前时间和日期的答案。希望OP会重新考虑这个问题,并接受一个更好的解决方案。
- 原则上很好,但是,编码应该是System.Text.Encoding.Unicode,以等效于Mehrdad的答案。
- 从最初的答案到现在,这个问题已经被编辑了无数次,所以,也许我的答案有点过时了。我从来没有打算给一个exace相当于Mehrdad的答案,但给出了一个明智的做法。但是,你可能是对的。但是,在最初的问题中"获取字符串存储在哪些字节中"这个短语非常不精确。存储在哪里?在内存中?在磁盘上?如果在内存中,System.Text.Encoding.Unicode.GetBytes可能更精确。
- 在检查了所有答案、许多注释和我的内存检查(不要忘记,Visual Studio允许内存检查)之后,正确的答案是Encoding.Default.GetBytes。
- @ amissigo,您的建议是有bug的,除非您确定您的字符串与您的系统默认编码兼容(在您的系统默认遗留字符集中只包含ASCII字符)。但OP并没有说明这一点。
- @Fr& # 233; d # 233;里克;在查看了所有信息并运行Unicode字符测试场景之后,我只是陈述了我的观点。我还使用了TextPad、HexEdit、WinHex和Visual Studio来查看这些字节。Encoding.Default.GetBytes的结果与那些应用程序相同。我没有提供OP问题的答案。
- 在不同的系统上,这个程序会给出不同的结果。这从来都不是一件好事。即使它是用来做散列什么的(我假设这就是OP对"encrypt"的含义),相同的字符串也应该始终提供相同的散列。
- 为utf - 8 + 1。这就是那些认为编码无关紧要的人所假设的。UTF-8是一个严格的值,用于无符号字符(字节)的值编码。其他都是……不是的。
- @jinzai,但是UTF-16呢?
- UTF-16是我提到的"其他所有东西"的一部分。最初的问题是指"字节表示"。关于UTF-16——ASCII的值映射相同,但是——它们是单词,而不是字节。我非常确定,每个人都知道. net在内部使用UTF-16,但是——我总是使用UTF-8来处理XML. net之类的东西,现在至少考虑到了这一点。
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()); |
- 您可以对所有这些操作使用相同的BinaryFormatter实例
- 很有趣。显然,它将删除任何高代理Unicode字符。参见[BinaryFormatter]上的文档
- @ErikA。请查看我的测试:stackoverflow.com/a/10384024
您需要考虑编码,因为一个字符可以由一个或多个字节表示(最多6个字节),不同的编码将以不同的方式对待这些字节。
Joel发表了一篇文章:
The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)
- "一个字符可以用一个或多个字节表示"我同意。我只想要那些字节,不管字符串是用什么编码的。字符串在内存中存储的唯一方式是字节。甚至字符也存储为一个或多个字节。我只是想要得到他们的字节。
- 您不需要编码,除非您(或其他人)真正打算解释数据,而不是将其视为一般的"字节块"。对于压缩、加密等,担心编码是没有意义的。查看我的答案,找到一种不用担心编码的方法。
- @Mehrdad——完全,但最初的问题,如上所述,当我开始回答,不注意OP会发生与字节后他们会转换,并为未来的搜索相关的信息,这是由乔尔的回答很好地——当你在你的回答是:只要你坚持在。net世界,和用你的方法转换为/,你快乐。只要您走出这一步,编码就很重要。
- 一个代码点最多可以用4个字节表示。(一个UTF-32代码单元,一个UTF-16代理对,或4个字节的UTF-8)。UTF-8需要超过4个字节的值位于0x0之外。Unicode的0x10FFFF范围。:-)
这是一个很普遍的问题。理解作者所问的问题是很重要的,而且它与最常见的需求是不同的。为了防止在不需要的地方滥用代码,我先回答了后面的问题。
共同需要
每个字符串都有一个字符集和编码。当您将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。但问题的作者并不关心这个。
- 通常,将byteCount设置为字符串长度的两倍是不正确的。对于基本多语言平面之外的Unicode代码点,每个字符将有两个16位代码单元。
- @Jan这是正确的,但是字符串长度已经给出了代码单元的数量(而不是代码点)。
- 谢谢你指出来!来自MSDN:"Length属性[of String]返回此实例中Char对象的数量,而不是Unicode字符的数量。"因此,编写的示例代码是正确的。
- 我不认为Char是真正的"特定于编码"类型;据我所知,有一个指定的1:1 Char的值之间的关系和UInt16值,任何Char[]可以被转换成一个字符串的长度,和任何这样的字符串可能转化为Char[]等于原始,Char值的序列是否形成一个有效的utf - 16字符串。
- char类型表示UTF-16代码单元,string类型表示UTF-16代码单元的序列。-_C # 5规范。_although, yes, there is nothing that prevent a invalid Unicode string: new String(new []{'\uD800', '\u0030'})
- @TomBlodget:我找不到任何表示所有值0x0000-0xFFFF都可以被视为"代码单元"的东西,但是术语"代码单元序列"意味着该类型可以容纳不代表代码点序列的代码单元序列。除了String,我真的不知道还有什么类型能更好地封装"16位值的不可变序列"的概念;因为System.String有特殊的运行时支持,这是任何其他类型都无法提供的,所以它可以为许多操作提供比任何其他类型更好的性能。
- @TomBlodget:有趣的是,如果一个人需要实例Globalization.SortKey, KeyData提取物,和包生成的字节从每个String(两个字节每字符(MSB第一),调用String.CompareOrdinal在生成的字符串将大幅高于叫SortKey.Compare SortKey实例,甚至那些实例上调用memcmp。鉴于此,我想知道为什么KeyData返回一个Byte[]而不是一个String?
- @TomBlodget +1很棒的答案!为了完整起见,最好添加如何反向返回。这对我很有效:unsafe string GetString(byte[] bytes) { fixed (byte* bptr = bytes) { char* cptr = (char*)(bptr); var result = new string(cptr, 0, bytes.Length / 2); return result; } }
- 唉,正确的答案,但多年来太迟了,将永远不会有那么多的选票被接受。由于TL;博士的存在,人们会认为公认的答案是肯定的。抄写下来,投赞成票。
- 喜欢这个答案是因为这种方法,但它是错误的——代理对将是单个代码单元,但将是4个字节。所以codeunitcount * 2是不正确的。
- @GerardONeill感谢您的反馈。根据c#规范,. net字符串是由UTF-16代码单元组成的序列。代码点编码在一个或多个代码单元中。在UTF-16的情况下,这是一个或两个。当两个代理时,它们是"高"代理,然后是"低"代理。因此,codeunitcount * 2是代码单元的正确字节数。代码根本不计算代码点。
- 对不起,我不知道"代码单元"的语义。没有意识到弦的恐怖。长度与代理人;很明显,长度将计算完整的字符(代码点)。所以,是的,你在这里所做的会起作用。这也解释了为什么和如何在字符串中允许不匹配的代理。
- @GerardONeill是的,恐惧。我一直假设字符串必须使用有效的Unicode(包括匹配的代理),但遗憾的是,没有人说它必须为真。
- @TomBlodget:你不需要fixed或unsafe代码,你也可以做var gch = GCHandle.Alloc("foo", GCHandleType.Pinned); var arr = new byte[sizeof(char) * ((string)gch.Target).Length]; Marshal.Copy(gch.AddrOfPinnedObject(), arr, 0, arr.Length); gch.Free();
- @Mehrdad是的,这也是一个很好的答案,它满足了问问题者相当有限的非功能性约束。我认为固定和固定是一回事但它确实消除了不安全的需要。
为了证明Mehrdrad的合理答案是有效的,他的方法甚至可以保留未配对的代理字符(许多人对我的答案提出了反对意见,但每个人都犯了同样的错误,例如System.Text.Encoding.UTF8.GetBytes、System.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ツ
谢谢!
- 代理程序不是必须成对出现才能形成有效的代码点吗?如果是这种情况,我可以理解为什么数据会被破坏。
- @dtanders是的,这也是我的想法,它们必须成对出现,如果你故意将未配对的代理字符放在字符串上,并使它们成对,就会出现未配对的代理字符。我不知道的是为什么其他开发人员不断强调我们应该使用编码感知的方法,因为他们认为序列化方法(我的答案,3年多来一直被接受的答案)并不能保持未配对代理字符的完整性。但他们忘了检查encoding-aware解决方案并不保持未配对的代理角色,具有讽刺意味的ツ
- 如果有一个在内部使用System.Buffer.BlockCopy的序列化库,那么所有支持编码的人的论点都是没有意义的
- 测试的问题是,您创建了一个无效的字符串。"在UTF-16中,它们必须总是成对出现,作为一个高代理,后面跟着一个低代理,因此使用32位表示一个代码点。"如果在/uD800后面加上/uDC00,那么它在所有unicode格式中都可以正常工作。需要注意的是,这是一个字符串,而不是char数组,所以某些限制是有意义的。而且,即使在UTF7中没有/uDC00,它也可以正常工作。
- @MichaelBuen在我看来,最主要的问题是,你用粗体字写了一些无关紧要的事情,而不是说在他们的情况下无关紧要。因此,您鼓励那些查看您的答案的人犯一些基本的编程错误,这将在将来导致其他人的失望。未配对的代理在字符串中无效。它不是一个char数组,因此将字符串转换成另一种格式将导致该字符上的错误FFFD是有意义的。如果您想手动操作字符串,请按照建议使用char[]。
- @Trisped:如果希望将字节数组转换为允许快速字典比较的形式(比较的排名是第一个不匹配的字节的排名),如果没有"不安全"的代码,比String.CompareOrdinal更快的东西还可用吗?将具有不匹配代理的Char[]数组转换为String以便在其上使用String.CompareOrdinal是很麻烦的,但是哪种方法更好呢?
- @dtanders: System.String是Char的不可变序列;. net始终允许从任何Char[]构造String对象,并将其内容导出到包含相同值的Char[],即使原始的Char[]包含未配对的代理。
- 医生说Char应该是UTF-16,所以无法匹配的代理在Char中也是非法的。两年后,当我再次阅读这篇文章时,我在想应该有什么东西抛出一个错误,而不是将一个非法的字节序列错误地输入到一个字符中,但无论如何。
- @dtanders:不,拥有不匹配的代理是完全合法的,但是您的结论是不合法的,因为Unicode术语让您感到困惑。没有a(n)这种东西"(在)有效utf - 16 char"。如果您阅读c#语言规范,它说"char类型表示UTF-16代码单元,字符串类型表示UTF-16代码单元的序列"。注意,它并没有说string必须是格式良好的"Unicode字符串"(并且注意,甚至"Unicode字符串"在术语表中也被显式地允许格式不正确)。
- @MichaelBuen:将上面提到的^编辑成您的答案可能不是一个坏主意,这样人们就会意识到,格式不正确的Unicode字符串实际上是完全合法的"Unicode字符串"(以及完全合法的string)。
您的问题的第一部分(如何获取字节)已经由其他人回答:查看System.Text.Encoding名称空间。
我将回答您接下来的问题:为什么需要选择编码?为什么不能从string类本身获取呢?
答案分两部分。
首先,string类在内部使用的字节并不重要,无论什么时候,只要您假定使用了这些字节,就很可能引入了一个bug。
如果您的程序完全在. net世界中,那么您根本不需要担心为字符串获取字节数组,即使您正在通过网络发送数据。相反,使用. net序列化来担心数据的传输。您不再需要担心实际的字节:序列化格式化程序将为您完成。
另一方面,如果您将这些字节发送到某个无法保证将从.Net序列化流中拉入数据的地方,会发生什么情况?在这种情况下,您肯定需要担心编码,因为显然这个外部系统关心编码。同样,字符串使用的内部字节并不重要:您需要选择一种编码,这样您就可以在接收端显式地说明这种编码,即使它与. net内部使用的编码相同。
我理解,在这种情况下,您可能更愿意在可能的情况下使用字符串变量在内存中存储的实际字节,这样可以节省创建字节流的一些工作。但是,我告诉您,与确保您的输出在另一端被理解以及确保您的编码必须显式相比,这并不重要。此外,如果您真的想匹配您的内部字节,您可以选择Unicode编码,并获得性能上的节省。
这就引出了第二部分……选择Unicode编码是在告诉. net使用底层字节。您确实需要选择这种编码,因为当一些新的Unicode-Plus出现时,. net运行时需要在不破坏程序的情况下自由地使用这种更新、更好的编码模型。但是,目前(以及可预见的将来),只要选择Unicode编码就可以得到您想要的结果。
理解您的字符串必须重新编写为wire也很重要,这至少涉及到位模式的一些转换,即使使用匹配编码也是如此。计算机需要考虑诸如大端与小端、网络字节顺序、分组、会话信息等因素。
- 在。net中,你确实需要为字符串获取字节数组。许多. net密码学类都包含ComputeHash()等方法,这些方法接受字节数组或流。您别无选择,只能首先将字符串转换为字节数组(选择编码),然后选择性地将其封装到流中。然而,只要你选择一个编码(即UTF8)坚持它没有问题。
试试这个,代码更少:
1
| 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:你为什么觉得你的例子很奇怪?当然,在变宽编码中,并非所有字符都具有相同的字节长度。有什么问题吗?
嗯,我已经阅读了所有的答案,它们都是关于使用编码的,或者是关于删除未配对代理的序列化的。
例如,当字符串来自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 & ; 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),我需要使用包含二进制数据的String做一些事情(这是MSSQL返回给我的方法)。所以上面的函数是针对String (binary data) <-> byte[] (easy accessible binary data)的。
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和字符集的绝对最小值(没有借口!)
- 请允许我澄清:"hello world"已被用于将"hello world"转换为物理字节。由于字符串存储在我的计算机上,我确信它必须以字节的形式存储。我只是想访问这些字节,以便将它们保存在磁盘上或出于其他原因。我不想解释这些字节。由于我不想解释这些字节,此时对编码的需要就像要求电话线调用printf一样是错误的。
- 但是,除非使用编码,否则不存在文本到物理字节的翻译概念。当然,编译器以某种方式将字符串存储在内存中——但它只是使用了一种内部编码,而您(或除编译器开发人员之外的任何人)并不知道这种编码。因此,无论您做什么,您都需要一个编码来从字符串获取物理字节。
- @Agnel Kurian:字符串在某个地方有一堆字节存储其内容(UTF-16 afair),这当然是正确的。但是有一个很好的理由阻止您访问它:字符串是不可变的,如果您可以获得内部byte[]数组,那么您也可以修改它。这打破了不可变性,这是至关重要的,因为多个字符串可能共享相同的数据。使用UTF-16编码来获取字符串可能只会将数据复制出来。
- @Gnafoo,字节的副本就可以了。
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); |
- VUPthis解决了我的问题(byte[] ff = ascii . ascii . getbytes (barcodetx . text);)
1 2 3 4 5
| byte[] strToByteArray (string str )
{
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
return enc .GetBytes(str );
} |
- 但是,为什么要考虑编码呢?为什么我不能简单地获取字节而不需要查看使用了什么编码?即使它是必需的,难道String对象本身不应该知道正在使用什么编码并简单地转储内存中的内容吗?
- 这并不总是奏效。一些特殊的字符可能会迷失在使用这种方法,我已经找到了困难的方法。
- 如果字符集是utf,它就不会工作!
我不确定,但我认为字符串将其信息存储为字符数组,这对字节效率很低。具体来说,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是一种流行的编码方式,它紧凑且无损耗。
- UTF-8只有在大多数字符都是英文(ASCII)字符集的情况下才会紧凑。如果有一长串中文字符,UTF-16的编码将比UTF-8更紧凑。这是因为UTF-8使用一个字节编码ASCII,否则使用3(或者4)。
- 真实的。但是,如果您熟悉处理中文文本,您怎么会不知道编码呢?
随着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 = & ;bytes .AsSpan().NonPortableCast<byte, char>().DangerousGetPinnableReference())
{
s = new string(f );
}
} |
NonPortableCast和DangerousGetPinnableReference这两个名称应该进一步说明,您可能不应该这样做。
注意,使用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) |
- ASCIIEncoding .....是不需要的。只需要使用encode . 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#的字符类型)。但是,这种表示形式是什么并不重要,从技术上讲,这就是选项的答案。为什么不"编码"呢?
- 这些字符不支持UTF-8或UTF-16,甚至exapmle: ?? &(Char) 55906,(Char) 55655。因此,您可能是错的,Mehrdad的答案是一个安全的转换,而不考虑使用什么类型的编码。
- Raymon,字符已经由一些unicode值表示——所有unicode值都可以由所有utf表示。对你所说的有更详细的解释吗?这两个值(或3..)存在于什么字符编码中?
- 它们是不受任何编码范围支持的无效字符。这并不意味着它们是100%无用的。无论编码是什么,将任何类型的字符串转换为等效的字节数组的代码都不是错误的解决方案,并且在需要的场合有自己的用法。
- 好的,那么我认为你没有理解这个问题。我们知道它是一个unicode兼容的数组——事实上,因为它是。net,所以我们知道它是UTF-16。所以这些字符不存在。您也没有完全阅读我关于内部表示更改的评论。字符串是对象,而不是经过编码的字节数组。所以我不同意你最后的观点。您希望代码将所有unicode字符串转换为任何UTF编码。这就是你想要的,没错。
- 对象是描述对象当前状态的原始位序列的数据序列。因此,编程语言中的每个数据都可以转换为字节数组(每个字节定义8位),因为您可能需要在内存中保存任何对象的某些状态。您可以在文件或内存中保存并保存一个字节序列,并将其转换为整数、bigint、image、Ascii字符串、UTF-8字符串、加密字符串或您自己定义的数据类型。所以你不能说对象不同于字节序列。
- Mojtaba——我在键盘上更新了我的答案,变得更聪明了。但是,您所说的并不适用于具有其他对象依赖项的对象。但是Mehrdad的解决方案,通过将其转换为char数组,消除了这种情况,使您所说的成为可能。还在考虑是否要替换我的全部回复…但也许我的学习过程会有一些价值。
以下是我将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 & ; 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次迭代:
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
}
} |
- 在。net的Itanium版本上,RuntimeHelpers.OffsetToStringData的值是8的倍数吗?因为否则,由于未对齐读取,此操作将失败。
- 调用memcpy不是更简单吗?stackoverflow.com/a/27124232/659190
如果您真的想要字符串底层字节的副本,可以使用如下函数。然而,你不应该继续往下读来找出原因。
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); |
- …并且在跳跃范围大于127时失去所有字符。用我的母语写"árvízt?r?"t # 252; k # 246;射频和# 250;" # 243;舌鳎# 233;p。"。System.Text.ASCIIEncoding.Default.GetBytes("Árvízt?r? tükörfúrógép.").ToString();将返回"Árvizturo tukörfurogép."丢失的无法检索的信息。(我还没有提到亚洲语言,在那里你会失去所有的字符。)
两种方式:
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 ;
} |
我更倾向于使用底部的,而不是顶部的,没有以速度为基准。
- 那么多字节字符呢?
- tobyte()是私有的:S
- @AgnelKurian Msdn说:"这个方法返回一个无符号字节值,表示传递给它的Char对象的数字代码。在. net框架中,Char对象是一个16位的值。这意味着该方法适用于返回ASCII字符范围内或Unicode C0控件和基本拉丁语中的字符的数字代码,以及C1控件和Latin-1补充范围内的字符的数字代码,范围从U+0000到U+00FF。"
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(); |
- 它几乎没有更快,更不用说最快了。这当然是一个有趣的选择,但它本质上与Encoding.Default.GetBytes(s)相同,顺便说一下,后者要快得多。快速测试表明,Encoding.Default.GetBytes(s)的性能至少快79%。YMMV。
- 试着配上€。这段代码不会崩溃,但是会返回一个错误的结果(这更糟)。尝试将类型转换为short而不是byte,看看有什么不同。
由于以下事实,字符串可以用几种不同的方式转换为字节数组:. 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中通常不需要处理字符编码。
但是,一般来说,要从字节流将字符串加载到系统中,您需要知道源编码,以便正确地解释和随后翻译它(否则这些代码将被视为已经在系统的默认编码中,因此呈现的是胡言乱语)。类似地,当将字符串写入外部源时,它将以特定的编码方式写入。
- Unicode不是一种编码。Unicode是字符到码点的抽象映射。Unicode编码有多种方法;特别是,UTF-8和UTF-16是最常见的。. net使用UTF-16,尽管我不确定它是UTF-16 LE还是UTF-16 BE。
从byte[]到string:
1
| return BitConverter.ToString(bytes); |
我写了一个Visual Basic扩展,类似于已接受的答案,但直接使用.NET内存和编组进行转换,它支持其他方法不支持的字符范围,比如UnicodeEncoding.UTF8.GetString或UnicodeEncoding.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); |
我希望这能有所帮助。
- 那不是解决办法!
- 你为什么这么说?
- 在编辑之前,它是:s.Select(e => (byte)e)这只适用于ASCII字符。但是char类型用于存储UTF16单元。现在,在您编辑之后,代码至少是正确的,但是它会随着环境的不同而变化,因此它实际上是无用的。IMHO编码。默认值应该只用于与遗留Windows"Ansi代码页"代码交互。
- 好点。您如何看待byte[] b = new System.Text.UTF32Encoding().GetBytes(s);吗?
- 使用byte[] b = System.Text.UTF32Encoding.GetBytes(s);, UTF8同样可以。
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)由于使用ASCII编码,将会丢失数据。创建一个新的ascii编码是没有意义的——只需要使用编码即可。ASCII财产。
代码如下:
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 ]; |
- 这样做是不安全的,如果输入字符串包含unicode范围字符,就会丢失原始数据。
- 这是一个串行通信项目,它无法处理unicode。当然,这是一个极其狭隘的案例。
OP的问题:"我如何在。net (c#)中将string转换为byte数组?"(原文如此)
你可以使用以下代码:
1 2 3
| static byte[] ConvertString (string s ) {
return new byte[0];
} |
作为一个好处,编码并不重要!等等,这是生态…它是平凡的,高度有损的。
- 这不是皈依。它是一个新的字节数组。OP真正需要的是一个指针和memcpy。或转换:byte[] b = (byte[]) s;。
- 此外,这里甚至没有使用"s"。绝对不是一个解决方案。