一:背景
话说昨天我居然奇迹般的去上C#课(老师是个美女,大家懂得....),感觉好久没去上她的课了,搞得自己都不好意思了,正好趁这个机会去看看她(不要多想哦,亲)。在课上,老师讲到个文件流的读写的例子,有一段代码引起了我的兴趣,以为是一个很简单的问题,但是运行的结果去让我很吃惊,我顿时有种冲动,想说一句:我靠,这啥玩意!哈哈 ,扯淡了,不多说了,下面我来还原下现场的情形:
首先是把 0 ~19 20个(Int32类型,4个字节)的数字以二进制的形式写到一个文本文件中,我这测试用例为 F: test.txt ;
demo:
static void Main(string[] args)
{
//写入数据
string filePath;
Console.WriteLine("请输入需要创建的二进制文件路径(包括文件名)");
filePath = Console.ReadLine();
FileStream fileStream = new FileStream(filePath, FileMode.Create);
BinaryWriter writer = new BinaryWriter(fileStream);
for (int index = 0; index < 20; index++)
{
writer.Write(index);
}
Console.WriteLine("写入完毕");
writer.Close();
fileStream.Close();
}
接着以每2个字节(即Int16类型)来读取刚刚写入到 F:test.txt 的二进制文本文件。
Demo:
//读入数据
Console.WriteLine("请输入文件名:");
string filePath = Console.ReadLine();
if (!File.Exists(filePath))
{
Console.WriteLine("文件不存在。");
}
else
{
FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
BinaryReader reader = new BinaryReader(fileStream);
try
{
while (true)
{
Console.WriteLine(reader.ReadInt16());
}
}
catch (EndOfStreamException)
{
Console.WriteLine("已经到文件尾部了");
}
finally
{
reader.Close();
fileStream.Close();
Console.ReadKey();
}
}
二:问题
上面的二段代码理解起来基本没有什么问题,按照我的常理:因为开始的时候是按每4个字节进行存储的,那么数据的存储顺序应该如下所示:
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 00000000 00000000 00000000 00000010 ......
如果按照每2个字节进行读取的话,那么结果应该是:
0 0 0 1 0 2 ......
但是结果呢?
是不是很吃惊呢?为啥会出现这种情况?这货是啥玩意?
三:解析
于是,我很困惑,非常的困惑,碰到问题,就要解决问题,这是作为软件工程师最基本的要求,在网上找了很久,看的眼睛都花了,最后去CSDN论坛里问了下,坛子了说这与字节序有关,具体什么没有详说,于是拿出我的秘密武器:google. 当看到了字节序的有关概念,了解到数据在内存的排列方式后,这个问题就有解了。于是乎就有了这篇博文,下面我来解解这个问题。
解:(先写个大的解字,哈哈)
在设计计算机系统的时候,有两种处理内存中数据的方法。一种叫为Little-Endian(小端),存放在内存中最低位的数值是来自数据的最右边部分(也就是数据的最低位部分)。与之相对于的是Big-Endian(大端),正好相反,存放在内存中最低位的数值是来自数据的最左边边部分(也就是数据的最高为部分),BIG-ENDIAN、LITTLE-ENDIAN、跟CPU有关的,每一种CPU不是BIG-ENDIAN就是LITTLE-ENDIAN、。IA架构(Intel、AMD)的CPU中是Little-Endian,而PowerPC 、SPARC和Motorola处理器是Big-Endian。这其实就是所谓的主机字节序。大部分的人使用的是IA架构的计算机,包括笔者我的本本。可能对于这样的解释有些人还是有些迷惑,下面就以Int32类型的 数字1 为列,分别以Little-Endian 和Big-Endian 来介绍其在内存中的存储顺序。
Little-Endian:将低序字节存储在起始地址
int32类型的数字1其二进制表示为:
-----数值高位----- -----数值低位-----
00000000 00000000 00000000 00000001
则其按照Little-Endian 方式进行数据存储的话,则其在内存中的排列方式为:
地址低位 地址高位
--------------------------------------->地址逐渐增大
00000000 000000001 00000000 00000000
-----数值低位------ ------数值高位----
即数值在内存中的存储顺序与其二进制表示有点不同,所有如果我按照每2个字节(即int16类型)进行读取的话,取到的数据应该是 1 0 ,正好与结果相吻合。至于之所以这样设计是为了方便计算机处理数据,提供效率。我的机器就是以Little-Endian方式对数据进行存储的。
Big-Endian: 将高序字节存储在起始地址
理解了Little-Endian 的排列方式,那么Big-Endian就不难理解了。下为int32类型的数字1在内存中以Big-Endian方式进行排列图:
地址低位 地址高位
---------------------------------------->地址逐渐增大
00000000 00000000 00000000 00000001
----数值高位------ ------数值低位------
从中可以看出,按照Big-Endian方式进行数据存储的话,其在内存中的排列方式与其二进制格式表示是一致的。估计大部分比较容易接受这种方式。
以上仅为个人感想,如有谬误之处,还望指正。