.NET 4.5 - 为什么StreamWriter在写入文件时没有按字符串的预期运行?

时间:2014-05-31 05:41:24

标签: c# .net streamwriter utf

为什么以下代码输出的十六进制字符串在十六进制编辑器中查看时与文件内容不同?

Console.Write(String.Concat(TheUTF7String.Select(c => ((int)c).ToString("x2"))).Substring(0, 40));
using (StreamWriter outfile = new StreamWriter("C:\\test", true))
{
    outfile.Write(TheUTF7String);
}

控制台输出

1f8b0800000000000003c57d6b931cc5b1e867eb

在十六进制编辑器中查看时的文件内容(前32个字节)

1F C2 8B 08 00 00 00 00 00 00 03 C3 85 7D 6B C2 93 1C C3 85 C2 B1 C3 A8 67 C3 AB 57 34 C3 A3 C2

解决Phoog的答案:

不,看起来像TheUTF7String中的一个字符被输出超过2个十六进制字符:

for (int i = 0; i < 20; i++)
    Console.Write(TheUTF7String.Select(c => ((int)c).ToString("x2")).ToArray()[i] + " ");

输出: 1f 8b 08 00 00 00 00 00 00 03 c5 7d 6b 93 1c c5 b1 e8 67 eb

2 个答案:

答案 0 :(得分:2)

简单的回答是“因为你的期望是错误的。”更有帮助的是,我希望:

尽管你的字符串的名称,它是一个UTF-16字符串(sort of)。所有.NET字符串都以这种方式编码在内存中。

流编写器的默认编码是UTF-8,这就是您在文件中获得的内容。

您的缓冲区包含UTF-7数据。当您调用Encoding.UTF7.GetString(buffer, 0, size)时,您将获得相同字符序列的内存中UTF-16表示。当您写入StreamWriter时,它会调用Encoding.GetBytes将字符串转换为它在文件中写入的字节。由于它使用UTF-8作为其默认编码,因此您可以在文件中获得UTF-8数据。

对于128-255(\u0080\u00ff范围内的任何值,UTF-16字符将转换为两位十六进制代码,但该字符的UTF-8序列将有两个字节。这解释了控制台输出和十六进制编辑器之间的区别。

字符8B在UTF-8中表示为C2 8B;在UTF-16中它是8B 00(因为intel芯片是“小端”)并且当转换为int然后转换为十六进制字符串时,它当然是“8B”。 UTF-7表示似乎是2B 41 49 73 2D

如果将Encoding.Unicode传递给StreamWriter,则应该与十六进制编辑器中的控制台输出相同,除非您有额外的00个字节,因为A表示为{ {1}}在内存中,但是当你将它转换为int并调用ToString(“x2”)时,你得到的是“41”而没有“00”。

编辑:

我只想到另一种看待它的方式。 41 00方法解码字节序列,返回相应的字符串,而GetString方法将字符串字符串编码为相应的字节序列。您可以忽略字符串的内存中表示。 (但是,对于诊断控制台输出,您需要记住字符串是一系列字符,而字节数组是一系列字节。)

答案 1 :(得分:2)

  

不是真的,它是二进制数据:“▼♥Å} k?∟űègë”

二进制数据必须存储在byte []中。它不能存储在System.String中,Unicode规范化将随机破坏数据,当二进制数据恰好匹配其中一个代理值时,程序将随机崩溃。

  

为什么StreamWriter的行为不符合预期

二进制数据必须由FileStream编写。 StreamWriter无法写入二进制数据,只能写入文本。它会在对字符串进行编码时随机销毁二进制数据。在您的情况下,Utf-8是默认值,产生额外的字节。

第一个引用是最重要的一个,当你假设你可以将数据存储在一个字符串中时,它就不用了。 StreamWriter是下一个不可避免的错误。您必须使用byte []。这可能意味着您必须修复获取数据的任何代码。