我如何转换string
为byte[]
在.NET(C#),而无需手动指定一个特定的编码?
我要加密字符串.我可以在不转换的情况下加密它,但我仍然想知道为什么编码在这里发挥作用.
另外,为什么要考虑编码?我不能简单地得到字符串存储的字节数吗?为什么依赖于字符编码?
1> Mehrdad..:
与此处的答案相反,如果不需要解释字节,则无需担心编码!
就像你提到的那样,你的目标只是"获取字符串存储的字节数".
(当然,能够从字节重新构造字符串.)
对于那些目标,老实说我不明白为什么人们一直告诉你需要编码.你当然不需要担心编码.
只需这样做:
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);
}
只要你的程序(或其他程序)不试图以某种方式解释字节,你显然没有提到你打算这样做,那么这种方法没有任何问题!担心编码只会让你的生活更加复杂,没有真正的理由.
这种方法的其他好处:
如果字符串包含无效字符并不重要,因为您仍然可以获取数据并重建原始字符串!
它将被编码和解码相同,因为您只是查看字节.
但是,如果您使用了特定的编码,那么编码/解码无效字符会给您带来麻烦.
@artbristol:如果他们不愿意阅读答案(或其他答案......),那么我很抱歉,那么我没有更好的方式与他们沟通.我通常选择回答OP,而不是试图猜测其他人可能会对我的答案做些什么 - OP有权知道,只是因为有人可能会滥用刀并不意味着我们需要隐藏世界上所有的刀为了我们自己 虽然如果你不同意那也没关系.
这个问题的丑陋之处在于,需要在具有相同字节序的系统上执行`GetString`和`GetBytes`.所以你不能用它来获得你想在其他地方变成字符串的字节.所以我很难想出一个我想要使用它的情况.
-1我保证有人(不理解字节与字符)会想要将他们的字符串转换为字节数组,他们会谷歌并阅读这个答案,他们会做错事,因为几乎所有例如,编码*IS*相关.
这个答案在很多层面上都是错误的,但最重要的是因为它"你不需要担心编码!".两个方法GetBytes和GetString是多余的,因为它们只是重新实现了Encoding.Unicode.GetBytes()和Encoding.Unicode.GetString()已经完成的任务.声明"只要你的程序(或其他程序)不试图解释字节"也从根本上是有缺陷的,因为它们意味着字节应该被解释为Unicode.
@CodeInChaos:就像我说的那样,这一点的重点在于你是否想在相同类型的系统上使用它,具有相同的功能集.如果没有,那么你不应该使用它.
好吧,我想的方式是:我不是法官.在我回答他之前,我并没有要求OP提供"证据"来证明他的案子(与其他人可能试图做的相反).他*明确*说,"我不能简单地得到字符串存储的字节数?为什么这种依赖于编码?",我的答案100%准确,比本页IMO上的其他人更多.而IMO他现在已经理解了这一点.此外,答案来自3年前的事实是无关紧要的.但是,如果你首先要求"证据",那么这就是你的风格,并随时保留下来.
-1为答案.大卫的+1和artbristol上面的评论.当然,.NET中存在字符串的内存表示.它恰好是小端UTF-16.当你得到字节数组时,你得到*that*encoding.如果你想要做的只是从字节数组转换回字符串,答案就足够了.但答案是有限且危险的.例如,如果要将字节包含在HTTP请求中,则需要知道整个请求的编码.如果您的业务是将字符转换为字节,那么您必须*理解编码.
@David:是的,它在UTF-8数据上崩溃,因为`GetBytes`永远不会返回UTF-8数据.看起来你所期待的抽象层与实际存在的抽象层不同.如果你不确定如何正确使用它,那就不要; 答案可能不是针对您的用例.但是,我100%支持我的回答它是正确的*用于它的用途*,我试图弄清楚.
@ConcreteGannet:我很高兴我们都同意*"如果您想要做的就是将字节数组转换回字符串,答案就足够了."*这就是我答案的全部内容.
** - 1**这是多么可怕**这是被接受的和最高投票的答案**.是的,以字符串存储在内存中的方式获取字符串的字节可能很有用.是的,如果在具有不同字节顺序的机器上调用`GetString`和`GetBytes`,它可能无效.但是说"你不需要担心编码!" 是如此可怕**邪恶**,因为你鼓励人们忽略[绝对最低每个软件开发人员绝对,积极必须知道](http://www.joelonsoftware.com/articles/Unicode.html).@artbristol是对的:编码_IS_相关.
@LeonLucardie:其他选择并不"更好";*事实上,它们更糟糕***因为它们打破了无法正确编码的字符串(例如包含未配对代理的字符串).我现在已经提到了这一百万次,但显然人们忽视这个事实非常方便......
@David:叹气,是的,他们*发生*重新实现,**但这在这个抽象级别**是无关紧要的.如果你甚至*关心那个事实,那么*你使用它是错误的*.如果您不知道我的意思,那么请不要使用它,但它对OP的用例/抽象级别100%有效.
@chiccodoro:安全不是这里唯一的问题.在你的(假设?)系统中,UTF-16不是内部表示,`Encoding.Unicode`会更慢,对于用于(OP已经理解的)用例没有任何好处.此外,如果您不知道自己在做什么,**安全只是一个问题**.你没有看到C程序员避免使用指针,尽管它们有多"危险",你呢?您也看不到建筑工人避免使用电锯和钻头.仅仅因为你认为某些事情是危险的并不意味着人们没有权利去了解它.
@ user1151923:老兄,问题是**你的**代码,不是我的答案!你正在使用`GetBytes`将字符串转换为字节,但是你没有使用`GetString`来反向!这些应该是成对****; 你不能只做任何你想做的事情,并希望它能发挥作用.如果你不以某种方式使用编码,你也必须在反方向忽略它们,但你忽略了`StreamReader`是基于编码的这一事实!阅读我的意见的前面:http://stackoverflow.com/questions/472906/net-string-to-byte-array-c-sharp/10380166?noredirect=1#comment13383434_10380166
我只需要字节来加密我的加密工作,我想你仍然回答岩石!
让这个答案如此可怕的原因是假设OP只想为某些短暂的操作"获取字节",然后继续评论使用编码将通过删除未配对的代理来破坏无效字符串这一事实.这引出了一个问题,**为什么数据首先表示或存储为字符串**?字符串用于表示文本,而不是某些破碎或非法的字符序列.(继续 ...)
当然,这种迂回的方法在技术上是正确的,因为它满足了OP绝大多数指定用例的一些虚构规范,但对于OP实际上*试图完成的内容,肯定有更正确的解决方案.既然我们可能永远都不知道那可能是什么,这个答案不仅不正确,而且作为这个问题的答案以及一般情况都是有害的.
@David:*"......隐含地表示字节应该被解释"*我不知道你如何阅读答案,但它"隐含地"意味着它们可以是任何编码.另外,如果你认为这些方法仅仅是"Encoding.Unicode"的"重新实现",只是因为它们做同样的事情,那么你似乎并没有正确地理解抽象层.
@Mehrdad:那我们就来了.`GetBytes`和`GetString`是`Encoding.Unicode.GetBytes()\ GetString()`的重新实现.你正在重新构建你的论点,以支持你对_"任何编码"的初始断言_.我并没有对你提供的OP无法解决的代码提出异议(至少对于unicode)我只是不认为它进一步加深了他对使用的编码的理解,但是你试图隐藏它.
-1表示答案,+1表示David's,artbristol和Concrete的评论......这个答案没有提及它只有在同一平台上执行这两种方法时才有效.此外,它没有增加价值.答案的论点是为一个简单的问题提供一个简单的答案,但答案比简单地使用`Encoding.Unicode`更复杂.如果您只是使用这些方法,则无需担心编码,但无论您运行哪种平台,它们都是安全的.
-1为误导性声明"你不需要担心编码".这完全忽略了算法主要将字符串转换为字节缓冲区的事实,因为某些流操作需要它.当这种序列化发生时,编码对我们序列化到文件或连线都很重要.由于编码不匹配问题,行业每年要投入1000个工作小时,我们最不需要的是"我们不需要担心编码......".
这个问题是3年前提出来的,而且完全是含糊不清的.你没有证据表明OP将如何使用这些字节.其他人会有*完全相同的问题*,但是计划在编码很重要的情况下使用字节,在这种情况下你的答案将是完全错误的.
在.NET中询问`string`的字节类似于要求`object`的字节.`string`和`char`类型的目的是抽象实现细节.通过使用这个答案,您无意中绕过了实现细节,并留下了类似于二进制序列化的脆弱解决方案.没有理由使用这个答案,因为使用编码更健壮,更便携,更合理,最重要的是*更容易*.说真的,编码答案是单行的...为什么这样的事情会疯狂?!
@Travis:我读了你的整个评论,但是它的整个基础是错误的(你声称它类似于读取`object`的字节).这两者之间没什么相似之处.我告诉你的是,这段代码意味着**与你想象的不同的抽象级别**.说*"它就像`Encoding.Unicode.GetBytes`"*是错误的,因为它破坏了*那个抽象障碍**.我不知道还能告诉你什么.我的答案已经达到了目的,即直接回答OP的问题.如果你不喜欢我的答案,那么就投票吧; 这就是它的用途!
*"担心编码只会让你的生活变得更加复杂而没有真正的理由."* - 呃,除了*担心编码的答案比这个更简单.当然,这个答案仍然**依赖于特定的编码 - "str.ToCharArray()"必须依赖于编码,即使代码中没有明确提到该编码*(这只能被认为是错误的) )*.我很尊重你,Mehrdad,但这是一个可怕的答案.
@Thomas:我真的不在乎你是否"喜欢"解决方案(哎呀,我也不特别),但你不能否认这是OP用例中唯一正确的答案(字符串之间的转换)和字节数组).其他答案在这个过程中会破坏一些`char`序列,而我的则没有.保持你的downvote,但请在赶上潮流并传播错误信息之前请三思而后行.
@Mehrad只是因为你的答案在技术上是正确的,在这种情况下,并不是因为我之前的许多人所说的理由.当更好的替代品可用时,它就像推荐`goto`语句一样,因为"在这种情况下,它是否有效,不是吗?".本网站旨在为在问题范围内尽可能多的用例正常运行的答案.你在答案的顶部以大尺寸宣布"你不需要编码",同时留下主要警告作为底部的一个小注释可能会导致问题.
@Mehrdad,离题,你需要意识到技术上可能不等于语用相关或建筑上的声音.回到主题,无论你是否意识到这一点,你都在有效地执行`System.Text.Encoding.Unicode.GetBytes(str)`因为这就是.NET在内存中表示`string`所做的事情.人们说你不懂编码因为*他们知道*你无法避免它.你正在做的唯一的事情就是跳过箍**来隐藏它!**你真的还认为这是一个好主意吗?
@Thomas:不,其他答案不适用于所有用例.你看过Michael Buen的回答了吗?他的回答告诉你为什么我的可以处理其他答案都没有的案例.*这里的答案没有*处理所有案例,但我处理OP的相关案例.
@Mehrdad即使在一个人们会表现得很专业的完美世界里,他们也不会花时间做一些研究.我几乎*肯定,这个解决方案的所有利弊都在这里的评论以及其他答案中得到了解决.如果仍然有人不会意识到这一事实并且认为继续争论(甚至1.25年后)已经解决过的问题,那么你的时间或其他任何人都不值得进一步争论.这里的答案适用于"需要编码"和"不需要编码"的用例; 就这么简单.
对于我的具体问题,最好的答案,谢谢!用于跟踪编码之间的转换毛刺,用于诊断目的,在同一台机器上,相同的应用程序,没有网络连接.仅仅因为我们大多数人都害怕会使用它来序列化数据并在平台/数据库中使用它们**并不是一个有效的理由**来设置这个答案.专门用于避免灾难性的编码结果.这就是为什么我非常喜欢这样的原因:你可以在这里得到非常具体和不同寻常的任务的答案.对于有关安全字符串字节转换的初学者,请重新阅读MSDN.
@ŞafakGür:是的我 - 我*做*鼓励人们忽略与问题无关的事情.什么*真的*"邪恶"教导人们担心错误的事情.我认为编码与问题无关,因为编码是*完全不同的**抽象级别*.你显然不这么认为,所以保持你的downvote,并感谢分享你的想法.
别误会我的意思,简单就是好.但OP提出了一个非常普遍的问题.他是否会在同一架构上转换为字符串?他是否要将字节写入文件并期望使用特定的文本编辑器进行查看?他没有陈述任何这些.因此,任何提出这个问题的人都可能会读到"你当然不需要担心编码",并认为编码不相关也不需要,无论如何.因此,如果您说"如果您在同一台计算机上解码字节并且不需要特定的编码,则使用此方法",这可能是一个很好的答案.
@ F.Buster:*一个字符串用于表示文本,而不是一些破碎或非法的字符序列.*...你得出结论.仅仅因为字符串可能无效,UTF-16并不意味着它"破碎"或"不是文本".它只是意味着您不能假设编码是UTF-16,因此答案需要独立于字符串可能正在使用的任何编码.它是.如果你不喜欢这个问题那么我很抱歉,但这个**是这个问题的正确答案.
+1完全是我的想法,我不知道这里有关于编码的一些窥视的坚持.只需要进行内存转储/序列化(虽然Microsoft的默认序列化库存在缺陷).我希望我之前知道这个BlockCopy API :-)
@Mehrdad _...它"隐式"意味着它们可以是任何编码"_我不明白这个陈述,你到底是什么意思?据我所见,_your_`GetBytes()`方法将返回一个字符串的Unicode编码字节数组和_your_`GetString()`方法将(如果你传递一个字符串的Unicode编码字节数组表示)返回一个可读的字符串,并在任何其他编码返回垃圾.更糟糕的是,虽然`如果你传递一个包含奇数个字符的字符串的UTF-8编码字节数组,GetString()`将崩溃.
是的,这个答案适用于利基用例.但其他答案适用于所有用例.为什么不使用要求您输入编码的上级(以及易于使用且不易出错的)技术?因此,给这个大胖子-1.
@SteffenWinkler:是的,答案确实使用了*a*编码,但重点是它并不关心什么.原因是保证两种方式都使用相同的编码.我不确定您认为用户如何更改编码,因为这是.NET用于存储字符串的编码.我不相信用户可以改变它.如果运行时已更改,那么您将重新启动程序,因此两种方法仍将使用相同的编码.
@ ErikA.Brandstadmoen:两件事:(1)如果它不是一个连续的字节序列,那么你就无法通过`fixed(char*p = str){...获得一个指向数据的指针. (2)实际情况是,这个事实实际上是100%不相关的,因为`ToCharArray`总是返回一个char数组而不管底层数据格式,这是我们需要和关心的.
@Mehrdad:_so答案需要独立于字符串可能碰巧使用的任何编码_ <=这是_representation_与_abstraction_的混淆.一个字符串,就像一个字符串,_already_独立于实现在封面下使用的任何编码.将字符串"Hello world"_any_转录到某个字节序列的行为是利用编码_by definition_.插入一个人的耳朵,喊"洛杉矶洛杉矶!",并重新解释一块内存作为字节是唯一的事情是_hiding_恰好使用的编码.
>>不要试图以某种方式解释字节<<只是查看字节是一种解释形式
这是一种编码.您刚刚发明了自己的编码,而不是使用标准编码.
@Travis:除了它**与请求`object`的字节相同之外:.NET专门阻止你这样做,但不会阻止你这样做.这本身应该足以告诉你有所不同.
@Travis:我要告诉你的最后一件事(因为我现在才注意到它)是阅读[**这个答案**](http://stackoverflow.com/a/10384024/541686).我之前已经提到了这一点,但既然答案实际上已经证明了这一点,我会再说一遍:我的答案可以保存并恢复字符串*完美*; 基于编码的方法无法处理无法正确表示的`char`序列.
这个答案错误的原因是,在没有编码的情况下将一系列字形映射到一个字节序列是不可能的.但是,该示例也可以在不直接使用Encoding对象的情况下工作.那是因为它暗中断言字符串的规范编码方案 - 我相信的Unicode 16 - 对于所有解码实现都是正确的.这适用于.NET,但不适用于其他语言或运行时.用户知道他们在这里做的是导出(已编码的)内部表示,而不是真正避免编码,这一点很重要.
@MichaelBuen:是的.只要您的内存转储/序列化*不*尝试解释数据,它就没事了.要记住的经验法则是:如果你的程序(或不同的程序)需要将`GetBytes`的输出转换回相同的字符串,它可能*仅*使用`GetString`来执行此操作.只要你不违反,你就可以完全忽略编码的概念.
@Mehrdad:`我如何将字符串转换为.NET中的字节数组(C#)?`是OP描述的用例.字面上任何返回`byte []`的答案在技术上都是正确的.但我完成了这个扩展聊天.
这不会保持编码完整.太糟糕了,这是最高票数的公认答案,因为我浪费了2个小时试图找出我的字符串为什么会出现乱码.将其追溯到使用此答案转换字符串 - > byte []的方法.
@ user1151923:在你责怪我没有警告你之前,要意识到你的代码中发生的事情*完全等同于使用`new StreamReader(stream).ReadToEnd()`向一个方向发展,但使用`编码. UTF8`走向另一个方向.这是错误的,因为作者是粗心的,它与可能告诉你使用"UTF8"的答案无关.如果"StreamReader"默认使用UTF-16这一事实令人困惑,请不要将其归咎于我的回答; 设计就是这样,这不是我的错.
@Jodrell:你刚刚回答了自己.而且,它需要.NET 3.5,这不是必需的.
这是我见过的最糟糕的代码之一.我看到人们在.NET 4中使用DataTables!提问者和发布此答案的人似乎都不了解编码的实际含义.当然你使用这个答案的编码...但你不知道哪种编码!即使你在同一台机器上转换东西,谁告诉你用户不会改变他的编码,渲染字节是不可读的?!
@ŞafakGür:你必须明白,你希望我把它放到我的答案中的方法是*完全错误*因为它在字符串和字节数组之间不是双射的 - 例如,它不保留不成对的代理.我现在已经说了这么多次了.如果它至少*工作正常*,我会考虑它.但它没有 - 它打破任何不符合Unicode编码的字符串.这就是为什么我如此坚持完全避免编码:它们不仅是不必要的,它们是*公然错误*并且不会对任意字符串起作用.
@artbristol:那是一个新的.在你到目前为止的所有评论中,你甚至从未对我所理解的问题有所了解,现在你在指责我是一个不知道String在C#中意味着什么的砖墙.为了记录,我既不是砖墙,也不是C程序员,如果你在C和C#中查看我的徽章,假装你非常了解我,那就相当明显了.
@tandztc:不,我认为你是那个没有正确使用它的人.你怎么首先得到一个奇怪的`bytes.Length`?如果您正确地遵循了答案(这意味着您使用`GetBytes`来获取`bytes`)那么该事件是不可能的.如果您以其他方式获得字节数组,则必须以相同的方式将其转换回字符串,而不是使用此答案.
Aardvark,你的编辑也不错,但我没有看到它的重点(我注意到了一点语法/大写错字),所以我把它翻了回来......我觉得原来是好的足够了,这就是我想要表达的东西,我宁愿不进行编辑.我认为讨论确实有一个好处,应该留下来,因为(1)它帮助读者意识到这个答案在共享代码库中可能引起争议,(2)它让我强调为什么我认为答案是正确的方法.无论如何,讨论已经结束了,所以不要担心.
只需使用Encoding.Unicode.GetBytes().此答案中发布的函数比Unicode.GetBytes()慢2倍.在Release&x64环境中测试.
不,没有混乱.您的答案正确地指出,如果您不打算解释字符串,则不需要担心编码,但正好有0种情况可能有用.甚至你自己的建议_("重构字符串")_依赖于不改变的字符串的内部编码.与此同时,初学者会看到这个答案而错误地认为他们不必担心编码是什么.这个答案比错误更糟糕,因为它在技术上是正确的但非常有害.
@BlueMonkMN:*"这就是我的观点:我不知道你怎么能得到一个包含无效字符的字符串,而不是从字节数组中转换它."*......这里有一个:`"\ uD800"+"\uDC00"`这两个字符串都无效,但它们的连接是有效的.也许你想将每个转换为字节,传输它们,然后将它们转换回来然后连接.也许它们是通过类似地分割有效字符串而生成的.有一百万种方法可能会导致无效的字符串...
@Mehrdad:经过一番询问,OP说他们打算对字符串进行加密。很有可能,转换为字节数组后的下一步将是某种形式的输出。您的答案是否正确取决于读取这些加密字节的内容。OP没有*没有*说.NET应用程序将读取加密的字节。如果要读取其他内容,OP应该确保编码符合读者的预期。如果字符串很大并且仅包含或几乎包含纯ASCII,则UTF-8会更紧凑,更快速地加密和更快地输出。
+1,但是,不会`str.SelectMany(BitConvertor.GetBytes).ToArray();`就够了.(是的,我怀疑`BlockCopy`更快.)
@Mehrdad:也许最好明确你的方法适用于可以保存任意二进制数据的`String`实例的可预测序列化,而不是那些已知保存有效UTF-16字符串的实例.真的太糟糕了MS没有包含任何其他"不可变的blob"类型,因为`String`经常被使用,而某些其他标准blob类型可能更合适**如果存在*.
我使用这个解决方案在密码和散列之前将密码字符串转换为`byte []`.在这个用例中,我绝对不关心编码_at all_.我甚至不需要将生成的哈希转换回字符串 - 对于密码验证,我只是直接比较生成的哈希`byte [].针对此特定用例的非常优雅且低开销的解决方案.不过,这里的火焰战争很有趣.
@Josep:我很高兴你的代码崩溃了,因为它试图告诉你你错了.而不是试图绕过它,意识到这个答案只是为了解决一个与你不同的特定问题,因此你不应该使用它.
@ BlueRaja-DannyPflughoeft:*"这里有0个案例,这可能是有用的."*我已经解释过,即使字符串无效UTF-16也能正常工作,因此在这种情况下对人们有用.如果您不亲自发现它有用,则不必使用它.
原始数据存储在字符串中的事实已经意味着编码.它不仅仅是一个可以随意玩弄的字节数组.如果是这样,为什么要将它存储在字符串中?那是......只是愚蠢.这里的断言是人们错误地"解释"字节是不正确的,因为它们的字节已经_already_被原始数据存储在.net字符串中的事实所解释.结果字节的消费者将不得不隐式地知道编码是什么来使用任何原始字节.
2> bmotmans..:
它取决于字符串的编码(ASCII,UTF-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 (...)
.
有关详细信息,请参阅.NET Framework中的字符编码(MSDN).
.NET字符串始终编码为Unicode.所以使用System.Text.Encoding.Unicode.GetBytes(); 获取.NET用来表示字符的字节集.但是你为什么要这样呢?我建议使用UTF-8,特别是当大多数角色都在拉丁西部时.
您不需要编码,除非您(或其他人)实际打算*解释*数据,而不是将其视为通用的"字节块".对于压缩,加密等问题,担心编码是没有意义的.请参阅[我的回答](http://stackoverflow.com/a/10380166/541686),了解如何在不担心编码的情况下执行此操作.(当你不这样做时,我可能会给你一个-1来说你需要担心编码,但我今天感觉不太特别.:P)
但是,为什么要考虑编码?为什么我不能简单地获取字节而不必查看正在使用的编码?即使它是必需的,String对象本身也不应该知道正在使用什么编码并简单地转储内存中的内容?
@Joel,小心System.Text.Encoding.Default,因为它在运行的每台机器上可能不同.这就是为什么建议始终指定编码,例如UTF-8.
另外:字符串内部使用的确切字节_don't matter_如果检索它们的系统不处理该编码或将其处理为错误的编码.如果它都在.Net中,为什么要转换为字节数组.否则,最好明确你的编码
+1; @Mehrdad:`GetString`方法*是*并解释了`GetBytes`方法的输出.这就是为什么你*担心在两种方法中使用相同的编码.
我认为重要的是要注意它*"不依赖于你的字符串的编码"*..NET隐藏了这一点.据我所知,String由一系列System.Chars表示,表示为UTF-16.重要的是你必须将字节存储在*某些编码*中,并且知道用*相同的编码*来检索它们.不这样做与密码保护文件相同,并尝试使用不同的密码来取消保护.
好的讨论,有时我需要上述替代方案之一.但也看起来像:"一个傻瓜可以问七个以上的智者可以回答":-)
3> Erik A. Bran..:
接受的答案非常非常复杂.使用包含的.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能够重新审视并接受更好的解决方案.
@AMissico,你的建议是错误的,除非你确定你的字符串与你的系统默认编码兼容(在你的系统默认遗留字符集中只包含ASCII字符的字符串).但OP没有说明这一点.
原则上很好但是,编码应该是`System.Text.Encoding.Unicode`等同于Mehrdad的答案.
自最初答案以来,这个问题已被编辑了很多次,所以,也许我的答案有点过时了.我从来没有打算给出与Mehrdad的答案相当的exace,但是给出一个合理的方法来做到这一点.但是,你可能是对的.但是,原始问题中的短语"获取字符串已存储的字节数"是非常不准确的.存储,在哪里?在记忆中?在磁盘上?如果在内存中,`System.Text.Encoding.Unicode.GetBytes`可能会更精确.
@AMissico它可以使程序在不同的系统中给出不同的结果.那是_never_一件好事.即使它是用于制作哈希或其他东西(我假设OP的含义是'加密'),相同的字符串仍然应该总是给出相同的哈希值.
4> 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());
很有意思.显然它会丢弃任何高代理Unicode字符.请参阅[[BinaryFormatter]上的文档(http://msdn.microsoft.com/en-us/library/system.runtime.serialization.formatters.binary.binaryformatter%28v=VS.100%29.aspx)]
您可以对所有这些操作使用相同的BinaryFormatter实例
5> Zhaph - Ben ..:
您需要考虑编码,因为1个字符可以由1个或更多字节(最多约6个)表示,不同的编码将以不同的方式处理这些字节.
乔尔有一个帖子:
绝对最低每个软件开发人员绝对必须知道Unicode和字符集(没有借口!)
您不需要编码,除非您(或其他人)实际打算*解释*数据,而不是将其视为通用的"字节块".对于压缩,加密等问题,担心编码是没有意义的.请参阅[我的回答](http://stackoverflow.com/a/10380166/541686),了解如何在不担心编码的情况下执行此操作.
@Mehrdad - 完全如此,但是我最初回答时提出的原始问题没有告诫OP在转换它们后会发生什么样的字节,并且对于未来的搜索者来说,周围的信息是相关的 - 这是[Joel的答案](http://stackoverflow.com/a/473419/33051)很好地涵盖了 - 并且正如你在答案中所述:只要你坚持在.NET世界中,并使用你的方法转换为/从, 你很高兴.一旦你走出它,编码就会很重要.
"1个字符可以用1个或更多字节表示"我同意.我只想要那些字节,无论字符串是什么编码.字符串可以存储在内存中的唯一方法是以字节为单位.偶数字符存储为1个或更多字节.我只是想抓住他们的字节.
6> Tom Blodget..:
这是一个很受欢迎的问题.重要的是要理解作者提出的问题,并且它与最常见的需求不同.为了阻止在不需要的地方滥用代码,我先回答了后面的问题.
共同需要
每个字符串都有一个字符集和编码.将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文章.
具体需要
现在,问题作者问:"每个字符串都存储为一个字节数组,对吧?为什么我不能简单地拥有这些字节?"
他不想要任何转换.
来自C#规范:
C#中的字符和字符串处理使用Unicode编码.char类型表示UTF-16代码单元,字符串类型表示UTF-16代码单元序列.
所以,我们知道如果我们要求空转换(即,从UTF-16到UTF-16),我们将得到所需的结果:
Encoding.Unicode.GetBytes(".NET String to byte array")
但为了避免提及编码,我们必须采取另一种方式.如果中间数据类型是可接受的,则有一个概念性的快捷方式:
".NET String to byte array".ToCharArray()
这并没有让我们得到所需的数据类型,但Mehrdad的答案显示了如何使用BlockCopy将此Char数组转换为Byte数组.但是,这会复制字符串两次!而且,它也明确使用特定于编码的代码:数据类型System.Char
.
获取存储String的实际字节的唯一方法是使用指针.该fixed
语句允许获取值的地址.来自C#规范:
[For] string类型的表达式,...初始化程序计算字符串中第一个字符的地址.
为此,编译器将代码跳过写入字符串对象的其他部分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这是正确的但是字符串长度已经给出了代码单元的数量(而不是代码点).
7> Joel Coehoor..:
您的问题的第一部分(如何获取字节)已被其他人回答:查看System.Text.Encoding
命名空间.
我将解决您的后续问题:为什么需要选择编码?为什么你不能从字符串类本身那里得到它?
答案分为两部分.
首先,字符串类内部使用的字节无关紧要,无论何时你认为它们都可能引入了一个bug.
如果您的程序完全在.Net世界中,那么即使您通过网络发送数据,也不必担心为字符串获取字节数组.相反,使用.Net Serialization来担心传输数据.您不再担心实际的字节:序列化格式化程序会为您执行此操作.
另一方面,如果您将这些字节发送到您无法保证的某个位置会从.Net序列化流中提取数据怎么办?在这种情况下,你肯定需要担心编码,因为显然这个外部系统关心.同样,字符串使用的内部字节无关紧要:您需要选择一个编码,以便您可以在接收端明确表示此编码,即使它与.Net内部使用的编码相同.
我知道在这种情况下你可能更喜欢在可能的情况下使用字符串变量存储在字节变量中的实际字节,并且可能会节省一些创建字节流的工作.但是,我把它告诉你,与确保在另一端理解你的输出相比,它并不重要,并保证你必须明确你的编码.此外,如果您确实希望匹配内部字节,则可以只选择Unicode
编码,从而节省性能.
这使我想到的第二部分...采摘Unicode
编码是告诉.NET来使用底层的字节.你确实需要选择这种编码,因为当出现一些新奇的Unicode-Plus时,.Net运行时需要免费使用这种更新,更好的编码模型而不会破坏你的程序.但是,目前(以及可预见的未来),只需选择Unicode编码即可获得所需内容.
理解你的字符串必须重写为连线也很重要,即使你使用匹配的编码,也至少需要对位模式进行一些翻译.计算机需要考虑Big vs Little Endian,网络字节顺序,打包,会话信息等.
在.NET中,您必须获得字符串的字节数组.许多.NET Cryptrography类都包含接受字节数组或流的ComputeHash()等方法.您别无选择,只能先将字符串转换为字节数组(选择编码),然后将其包装在流中.但是,只要你选择一个编码(即UTF8),它就会有一个问题.
8> Michael Buen..:
只是为了证明Mehrdrad的合理答案是有效的,他的方法甚至可以坚持不成对的代理人物(许多人反对我的答案,但其中每个人都同样有罪,例如System.Text.Encoding.UTF8.GetBytes
,System.Text.Encoding.Unicode.GetBytes
那些编码方法不能坚持高代理人字符d800
例如,这些只是仅仅用替换值高代理字符fffd
):
using System;
class Program
{
static void Main(string[] args)
{
string t = "??";
string s = "Test\ud800Test";
byte[] dumpToBytes = GetBytes(s);
string getItBack = GetString(dumpToBytes);
foreach (char item in getItBack)
{
Console.WriteLine("{0} {1}", item, ((ushort)item).ToString("x"));
}
}
static byte[] GetBytes(string str)
{
byte[] bytes = new byte[str.Length * sizeof(char)];
System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
static string GetString(byte[] bytes)
{
char[] chars = new char[bytes.Length / sizeof(char)];
System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
return new string(chars);
}
}
输出:
T 54
e 65
s 73
t 74
? d800
T 54
e 65
s 73
t 74
尝试使用System.Text.Encoding.UTF8.GetBytes或System.Text.Encoding.Unicode.GetBytes,它们只会用值fffd替换高代理字符
每当这个问题发生变化时,我仍然会想到一个序列化器(无论是来自Microsoft还是来自第三方组件),它可以持久化字符串,即使它包含不成对的代理字符; 我不时地谷歌这个:序列化不成对的代理人物.NET.这并没有让我失去任何睡眠,但是当有人评论我的答案是否存在缺陷时,它会有点烦人,但是当涉及到不成对的代理角色时,他们的答案同样存在缺陷.
Darn,微软应该刚刚使用System.Buffer.BlockCopy
它的BinaryFormatter
ツ
谢谢!
代理人不必成对出现以形成有效的代码点吗?如果是这种情况,我可以理解为什么数据会被破坏.
@MichaelBuen在我看来,主要的问题是,您用粗体大写的字母表示无所谓,而不是说在他们的情况下无所谓。结果,您鼓励那些看您答案的人犯一些基本的编程错误,这些错误将来会导致其他人感到沮丧。未配对的代理在字符串中无效。它不是一个char数组,因此将字符串转换为另一种格式会导致该字符出现错误“ FFFD”,这是有道理的。如果要执行手动字符串操作,建议使用char []。
@dtanders:`System.String`是一个不可变的`Char`序列; .NET总是允许`String`对象从任何`Char []`构造,并将其内容导出到包含相同值的`Char []`,即使原始的`Char []`包含未配对的代理.
9> Nathan..:
试试这个,少了很多代码:
System.Text.Encoding.UTF8.GetBytes("TEST String");
@ mg30rg:为什么你认为你的例子很奇怪?当然,在可变宽度编码中,并非所有字符都具有相同的字节长度.它出什么问题了?
10> Gman..:
好吧,我已经阅读了所有的答案,他们是关于使用编码或一个关于序列化,删除不成对的代理人.
例如,字符串来自SQL Server,它是从存储密码哈希的字节数组构建的,这很糟糕.如果我们从中删除任何东西,它将存储一个无效的哈希,如果我们想将它存储在XML中,我们希望保持原样(因为XML编写器在它找到的任何未配对的代理上删除了一个例外).
所以我在这种情况下使用字节数组的Base64编码,但是,在互联网上只有一个解决方案,在C#中,它有bug,只有一种方式,所以我修复了bug并写回来了程序.在这里,未来的googlers:
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);
}
11> Konamiman..:
还请解释为什么要考虑编码.我不能简单地得到字符串存储的字节数吗?为什么这种依赖编码?!!!
因为没有"字符串的字节"之类的东西.
字符串(或更一般地说,文本)由字符组成:字母,数字和其他符号.就这样.然而,计算机对字符一无所知; 他们只能处理字节.因此,如果要使用计算机存储或传输文本,则需要将字符转换为字节.你是怎样做的?这是编码到达现场的地方.
编码只不过是将逻辑字符转换为物理字节的惯例.最简单和最知名的编码是ASCII,如果你用英文写作,它就是你所需要的.对于其他语言,您将需要更完整的编码,任何Unicode风格都是当今最安全的选择.
因此,简而言之,尝试"在不使用编码的情况下获取字符串的字节数"与"不使用任何语言编写文本"一样不可能.
顺便说一下,我强烈建议你(以及任何人,就此而言)阅读这一小小的智慧:绝对最低每个软件开发人员绝对必须知道Unicode和字符集(没有借口!)
但同样,除非你使用编码,否则没有文本到物理字节翻译的概念.当然,编译器以某种方式将字符串存储在内存中 - 但它只是使用内部编码,您(或除编译器开发人员之外的任何人)都不知道.所以,无论你做什么,你都需要一个编码来从字符串中获取物理字节.
请允许我澄清一下:编码已用于将"hello world"转换为物理字节.由于字符串存储在我的计算机上,我确信它必须以字节存储.我只是想访问这些字节以将它们保存在磁盘上或出于任何其他原因.我不想解释这些字节.由于我不想解释这些字节,因此此时对编码的需求与要求电话线调用printf一样错位.
@Gnafoo,这个字节的副本就可以了.
12> Shyam sundar..:
C#将a转换string
为byte
数组:
public static byte[] StrToByteArray(string str)
{
System.Text.UTF8Encoding encoding=new System.Text.UTF8Encoding();
return encoding.GetBytes(str);
}
13> gkrogers..:
byte[] strToByteArray(string str)
{
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
return enc.GetBytes(str);
}
这并不总是有效.一些特殊的角色在使用我发现困难的方法时会迷失方向.
14> 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);
15> John Rasch..:
随着Span
C#7.2 的发布,将字符串的底层内存表示捕获到托管字节数组的规范技术是:
byte[] bytes = "rubbish_\u9999_string".AsSpan().AsBytes().ToArray();
将其转换回来应该是非首发的,因为这意味着您实际上是以某种方式解释数据,但为了完整起见:
string s;
unsafe
{
fixed (char* f = &bytes.AsSpan().NonPortableCast().DangerousGetPinnableReference())
{
s = new string(f);
}
}
名称,NonPortableCast
并DangerousGetPinnableReference
应进一步说你可能不应该这样做的论点.
请注意,使用时Span
需要安装System.Memory NuGet包.
无论如何,实际的原始问题和后续注释意味着底层内存没有被"解释"(我假设意味着没有修改或读取超出需要按原样写入),表明Stream
该类的一些实现应该使用而不是推理数据作为字符串.
16> Ed Marty..:
我不确定,但我认为字符串将其信息存储为Chars数组,这对字节来说效率很低.具体而言,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
,但使用存储空间效率低下.
17> Hans Passant..:
关键问题是字符串中的字形占用32位(字符代码为16位),但一个字节只有8位备用.除非您将自己限制为仅包含ASCII字符的字符串,否则不存在一对一映射.System.Text.Encoding有很多方法可以将字符串映射到byte [],你需要选择一个避免信息丢失的方法,当你需要将byte []映射回字符串时,客户端很容易使用它.
Utf8是一种流行的编码,它紧凑而且没有损耗.
真正.但是,如果您熟悉处理中文文本,怎么能不知道编码?
只有当您的大多数字符都是英文(ASCII)字符集时,UTF-8才是紧凑的.如果你有一长串中文字符,UTF-16对于该字符串来说将比UTF-8更紧凑.这是因为UTF-8使用一个字节来编码ASCII,否则使用3个(或者4个).
18> mashet..:
使用:
string text = "string";
byte[] array = System.Text.Encoding.UTF8.GetBytes(text);
结果是:
[0] = 115
[1] = 116
[2] = 114
[3] = 105
[4] = 110
[5] = 103
19> Alessandro A..:
最快的方式
public static byte[] GetBytes(string text)
{
return System.Text.ASCIIEncoding.UTF8.GetBytes(text);
}
编辑
Makotosan评论说这是现在最好的方法:
Encoding.UTF8.GetBytes(text)
ASCIIEncoding .....不需要.简单地使用Encoding.UTF8.GetBytes(text)是首选.
20> Jason Goemaa..:
如何在不手动指定特定编码的情况下将字符串转换为.NET(C#)中的byte []?
.NET中的字符串将文本表示为UTF-16代码单元序列,因此字节在UTF-16中已经在内存中进行编码.
Mehrdad的回答
你可以使用Mehrdad的答案,但它确实使用编码,因为字符是UTF-16.它调用ToCharArray,查看源创建char[]
并直接复制内存.然后它将数据复制到也分配的字节数组.所以发动机罩下它是复制底层字节两次并分配该呼叫后不使用字符数组.
Tom Blodget的回答
Tom Blodget的答案比Mehrdad快20-30%,因为它跳过了分配char数组并将字节复制到它的中间步骤,但它需要你使用该/unsafe
选项进行编译.如果你绝对不想使用编码,我认为这是要走的路.如果将加密登录放在fixed
块中,则甚至不需要分配单独的字节数组并将字节复制到其中.
另外,为什么要考虑编码?我不能简单地得到字符串存储的字节数吗?为什么依赖于字符编码?
因为这是正确的方法. string
是一种抽象.
如果您的"字符串"包含无效字符,则使用编码可能会给您带来麻烦,但这不应该发生.如果您使用无效字符将数据输入到字符串中,那么您做错了.您可能应该使用字节数组或Base64编码开始.
如果您使用System.Text.Encoding.Unicode
,您的代码将更具弹性.您不必担心代码将运行的系统的字节顺序.如果下一版本的CLR将使用不同的内部字符编码,则无需担心.
我认为问题不在于你为什么要担心编码,而是为什么要忽略它并使用别的东西.编码旨在表示字节序列中字符串的抽象. System.Text.Encoding.Unicode
将为您提供一个小端字节顺序编码,并将在现在和将来的每个系统上执行相同的操作.
21> Gerard ONeil..:
最接近OP问题的方法是Tom Blodget,它实际上是进入对象并提取字节.我说最接近因为它取决于String对象的实现.
"Can't I simply get what bytes the string has been stored in?"
当然,但这就是问题中出现根本错误的地方.String是一个可能具有有趣数据结构的对象.我们已经知道它确实存在,因为它允许存储未配对的代理.它可能存储长度.它可能会指向每个"配对"代理,以便快速计数.等等.所有这些额外字节都不是字符数据的一部分.
你想要的是数组中每个字符的字节.这就是'编码'的用武之地.默认情况下,你会得到UTF-16LE.如果您不关心除了往返之外的字节本身,那么您可以选择任何编码,包括'default',并稍后将其转换回来(假设相同的参数,例如默认编码,代码点,错误修复) ,允许的事情,如未成对的代理人等.
但为什么要让'编码'达到魔法?为什么不指定编码,以便您知道要获得的字节数?
"Why is there a dependency on character encodings?"
编码(在此上下文中)仅表示表示字符串的字节.不是字符串对象的字节.你想要存储字符串的字节 - 这就是问题被天真地问的地方.您希望字符串的字节在表示字符串的连续数组中,而不是字符串对象可能包含的所有其他二进制数据.
这意味着如何存储字符串是无关紧要的.您希望在字节数组中将字符串"Encoded"转换为字节.
我喜欢Tom Bloget的答案,因为他带你走向'字符串对象的'字节'方向.虽然它依赖于实现,并且因为他正在窥视内部,所以可能很难重新构建字符串的副本.
Mehrdad的回答是错误的,因为它在概念层面上具有误导性.您仍然有一个编码的字节列表.他的特殊解决方案允许保留未配对的代理 - 这取决于实现.如果GetBytes
默认情况下以UTF-8返回字符串,则他的特定解决方案不会准确生成字符串的字节.
我已经改变了主意(Mehrdad的解决方案) - 这不是获取字符串的字节; 而是获取从字符串创建的字符数组的字节.无论编码如何,c#中的char数据类型都是固定大小.这允许产生一致的长度字节数组,并且它允许基于字节数组的大小来再现字符数组.因此,如果编码是UTF-8,但每个char是6个字节以容纳最大的utf8值,它仍然可以工作.确实如此 - 角色的编码并不重要.
但是使用了转换 - 每个字符都放在一个固定大小的框中(c#的字符类型).然而,这种表示无关紧要,这在技术上是OP的答案.所以 - 无论如何你要转换......为什么不'编码'?
22> Shyam sundar..:
您可以使用下面的代码的转换string
到byte array
.NET中
string s_unicode = "abcéabc";
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(s_unicode);