C#中char存储时sizeof、BaseStream.Position、FileStream.Length数值矛盾疑问
问题解答
测试代码
string file = @"G:\test.dat"; int num = 12; char c = 'w'; FileStream fsSave = new FileStream(file, FileMode.Create); BinaryWriter bwSave = new BinaryWriter(fsSave); Console.WriteLine("Start position:" + bwSave.BaseStream.Position); //bwSave.Write((int)num); bwSave.Write((char)c); Console.WriteLine("End position:" + bwSave.BaseStream.Position); bwSave.Flush(); bwSave.Close(); fsSave.Close(); FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read); BinaryReader br = new BinaryReader(fs); Console.WriteLine("char:"+sizeof(char)); Console.WriteLine("int:" + sizeof(int)); Console.WriteLine("fs.Length:" + fs.Length); Console.ReadLine(); br.Close(); fs.Close();
你观察到的数值矛盾本质是CLR中char类型的内存占用和BinaryWriter序列化字符时的编码输出长度是两个完全独立的概念,二者没有必然关联,具体原因如下:
1. C# char类型的实际内存占用
C#的char是值类型,用于表示一个UTF-16编码的Unicode字符,在内存中固定占用2字节,因此sizeof(char)返回2是完全正确的,该值是char类型的固有属性,不受其他业务逻辑影响。
2. BinaryWriter写char仅输出1字节的原因
BinaryWriter.Write(char)方法默认采用UTF-8编码对字符做序列化后写入流:
- 本次测试使用的字符
'w'属于ASCII字符集范围,UTF-8编码下刚好只占1字节,因此写入后文件长度为1、流位置差值为1 - 如果写入中文等超出ASCII范围的字符,序列化后的长度可能为2、3甚至4字节,完全由UTF-8编码规则决定,和char本身的2字节内存占用无关
3. int类型三者数值一致的原因
BinaryWriter.Write(int)方法是直接将Int32的4字节原始二进制数据写入流,没有经过文本编码转换,所以序列化后的长度和int类型本身的内存占用完全一致,因此三个数值相等。
对齐测试结果的方法
如果想要让BinaryWriter写入char时输出和内存占用一致的2字节UTF-16编码,只需要在构造BinaryWriter时显式指定编码为Unicode即可:
// 构造时指定使用UTF-16编码序列化字符 BinaryWriter bwSave = new BinaryWriter(fsSave, Encoding.Unicode);
修改后再运行测试,流位置差值、文件长度都会变为2,和sizeof(char)的返回值完全匹配。
内容的提问来源于stack exchange,提问作者wpumain
相关产品推荐
相关产品推荐

