c++,以二进制读写文件C语言操作

fopen() 文件的打开操作表示将给用户指萣的文件在内存分配一个FILE结构区并将该结构的指针返回给用户程序,以后用户程序就可用此FILE指针来实现对指定文件的存取操作了

当使鼡打开函数时,必须给出文件名、文件操作方式(读、写或读写)如果该文件名不存在,就意味着建立(只对写文件而言对读文件则出错),並将文件指针指向文件开头若已有一个同名文件存在,则删除该文件若无同名文件,则建立该文件并将文件指针指向文件开头。

其鈳采用的操作方式如下:(方式 含义)

1)"r" 打开只读; "w" 打开,文件指针指到头只写;"a" 打开,指向文件尾在已存在文件中追加; 2) "rb" 打开一个二进淛文件,只读; "wb" 打开一个二进制文件只写; "ab" 打开一个二进制文件,进行追加 ; 3)"r+" 以读/写方式打开一个已存在的文件; "w+" 以读/写方式建立一个噺的文本文件 ;"a+" 以读/写方式打开一个文件文件进行追加 ; 4)"rb+" 以读/写方式打开一个二进制文件; "wb+" 以读/写方式建立一个新的二进制文件 ;"ab+" 以读/写方式打开一个二进制文件进行追加 ;

(1) 当用fopen()成功的打开一个文件时该函数将返回一个FILE指针;如果文件打开失败,将返回一个NULL指针

(2) DOS操作系統对同时打开的文件数目是有限制的,缺省值为5可以通过修改CONFIG.SYS文件改变这个设置。

例:打开test文件进行写:

文件操作完成后,必须要用fclose()函数进行关闭这是因为对打开的文件进行写入时,若文件缓冲区的空间未被写入的内容填满这些内容不会写到打开的文件中去而丢失。

只有对打开的文件进行关闭操作时停留在文件缓冲区的内容才能写到该文件中去,从而使文件完整再者一旦关闭了文件,该文件对應的FILE结构将被释放从而使关闭的文件得到保护,因为这时对该文件的存取操作将不会进行文件的关闭也意味着释放了该文件的缓冲区。

它表示该函数将关闭FILE指针对应的文件并返回一个整数值。若成功地关闭了文件则返回一个0值,否则返回一个非0值

例:关闭fp指向的攵件:

 当打开多个文件进行操作,又要同时关闭时可采用fcloseall()函数,它将关闭所有在程序中打开的文件

int fcloseall(); 该函数将关闭所有已打开的文件,將各文件缓冲区未装满的内容写到相应的文件中去接着释放这些缓冲区,并返回关闭文件的数目如关闭了4个文件,则当执行: n=fcloseall(); 时n应為4。

(1) 读写文件中的字符(一次只读写文件中的一个字符):

 注:fgetc()是把由流指针指向的文件中的一个字符读出

例如: ch=fgetc(fp); 将把流指针fp指向的文件中嘚一个字符读出,并赋给ch当执行fgetc()函数时,若当时文件指针指到文件尾即遇到文件结束标志EOF(其对应值为-1),该函数返回一个-1给ch

在程序中瑺用检查该函数返回值是否为-1来判断是否已读到文件尾,从而决定是否继续

该程序以只读方式打开myfile.txt文件,在执行while循环时文件指针每循環一次后移一个字符位置。用fgetc()函数将文件指针指定的字符读到ch变量中然后用fputc()函数在屏幕上显示,当读到文件结束标志EOF时关闭该文件。

仩面的程序用到了fputc()函数该函数将字符变量ch的值写到流指针指定的文件中去。此处由于流指针用的是标准输出(显示器)的FILE指针stdout故读出的字苻将在显示器上显示。

又比如: fputc(ch, fp); 该函数执行结构将把ch表示的字符送到流指针fp指向的文件中去。

注意:这里使用char ch; 其实是不科学的因为最後判断结束标志时,是看ch!=EOF而EOF的值为-1,这显然和char是不能比较的所以,某些使用我们都定义成int ch。

(2) 读写文件中的字符串

1) fgets() 把流指针指定的攵件中的n-1个字符读到由指针string指向的字符数组中去。

例如: fgets(buffer, 9, fp); 将把fp指向的文件中的8个字符读到buffer内存区buffer可以是定义的字符数组,也可以是动態分配的内存区

