二进制和文本文件的比较

一).一般问题
  二进制文件与我们通常使用的文本文件储存方 式有根本的不同。这样的不同很难用言语表达,自己亲自看一看,理解起来会容易得多。因此,我推荐学习二进制文件读写的朋友安装一款十六进制编辑器。这样的 编辑器有很多,在我们的 CVF 附带的集成开发环境下就可以(将二进制文件拖动到 IDE 窗口后松开)。Visual Studio 2005 也是 可以的。(不过需要在 File 菜单下 Open,File)
  另外推荐一款使用较多的软件,叫做 UltraEdit(以下简称 UE)。是很不错的文本编辑器,也能做十六进制编辑器使用。
   为什么要用十六进制编辑器?而不用 进制呢?因为 进制实在太小,书写起来会很长,很不直观。而我们的计算机把 位作为一个字节。刚 好 ** 256 16 ** 2。用 位 进制表达的数,我们用 个十六进制数据来表达,更直观和方便。

二).文件格式
  所有文件,笼统意义上将可以区分为两类,一类是文本文件,一类是二进制文件。

 1).文本文件
  文本文件用记事本等文本编辑器打开,我们可以看懂上面的信息。所以使用比较广泛。通常一个文本文件分为很多很多行,作为数据储存时,还有列的概念。实际上,储存在硬盘或其他介质上,文件内容是线一样储存的,列是用空格或 Tab 间隔,行是用回车和换行符间隔。
  以 ANSI 编码(使用较多)的文本文件来说,例如我们储存如下信息:
引用
10
11
12

  需要的空间是:3 行 × 每 行 个字符 个回车符 个换行符 10 字节。文本文件储存数据是有格式,无数据类型的。比如 10 这个数据,并不指定是整型 还是实型还是字符串。它有长度,就是 2,两个字节。储存时计算机储存它的 ASCII 码:31h,30h。(十六进制表示)。回车符是:0Dh,换行 符:0Ah。
    因此,这个数据储存是这样的:
引用
31 30 0D 0A 31 31 0D 0A 31 32

  (红色为回车符和换行符) 31h 30h 就是 10,31h 31h 就是 11,31h 32h 就是 12。因此我们也可以认为文本文件是特殊的二进制文件。
 2).二进制文件
  二进制文件,是无格式有数据类型的。比如上面的 10 11 12 三个数。但二进制文件没有行的概念。我们要紧凑地储存他们。(当然也可以中间加入一些空白的字节)
   从数据类型上来说,我们首先考虑整型。如果把 10 11 12 当作 字长的整型。则 10 表示为:0Ah 00h。因为 0Ah 对应十进 制 10。而后面的 00h 是空白位。2 字长的整型如果不足 FFh,也就是不足 255,则需要一个空白位。类似的:11 表示 为 0Bh 00h,12 表示为 0Ch 00h。
  当整型数据超过 255 时,我们需要 个字节来储存。比如 2748(ABCh),则表示为:BCh 0Ah。要把低位写在前面(BCh),高位写在后面(0Ah)。
  当整型数据超过 65535 时,我们就需要 个字节来储存。比如 439041101(1A2B3C4Dh),则表示成:4Dh 3Ch 2Bh 1Ah。当数据再大时,我们就需要 字节储存了。
   二进制文件的实型数据也有字节长度的区分,比如 字长,8 字长。但实型数据的长度并不仅仅代表它的表达的范围,更多的代表精度。所以,8 字长的 我们又称为双精度。关于实型数据如何储存为 进制。则有很多套规则。现在都广泛使用的是 IEEE 标准浮点格式。关于这样的规则,我还正在了解,比 较麻烦。就不多说了。在这里也没有必要了解。
  二进制文件也可以储存字符型数据,储存方法和文本文件一样。都是使用 ASCII 编码储存的。所以我们用记事本打开某些二进制文件时,也能看到一些有意义的字符串。(无意义的乱码我们可以认为是整型或实型,不过记事本程序当作字符来解释,因此造成了乱码)

三).使用二进制文件的好处
  为什么要使用二进制文件。原因大概有三个:
   第一是二进制文件比较节约空间,这两者储存字符型数据时并没有差别。但是在储存数字,特别是实型数字时,二进制更节省空间,比如储存 Real*4 的 数据:3.1415927,文本文件需要 个字节,分别储存:3 这 个 ASCII 值,而二进制文件只需 要 个字节(DB 0F 49 40)
  第二个原因是,内存中参加计算的数据都是用二进制无格式储存起来的,因此,使用二进制储存到文件就更快捷。如果储存为文本文件,则需要一个转换的过程。在数据量很大的时候,两者就会有明显的速度差别了。
  第三,就是一些比较精确的数据,使用二进制储存不会造成有效位的丢失。

