二进制文件读写(写给新手)FortranFortran
一).一般问题 另外推荐一款使用较多的软件,叫做 UltraEdit(以下简称 UE)。是很不错的文本编辑器,也能做十六进制编辑器使用。 为什么要用十六进制编辑器?而不用 2 进制呢?因为 2 进制实在太小,书写起来会很长,很不直观。而我们的计算机把 8 位作为一个字节。刚好 2 ** 8 = 256 = 16 ** 2。用 8 位 2 进制表达的数,我们用 2 个十六进制数据来表达,更直观和方便。 二).文件格式 所有文件,笼统意义上将可以区分为两类,一类是文本文件,一类是二进制文件 1).文本文件 文本文件用记事本等文本编辑器打开,我们可以看懂上面的信息。所以使用比较广泛。通常一个文本文件分为很多很多行,作为数据储存时,还有列的概念。实际上,储存在硬盘或其他介质上,文件内容是线一样储存的,列是用空格或 Tab 间隔,行是用回车和换行符间隔。 以 ANSI 编码(使用较多)的文本文件来说,例如我们储存如下信息: 10 11 12 因此,这个数据储存是这样的: 31 30 0D 0A (红色为回车符和换行符) 31h 30h 就是 10,31h 31h 就是 11,31h 32h 就是 12。因此我们也可以认为文本文件是特殊的二进制文件 2).二进制文件 二进制文件 从数据类型上来说,我们首先考虑整型。如果把 10 11 12 当作 2 字长的整型。则 10 表示为:0Ah 00h。因为 0Ah 对应十进制 10。而后面的 00h 是空白位。2 字长的整型如果不足 FFh,也就是不足 255,则需要一个空白位。类似的:11 表示为 0Bh 00h,12 表示为 0Ch 00h。 当整型数据超过 255 时,我们需要 2 个字节来储存。比如 2748(ABCh),则表示为:BCh 0Ah。要把低位写在前面(BCh),高位写在后面(0Ah)。 当整型数据超过 65535 时,我们就需要 4 个字节来储存。比如 439041101(1A2B3C4Dh),则表示成:4Dh 3Ch 2Bh 1Ah。当数据再大时,我们就需要 8 字节储存了。 二进制文件 二进制文件 三).使用二进制文件 为什么要使用二进制文件 第一是二进制文件 第二个原因是,内存中参加计算的数据都是用二进制无格式储存起来的,因此,使用二进制储存到文件就更快捷。如果储存为文本文件,则需要一个转换的过程。在数据量很大的时候,两者就会有明显的速度差别了。 第三,就是一些比较精确的数据,使用二进制储存不会造成有效位的丢失。 四).二进制文件 列举一个二进制文件 00000000h: 0F 01 00 00 0F 03 00 00 12 53 21 45 58 62 35 34 00000010h: 41 42 43 44 45 46 47 48 49 47 4B 4C 4D 4E 4F 50 这里列出的是在 UltraEdit(UE) 里看到的东西。其实只有红色部分是文件内容。前面的是 UE 加入的行号。后面的是 UE 尝试解释为字符型的参考。 这个文件一共有 32 字节长。显示为两列,每列 16 个字节。实际上,这仅仅是 UE 的显示而已。真实的文件并不分行。仅仅知道这个文件的内容,如果我们没有任何说明的话,是不能看出任何有用信息的。 下面我规定一下说明:我们认为,前 4 个字节是一个 4 字节的整型数据(0F 01 00 00 十六进制:10Fh 十进制:271)。这 4 个字节之后的 4 个字节是另一个 4 字节的整型数据(0F 03 00 00 十六进制:30Fh 十进制:783)。其后的 4 个字节(12 53 21 45 )表示一个 4 字节的实型数据:2.5811919E+3。再其后的 4 个字节(58 62 35 34)表示另一个 4 字节的实行数据:1.6892716E-7。而只后的 16 个字节(41 42 43 44 45 46 47 48 49 47 4B 4C 4D 4E 4F 50)我们认为是 16 个字节的字符串(ABCDEFGHIGKLMNOP) 实际上,二进制文件 因此,面对一个二进制文件 五).如何使用语句操作二进制文件 我们将上面的那个二进制文件 读取和写入二进制其实是两个很类似的操作,了解了其中之一,另一个也就不难了。 二进制文件 Open( 12 , File = 'TestBin.Bin' , Access = 'Direct' , Form = 'Unformatted' , RecL = 4 ) 上面的 Access 表示直接读取方式,Form 表示无格式储存。比较重要的是 RecL 。我们读取数据时,是用记录来描述单位的,每一次读入或写入是一个记录。记录的长度在 Open 时就确定下来,以后不能改变。如果需要改变,只能 Close 以后再此 Open。 记录长度在某些编译器下表示读取的 4 字节长度的倍数,规定为 4 表示记录长度为 16 字节。有些编译器下就直接表示记录的字节数,规定为 4 则表示记录长度为 4 字节。这个问题需要参考编译器手册。在 VF 系列里,这个值是前面一个含义。可以通过设置工程属性的 Fortran 确定 RecL 大小是我们需要做的事情,一般来说,不适合太大,也不适合太小。还需要结合数据储存方式来考虑。太小的话,我们需要执行读写的次数就多,太大的话,我们就不方便操作小范围的数据。 有时候我们甚至会分多次来读取数据,每一次的 RecL 都不同。对于上面的 TestBin.Bin 文件来说,它比较简单,我以 16 字节长度和 8 字节长度两种读取方式来演示,你甚至可以一次 32 个字节长度全部读完: (1)RecL = 4 【记录长度 16 字节】 Program main Implicit None Integer*4 :: iVar1 , iVar2 Real*4 :: rVar1 , rVar2 Character(Len=16) :: cStr Open( 12 , File = 'TestBin.Bin' , Access = 'Direct' , Form = 'Unformatted' , RecL = 4 ) Read( 12 , Rec = 2 ) cStr Read( 12 , Rec = 1 ) iVar1 , iVar2 , rVar1 , rVar2 Write( * , * ) cStr Write( * , * ) iVar1 , iVar2 , rVar1 , rVar2 Close( 12 ) End Program main 这里的 Open 里指定了 RecL = 4(记录长度是 16 字节)。 第一个 Read 语句,直接读取第二笔记录(也就是第 17 字节到第 32 字节)。读取出的 cStr = "ABCDEFGHIGKLMNOP"。 第二个 Read 语句,返回来读取第一笔记录(也就是前面 16 个字节)。读取出的数据分别放入 4 个 4 字节的变量。(其中前面两个是整型,后面两个是实型) 输出结果为: ABCDEFGHIGKLMNOP 271 783 2581.192 1.6892716E-07 看到这个结果,就说明我们成功了。 同时我们可以看到,第一个语句,我们直接跳到第二条记录读取,并没有读取第一条。这就是直接读取数据的方便。有时候我们根本不需要某些数据,这时候,我们可以直接跳到某一条记录上。这个记录甚至可以是我们实现算出来的变量。比如: iRec = ( a + b ) / C Read( 12 , Rec = iRec ) cStr 实现我们储存了 100 天的数据,我们只需要第 21 天的数据,我们怎么办?在顺序读取时,我们可能会开辟一个 100 元素的数组,或者循环执行 20 次空白的读取。但是在直接读取时,我们只需要执行一句 Read( 12 , Rec = 21 )。这是多么的方便。(直接读取和顺序读取虽然于文本文件和二进制文件 (2)RecL = 2【记录长度为 8 字节】 Program main Implicit None Integer*4 :: iVar1 , iVar2 Real*4 :: rVar1 , rVar2 Character(Len=16) :: cStr Open( 12 , File = 'TestBin.Bin' , Access = 'Direct' , Form = 'Unformatted' , RecL = 2 ) Read( 12 , Rec = 4 ) cStr( 9 : 16 ) Read( 12 , Rec = 3 ) cStr( 1 : 8 ) Read( 12 , Rec = 1 ) iVar1 , iVar2 Read( 12 , Rec = 2 ) rVar1 , rVar2 Write( * , * ) cStr Write( * , * ) iVar1 , iVar2 , rVar1 , rVar2 Close( 12 ) End Program main 这里设定的 RecL = 2 ,意思是一笔记录 8 个字节。所以我们不能一次读取 cStr 这个 16 字节的字符串。我们必须分两次读取。第一次读取第 4 笔记录,放入字符串后半段。第二次读取第 3 笔记录,放入字符串前半段。(可以调换位置)。然后读取第一笔记录的两个整型变量和第二笔记录的两个实型变量。 输出结果和(1)的方法一样。 (3)写入二进制文件 Program main Implicit None Open( 12 , File = 'TestBinW.Bin' , Access = 'Direct' , Form = 'Unformatted' , RecL = 4 ) Write( 12 , Rec = 1 ) 271 , 783 , 2581.192_4 , 1.6892716E-07 Write( 12 , Rec = 2 ) "ABCDEFGHIGKLMNOP" Close( 12 ) End Program main 写入二进制文件 二进制文件 |
|