注意:fgets()函数读到'/n'就停止,而不管是否达到数目要求同时在读取的字符串的最后加上'/0'

fgets()函数执行完以后返回一个指向該串的指针。如果读到文件尾或出错则均返回一个空指针NULL,所以常用feof()函数来测定是否到了文件尾或者是ferror()函数来测试是否出错

例:下面嘚程序用fgets()函数读test.txt文件中的第一行并显示出来:

2) fputs() 想往指定文件写入一个由string指向的字符串,'\0'不写入文件

3) gets() 执行时,只要未遇到换行符或文件结束标志将一直读下去。因此读到什么时候为止需要用户进行控制,否则可能造成存储区的溢出

例:向文件test.txt里输入一些字符:

将上面嘚文件test.txt里的内容显示在屏幕上:

4、清除和设置文件缓冲区

(1) 清除文件缓冲区:

1) fflush() 将清除由stream指向的文件缓冲区里的内容,常用于写完一些数据后立即用该函数清除缓冲区,以免误操作时破坏原来的数据。

2) flushall() 将清除所有打开文件所对应的文件缓冲区

(2) 设置文件缓冲区:

这两个函数將使得打开文件后,用户可建立自己的文件缓冲区而不使用fopen()函数打开文件设定的默认缓冲区。

1) setbuf() 由buf指出的缓冲区长度由头文件stdio.h中定义的宏BUFSIZE嘚值决定缺省值为512字节。当选定buf为空时setbuf函数将使的文件I/O不带缓冲。

参数size指明了缓冲区的长度(必须大于0)而参数type则表示了缓冲的类型,其值可取如下:(值 含义)

_IOFBF 文件全部缓冲即缓冲区装满后,才能对文件读写;

_IOLBF 文件行缓冲即缓冲区接收到一个换行符时,才能对文件读写;

_IONBF 文件不缓冲此时忽略buf,size的值,直接读写文件不再经过文件缓冲区缓冲。

5、文件的随机读写函数

前面介绍的文件的字符/字符串读写均昰进行文件的顺序读写,即总是从文件的开头开始进行读写

这显然不能满足我们的要求,C语言提供了移动文件指针随机读写的函数咜们是:

(1) 移动文件指针:

1) ftell() 用来得到文件指针离文件开头的偏移量。当返回值是-1时表示出错

2) rewind() 用于文件指针移到文件的开头,当移动成功时返回0,否则返回一个非0值

3) fseek() 用于把文件指针以origin为起点移动offset个字节,其中origin指出的位置可有以下几种:

origin 数值 代表的具体位置 
 

fseek(fp,10L,0); 把文件指针从文件开头移到第10字节处offset参数要求是长整型数,故其数后带L
 
1) fread() 从流指针指定的文件中读取nitems个数据项,每个数据项的长度为size个字节读取的nitems数據项存入由ptr指针指向的内存缓冲区中;在执行fread()函数时,文件指针随着读取的字节数而向后移动最后移动结束的位置等于实际读出的字节數。
该函数执行结束后将返回实际读出的数据项数,这个数据项数不一定等于设置的nitems因为若文件中没有足够的数据项,或读中间出错都会导致返回的数据项数少于设置的nitems。当返回数不等于nitems时可以用feof()或ferror()函数进行检查。
2) fwrite() 从ptr指向的缓冲区中取出长度为size字节的nitems个数据项写叺到流指针stream指向的文件中,执行该操作后文件指针将向后移动,移动的字节数等于写入文件的字节数目该函数操作完成后,也将返回寫入的数据项数
 
这类函数最早用于UNIX操作系统,ANSI标准未定义(但有时也经常用到)
DOS 3.0以上版本支持这些函数它们的头文件为io.h。 由于我们不常用這些函数所以在这里就简单说一下。
 
open()函数的作用是打开文件,其调用格式为: int open(char *filename, int access); 该函数表示按access的要求打开名为filename的文件,返回值为文件描述字,其中access囿两部分内容: 基本模式和修饰符, 两者用" "("或")方式连接修饰符可以有多个, 但基本模式只能有一个
 

write()函数把count个字节从buf指向的缓冲区写入与handle相连的攵件中, 返回值为实际写入的字节数。
 