四).二进制文件的储存方式
    列举一个二进制文件如下:
引用
00000000h: 0F 01 00 00 0F 03 00 00 12 53 21 45 58 62 35 34 .........S!EXb54
00000010h: 41 42 43 44 45 46 47 48 49 47 4B 4C 4D 4E 4F 50 ABCDEFGHIGKLMNOP

  这里列出的是在 UltraEdit(UE) 里看到的东西。其实只有红色部分是文件内容。前面的是 UE 加入的行号。后面的是 UE 尝试解释为字符型的参考。
  这个文件一共有 32 字节长。显示为两列,每列 16 个字节。实际上,这仅仅是 UE 的显示而已。真实的文件并不分行。仅仅知道这个文件的内容,如果我们没有任何说明的话,是不能看出任何有用信息的。
   下面我规定一下说明:我们认为,前 个字节是一个 字节的整型数据(0F 01 00 00 十六进制:10Fh 十进制:271)。 这 个字节之后的 个字节是另一个 字节的整型数据(0F 03 00 00 十六进制:30Fh 十进制:783)。其后的 个字节 (12 53 21 45 )表示一个 字节的实型数据:2.5811919E+3。再其后的 个字节(58 62 35 34)表示另一 个 字节的实行数据:1.6892716E-7。而只后的 16 个字节 (41 42 43 44 45 46 47 48 49 47 4B 4C 4D 4E 4F 50)我们认为是 16 个字节的字符串 (ABCDEFGHIGKLMNOP)
  实际上,二进制文件只是储存数据,并不写明数据类型,比如上面的第 字节到第 16 字节 (12 53 21 45 58 62 35 34),我们刚才认为是 个 字节的实型,其实也可以认为是 个字节的字符型( S!EXb54)。而后面的 16 个字节的字符串(ABCDEFGHIGKLMNOP),我们也可以认为是 个 字节的整型,或 者 个 字节的整型,甚至 个 字节的实型,4 个 字节的实型,等等等等。
  因此,面对一个二进制文件,我们不能准确地知道它的含义,我们需要他的数据储存方式的说明。这个说明告诉我们第几个字节到第几个字节是什么类型的数据,储存的数据是什么含义。否则的话,我们只能猜测,或者无能为力。

五).如何使用语句操作二进制文件
  我们将上面的那个二进制文件保存为:TestBin.Bin 来举例。
  读取和写入二进制其实是两个很类似的操作,了解了其中之一,另一个也就不难了。
  二进制文件我们通常使用直接读取方式,Open 语句可以写为:
