. net 4.5 -为什么写入文件时StreamWriter不能像预期的那样处理字符串

本文关键字:字符串 处理 不能 StreamWriter 为什么 文件 net | 更新日期: 2023-09-27 18:05:56

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

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);
}
控制台输出

1 f8b0800000000000003c57d6b931cc5b1e867eb

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

1F C2 8B 08 00 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

不,它看起来不像一个字符从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 00 03 c5 7d 6b 93 1c c5 b1 e8 67 eb

. net 4.5 -为什么写入文件时StreamWriter不能像预期的那样处理字符串

不是真的,它是二进制数据:"▼♥Å}k?∟一±针对"

二进制数据必须存储在字节[]中。它不能存储在系统中。字符串,Unicode规范化将随机破坏数据,当二进制数据恰好匹配代理值之一时,您的程序将随机崩溃。

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

二进制数据必须由FileStream写入。StreamWriter不能写二进制数据,只能写文本。它将在编码字符串时随机销毁二进制数据。在您的例子中是Utf-8,默认值,产生额外的字节。

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

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

不管你的字符串的名字是什么,它是一个UTF-16字符串(某种程度上)。所有。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(因为英特尔芯片是"小端序"),当转换为int然后转换为十六进制字符串时,它当然是"8B"。UTF-7的表示似乎是2B 41 49 73 2D

如果您传递Encoding。Unicode到StreamWriter,您应该得到与十六进制编辑器中的控制台输出相同的输出,除了您将有额外的00字节,因为A在内存中表示为41 00,但是当您将其转换为int并调用ToString("x2")时,您将得到没有"00"的"41"。

编辑:

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