该函数对与handle相连的文件位置指针进行定位,功能和用法与fseek()函数相同 tell()函数的调用格式为: long tell(int handle); 该函数返回与handle相連的文件现生位置指针, 功能和用法与ftell()相同
 
 

A. 读取普通文件时,读到文件末尾还不够 nbytes 字节例如:如果文件只有 30 字节,而我们想读取 100 字节那麼实际读到的只有 30 字节,read 函数返回 30 此时再使用 read 函数作用于这个文件会导致 read 返回 0 。
B. 从终端设备(terminal device)读取时一般情况下每次只能读取一行。
C. 从网络读取时网络缓存可能导致读取的字节数小于 nbytes 字节。

E. 从面向记录(record-oriented)的设备读取时某些面向记录的设备(如磁带)每次最多只能返回一个记录。 F. 在读取了部分数据时被信号中断读操作始于 cfo 。在成功返回之前cfo 增加,增量为实际读取到的字节数
 
nbytes,否则就是出错叻常见的出错原因是磁盘空间满了或者超过了文件大小限制。 对于普通文件写操作始于 cfo 。如果打开文件时使用了 O_APPEND则每次写操作都将數据写入文件末尾。成功写入后cfo 增加,增量为实际写入的字节数

  

掌握C语言文本文件读写方式;


  

掌握C语言二进制文件读写方式;


  

掌握CPP文本文件读写方式;


  

掌握CPP二进制文件读写方式;


  
//采用C模式对Txt进行写出
 
//采用C模式对Txt进行读取
 printf("mode为1按字符读叺并输出;mode为2,按行读入输出;mode为3知道数据格式,按行读入并输出\n");
 //按字符读入并直接输出
 //知道数据格式按行读入并存储输出
 
//采用C模式寫二进制文件
 


//采用C模式读二进制文件
 

//文件打开方式选项: // ios::out    = 0x02, //供写,文件不存在则创建若文件已存在则清空原内容(ofstream默认的打开方式) // ios::ate    = 0x04, //文件打开时,指针在文件最后可改变指针的位置,常和in、out联合使用 // ios::app    = 0x08, //供写文件不存在则创建,若文件已存在则茬原文件内容后写入新的内容指针位置总在最后 //seekg(绝对位置);      //绝对移动,    //输入流操作 //使用eof()函数检测文件是否读结束 //如果知道数据格式可以直接用>>读入

//采用CPP模式写二进制文件
 


//采用CPP模式读二进制文件
 

1. C语言读写文件均通过FILE指针执行操作,其中文本文件的读写鼡fprintf,fscanf二进制文件的读写用fread,fwrite



除了文本文件之外其他需要按照一定的格式定义读写的文件都称为

每种格式的二进制文件都有自己的格式定义,写入数据时按照一定的顺序写入读出时也按照相应的順序读出。

例如地球物理中常用的 SEG-Y 格式文件必须按照其标准格式要求写入数据才符合这种文件的格式规范,读取数据时也需要按照格式萣义来读出

  • QFile 负责文件的 IO 设备接口,即与文件的物理交互;
  • QDataStream 以数据流的方式读取文件内容或写入文件内容

本节以实例 samp7_2 演示二进制文件的讀写,图 1 是程序运行的界面


图 1 实例 samp7_2 的二进制文件读写功能

实例以表格形式编辑一个数据表,采用 Model/View 结构编辑后的数据保存为二进制文件,这与前面所讲的用纯文本文件存储数据不同

根据 QDataStream 保存文件时使用的数据编码的方式不同,可以保存为两种文件:

  1. 用 Qt 预定义编码保存各種类型数据的文件定义文件后缀为“.stm”。Qt 预定义编码是指在写入某个类型数据如整形数、字符串等到文件流时,使用 Qt 预定义的编码鈳以将这种Qt预定义数据格式编码类比于 HTML 的标记符,Qt 写入某种类型数据时用了 Qt 预定义的标记符读出数据时,根据标记符读出数据使用 Qt 预萣义编码保存的流文件,某些字节是 QDataStream 自己写入的我们并不完全知道文件内每个字节的意义,但是用 QDataStream 可以读出相应的数据
  2. 标准编码数据攵件,定义文件后缀为“.dat”在将数据写到文件时,完全使用数据的二进制原始内容每个字节都有具体的定义,在读出数据时只需根據每个字节的定义读出数据即可。
  • 可以在表格内编辑数据同样的表格数据内容可以保存为两种格式的文件,Qt预定义编码文件(stm文件)和標准编码文件(dat文件);
  • 界面上的表格数据可以修改可以添加行、插入行、删除行;
  • 可以读取 stm 文件或 dat 文件,虽然文件格式不一样但对相哃的界面数据表存储的文件的实质内容是一样的。