引用
Open( 12 File 'TestBin.Bin' Access 'Direct' Form 'Unformatted' RecL )

   上面的 Access 表示直接读取方式,Form 表示无格式储存。比较重要的是 RecL 。我们读取数据时,是用记录来描述单位的,每一次读入或 写入是一个记录。记录的长度在 Open 时就确定下来,以后不能改变。如果需要改变,只能 Close 以后再此 Open。
  记录长度在某 些编译器下表示读取的 字节长度的倍数,规定为 表示记录长度为 16 字节。有些编译器下就直接表示记录的字节数,规定为 则表示记录长度 为 字节。这个问题需要参考编译器手册。在 VF 系列里,这个值是前面一个含义。可以通过设置工程属性 的 Fortran,Data,Use Bytes as RECL= Unit for Unformatted Files 来改变,使之成为后一个 含义。在命令行模式下,则使用 /assume:byterecl 这个编译选项。
  确定 RecL 大小是我们需要做的事情,一般来说,不适合太大,也不适合太小。还需要结合数据储存方式来考虑。太小的话,我们需要执行读写的次数就多,太大的话,我们就不方便操作小范围的数据。
  有时候我们甚至会分多次来读取数据,每一次的 RecL 都不同。对于上面的 TestBin.Bin 文件来说,它比较简单,我以 16 字节长度和 字节长度两种读取方式来演示,你甚至可以一次 32 个字节长度全部读完:
  (1)RecL 【记录长度 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 )
  Read( 12 Rec cStr
  Read( 12 Rec 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 个字节)。读取出的数据分别放入 个 字节的变量。(其中前面两个是整型,后面两个是实型)
输出结果为:
 ABCDEFGHIGKLMNOP
         271         783   2581.192      1.6892716E-07
  看到这个结果,就说明我们成功了。
  同时我们可以看到,第一个语句,我们直接跳到第二条记录读取,并没有读取第一条。这就是直接读取数据的方便。有时候我们根本不需要某些数据,这时候,我们可以直接跳到某一条记录上。这个记录甚至可以是我们实现算出来的变量。比如:
  iRec C
    Read( 12 Rec iRec cStr
    实 现我们储存了 100 天的数据,我们只需要第 21 天的数据,我们怎么办?在顺序读取时,我们可能会开辟一个 100 元素的数组,或者循环执 行 20 次空白的读取。但是在直接读取时,我们只需要执行一句 Read( 12 Rec 21 )。这是多么的方便。(直接读取和顺序读取虽 然于文本文件和二进制文件没有直接的关联,但是文本文件通常用顺序读取,而二进制文件通常用直接读取。这是他们的性质决定的。)
    (2)RecL 2【记录长度为 字节】
引用
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 )
  Read( 12 Rec cStr( 16 )
  Read( 12 Rec cStr(  )
  Read( 12 Rec iVar1 iVar2 
  Read( 12 Rec rVar1 rVar2
  Write( cStr
  Write( iVar1 iVar2 rVar1 rVar2
  Close( 12 )
End Program main
    
   这里设定的 RecL ,意思是一笔记录 个字节。所以我们不能一次读取 cStr 这个 16 字节的字符串。我们必须分两次读取。第一 次读取第 笔记录,放入字符串后半段。第二次读取第 笔记录,放入字符串前半段。(可以调换位置)。然后读取第一笔记录的两个整型变量和第二笔记 录的两个实型变量。
  输出结果和(1)的方法一样。
  (3)写入二进制文件
  写入二进制文件同样需要考虑 RecL 的问题。我们这里以 RecL 来举例。
引用
Program main
  Implicit None
  Open( 12 File 'TestBinW.Bin' Access 'Direct' Form 'Unformatted' RecL )
  Write( 12 Rec 271 783 2581.192_4 1.6892716E-07
  Write( 12 Rec "ABCDEFGHIGKLMNOP"
  Close( 12 )
End Program main



  写入二进制文件和读取二进制文件是差不多的,我就不再解释了。需要注意的是,如果直接写入第 笔记录,而文件没有只有 笔记录(M N),那么,第 M+1 到第 N-1 笔记录会用 填充。也就是说,二进制文件不会出现断裂。

  二进制文件的读写是比较灵活的,实际应用中,我们使用哪种方式,我们应该根据自己的情况来设计。如何选择合适的记录长度 RecL,如何设计高效的储存方式等。

 

 

 

1、二进制文件是把内存中的数据按其在内存中的存储形式原样输出到磁盘上存放,也就是说存放的是数据的原形式。


2、文本文件是把数据的终端形式的二进制数据输出到磁盘上存放,也就是说存放的是数据的终端形式。

 


 

我们有必要把需要存储的数据分为字符数据和非字符数据两类。当你有数据要存储的时候.首先要考虑的问题并不是你要选择用二进制文件还是文本文件来进行存储,而是首先得考虑你要存储的数据是字符数据还是非字符数据.在此基础上再讨论应该选择用什么文件进行存储为好。

一、如果要存储字符数据,无论是放在文本文件还是放在二进制文件中都和内存中的数据形式是没有区别的.同样也和终端形式没有区别。那么在存储和显示的特性上也没有任何区别,不浪费存储空间也不浪费转换时间。所以如果一个文件只存放字符数据,那么讨论该文件是用文本文件或是二进制文件是没有任何意义的。

二、如果要存储非字符数据,则情况要复杂一些。

1、如果您需要频繁地保存和访问数据.那么应该采用二进制文件进行存放,这样可以节省存储空间和转换时间。

2、如果您需要频繁地向终端显示数据或从终端读人数据,那么应该采用文本文件进行存放,这样可以节省转换时间。

三、如果要存储的数据中既有字符数据又有非字符数据那么要怎么办呢?那就要综合上述两点进行权衡以找到最佳平衡点了

总结:字符数据本身在内存中就经过了编码,所以无论是二进制还是文本形式都是一样的,而对于非字符数据来说,例如inti=10;如果用二进制来进行存储的话为1010,但是如果需要用文本形式来进行存储的话就必须进行格式化编码(对1和0分别编码,即形式为‘1’和‘0’分别对应的码值)。


posted @ 2013-06-23 14:40  ~风~  阅读(1356)  评论(0编辑  收藏  举报