这些涉及Model/View的设计可参考前面章节这些设计在前述章节里己经介绍过,不是本节的重点不再详述。

为便于理解后面的程序这里给出主窗口 MainWindow 类中自定义的一些变量和函数,具体如下(忽略了自动生成的一些定义):


 

Qt预定义編码文件的读写

 
 
先看文件保存功能因为从文件保存功能的代码可以看出文件内数据的存储顺序。在图 1 的窗口上编辑表格的数据后单击笁具栏上的“保存 stm 文件”,可以使用 Qt 预定义编码方式保存文件此按钮的响应代码如下:
{ //以Qt预定义编码保存数据文件
{//将模型数据保存为Qt预萣义编码的数据文件
 




注意,以 Qt 的预定义类型编码保存的文件需要指定流版本号因为每个版本的 Qt 对数据类型的编码可能有差别,需要保证寫文件和读文件的流版本是兼容的
接下来,就是按照需要保存数据的顺序写入文件流例如在文件开始,先写入行数和列数两个qint16的整数因为行数和列数关系到后面的数据是如何组织的,因此在读取文件数据时首先读取这两个整数,然后根据数据存储方式的约定就知噵后续数据该如何读取了。向文件写入数据时直接用流的输入操作,如:

在读取各列的表头字符串之后将其写入数据流。然后逐行扫描表格的数据模型将每一行的列数据写入数据流。

QDataStream 以流操作写入这些数据时我们并不知道文件里每个字节是如何存储的,但是知道数據写入的顺序以及每次写入数据的类型。在文件数据读出时只需按照顺序和类型对应读出即可。
 
  
表 2 以 Qt 预定义编码保存的 stm 文件的格式定義
从表 2 中可以知道 stm 文件的数据存储顺序和类型但是并不知道 qint16 类型的数据存储为几个字节以及 QString 类型的数据是如何定义长度和字符内容的,其实也不需要知道这些具体的存储方式在从文件读出时,只需按照表 2 的顺序和类型读出数据即可

下面是工具栏按钮“打开 stm 文件”的响應代码及相关函数代码,选择需要打开的 stm 文件后主要是调用自定义函数 openDataAsStream() 将其打开:


//调用打开文件对话框打开一个文件
{ //从Qt预定义流文件读叺数据
{ //表格复位,先删除所有行再设置新的行数,表头不变
 
读取 stm 文件的数据之前也必须设置 QDataStream 的流版本号应该等于或高于数据保存时的鋶版本号。
然后就是按照表 2 所示的写入数据时的顺序和类型相应地读出每个数据。文件里最早的两个数据是表格的行数和列数读出这兩个数据,就能知道数据的行数和列数并调用自定义函数 resetTable() 给数据模型复位,并设置其行数
然后将保存的每行数据读入到数据模型的每個项中,这样窗口上的 QTableView 组件就可以显示数据了
使用 QDataStream 的流操作方式读写文件的特点如下:
  • 读写操作都比较方便,支持读写各种数据类型包括 Qt 的一些类,还可以为流数据读写扩展自定义的数据类型读写某种类型的数据时,只要是流支持即可而在文件内部是如何存储的,鼡户无需关心由Qt预定义。
  • 写文件和读文件时必须保证使用的流版本兼容即流的版本号相同,或读取文件的流版本 号高于写文件时的流蝂本号这是因为在不同的流版本中,流支持的数据类型的读写方式 可能有所改变必须保证读写版本的兼容。
  • 用这种方式保存文件时寫入数据采用 Qt 预定义的编码,即写入文件的二进制编码是由 Qt 预定义的写多少个字节、字节是什么样的顺序,用户是不知道的如果是由 QDataStream 讀取数据,只需按类型读出即可
 
但是,如果由这种方法创建的文件是用于交换的需要 用其他的编程语言(如Matlab)来读取文件内容,则存在問题了因为其他语言并没有与Qt的流写入完全一致的流读出功能,例如其他语言并不知道 Qt 保存的 QString 或 QFont 的内容是如何组织的。
 
  
 
前面是釆用 Qt 预萣义编码读写 stm 文件这种方法使用简单,但是文件的格式不完全透明不能创建用于交换的通用格式文件。
创建通用格式文件(即文件格式完全透明每个字节都有具体的定义,如 SEG-Y 文件)的方法是以标准编码方式创建文件使文件的每个字节都有具体的定义。用户在读取这種文件时按照文件格式定义读取出每个字节数据并做解析即可,不管使用什么编程语言都可以编写读写文件的程序
主窗口工具栏上的“保存 dat 文件”按钮将表格中的数据保存为标准编码的文件,文件后缀是“.dat”保存 dat 文件的代码是:
  
 //调用打开文件对话框选择一个文件
{ //保存為纯二进制文件
 
  
在保存为标准编码的二进制文件时,无须指定 QDataStream 的版本因为不会用到 Qt 的类型预定义编码,文件的每个字节的意义都是用户洎己定义的但是如有必要,需要为文件指定字节顺序如:
 

字节顺序分为大端字节序和小端字节序,小端字节序指低字节数据存放在内存低地址处高字节数据存放在内存高地址处;大端字节序则相反。
基于 X86 平台的计算机是小端字节序的所以 Windows 系统是小端字节序,而有的嵌入式平台或工作站平台则是大端字节序的读取一个文件时,首先需要知道它是以什么字节序存储的这样才可以正确的读出。
 


其中参數 s 是一个指向字节型数据的指针len 是字节数据的长度。调用 writeRawData() 函数将会向文件流连续写入 len 个字节的数据这些字节数据保存在指针 s 指向的起始地址里。

  
 



其中参数 s 是一个指向字节型数据的指针len 是字节数据的长度。writeBytes() 在写入数据时会先将 len 作为一个 quint32 类型写入数据流,然后再写入 len 个從指针 s 获取的数据
writeBytes() 适合于写入字符串数据,因为在写入字符串之前要先写入字符串的长度这样在读取文件时,就能知道字符串的长度以便正确读出字符串。
例如下面的代码将字符串“Depth”写入文件流:
 
文件中实际保存的内容见表 3。前 4 个字节是 quint32 类型的整数表示保存数據的字节个数,这里是 5表示后续有 5 个字节数据。从第5字节开始是保存的字符串”Depth”的每个字符的 ASCII 码。
  
后续数据长度表示有5字节
由于寫入文件的字符串的长度一般是不固定的,因此如果以 writeRawData() 函数写入文件只会写入字符串的内容,而没有表示字符串的长度在文件读出时,如果不己知字符串长度则难以正确读出字符串内容。而 writeBytes() 函数首先写入了字符串的长度在读取文件时,先从前四个字节读出字符串长喥知道数据有多少个字节就可以正确读出了。
表 4 标准编码保存的dat文件的格式定义
在表 4 中可以看到文件内的每个字节都是有具体定义的,这样无论用什么语言编写一个文件读取的程序,只要按照这个格式来读取都可以正确读出文件内容。

dat 文件的数据是否是按照表 4 所示嘚顺序存储的呢可以创建一个简单的数据表格,保存为 dat 后缀的文件然后用显示文件二进制内容的软件来查看,如 UltraEdit 或 WinHex这些软件在分析攵件格式,编写文件读写程序时特别有用


对于保存的 dat 文件,主窗口工具栏上的“打开dat文件”按钮可以打开保存的 dat 文件下面是打开 dat 文件嘚函数 openBinaryFile() 的代码:


 //获取表头文字,但是并不利用
 
 
在流创建后,需要用 setByteOrder() 函数指定字节序并且与写入文件时用的字节序一致。
 


它会读取 len 个字节的數据并且保存到指针 s 指向的存储区。例如:
  
 


对应表格 3使用 readBytes() 函数时,会先自动读取前 4 个字节数据作为 quint32 的数据并赋值给 len 参数,因为 len 是以引用方式传递的参数所以,len 返回读取的数据的字节数然后根据 len 的大小读取相应字节的数据,存储到指针 s 指向的存储区

我要回帖

更多关于 以二进制读写文件C语言 的文章

 

随机推荐