C语言文件操作

C语言文件操作


“文件”是指存储在计算机外部存储器中的数据的集合。计
算机根据?文件的名字,完成对?文件的操作
C语?言将?文件看作是字符构成的序列,即字符流。其基本 的存储单位是字节。
?文件的读写操作函数,它们都在头?文 件stdio.h定义的:
? 按字符读写的函数fgetc()、fputc()
? 按字符串读写的函数fgets()、fputs()
? 按格式要求读写的函数fprintf()、fscanf()
? 按数据块读写的函数fread()、fwrite()

概述

所谓文件(file)一般指存储在外部介质上数据的集合,比如我们经常使用的mp3、mp4、txt、bmp、jpg、exe、rmvb等等。这些文件各有各的用途,我们通常将它们存放在磁盘或者可移动盘等介质中。那么,为什么这里面又有这么多种格式的文件呢?原因很简单,它们各有各的用途,区分就在于这些文件里面存放的数据集合所遵循的存储规则不一样。举个例子比如bmp图片文件,为什么他能够表示一张图片,因为它有固定的格式,哪一段到哪一段,哪个偏移到哪个偏移应该存放什么数据是规定好了的。比如有文件头,一般是一个结构体,存放的文件的一些信息,如图片的大小,像素等等。再后来有数据区。然后我们要显示一张图片,就只需要按照前面所说的规则将文件头结构和数据块读出来,然后将这些数据在屏幕上用颜色表示出来,就成了一张图片。其它文件格式也类似。

 

这里要说一个更重要的例子,对我们理解文件有好处。那么这个文件就是exe文件(这里只讨论Windows平台),通常我们认为它是一个可执行程序,这无疑是增加了它的神秘度。从本质上来讲exe无非是一种固定的文件格式罢了。既然这样,它就有一套自己的存储规则。跟前面的图片文件一样有规则。此时,你可能会问:你这么说那我就可以纯手工(直接填写数据填充文件)写出一个exe可执行文件了?
面对你这个问题,我只能说你已经习惯思考了,已经习惯给自己提问了,已经很聪明了。那么答案是肯定的,你完全可以用一个编辑器直接填写数据写出一个helloworld.exe文件或者helloworld.dll文件。因为这些具有一定格式规则的文件一般是二进制存储的,于是我们可以用一个二进制编辑器新建一个二进制文件,然后向里面填写数据。然后双击运行输出“helloworld”字符串。你可能会觉得很有成就感,我之前就写过一个exe和dll。这里exe和dll的文件格式也就是著名的PE文件格式。有兴趣你可以去查阅相关资料,此非本文重点。

 

总结上面的认识,文件无非就是一段数据的集合,这些数据可以是有规则的集合,也可以是无序的集合。操作系统也就是以文件为单位对数据进行管理的。也就是说,要访问外部介质上的数据,必须先按照文件名进行查找,然后从该文件中读取数据。要想写数据到外部介质,必须得建立一个文件,然后再写入。因此,这样来看,你眼前的文件将是一堆一堆数据而已,也没有什么类型文件之分了,类型只是为了区分而已,假如你把一个exe文件的扩展名改为txt,把它用记事本打开,同样是可行的,只是会执行exe文件里面的东西而已。(这里又不得不提到一点,如果你是一名程序员或者爱好者,那么你不应该将你的文件扩展名给隐藏了,要让它显示出来,如果你隐藏了,无非是增加了它的神秘感,同时在文件操作上不方便。通过上面的本质,我相信你能体会到我为什么这么说。)

 

说到这里,你应该知道文件是什么了,那么再来看二进制文件和ASCII文本文件,为什么要分为这两种呢?

 

首先、文本文件方式存储多用于我们需要明显知道文件里面的内容时,比如ini、h、c等文件都是文本文件,这种文件存储的是字符(ASCII码),比如一个整数10000,类型是short,占2字节,存储文本形式将占用5个字节,一共5个字符。你可以想想更多的例子,体会文本文件方便之处(提示:这里的文本文件不是说是txt文件,而是指所有以文本格式存储的文件。)

 

其次、二进制文件方式多用于直接将内存里面的数据形式原封不动存放到文件里,比如上面的short
10000,在内存中占2字节,存储内容为10000的二进制数,存到文件后还是占2字节,内容也是10000的二进制。这种方式可以整块数据一块儿存储,同时还可以将内存数据映射到文件里。

 

由上面两点,C语言操作文件可以是字节流或者二进制流。它把数据看成是一连串字符(字节),而不需要考虑边界。C语言对文件的存取是以字节为单位的。输入输出的数据流的开始和结束仅受程序控制而不受物理符号(如回车换行符)控制。这种文件通常称为流式文件,大大增加了灵活性。我们可以产生很多自己的文件格式,在游戏程序里面,用得比较多的就是资源包的格式,一般就是自定义的存取规则。我之前也写了一个包文件,存取只需要遵循规则,原理是非常简单的。大家可以试试在脑子里面构造一个包文件。

 

 

在ANSI
C标准中,使用的是“缓冲文件系统”。所谓缓冲文件系统指系统自动地在内存区为每一个正在使用的文件名开辟一个缓冲区,从内存向磁盘输出数据必须先送到内存中的缓冲区,装满后再一起送到磁盘去。反向也是如此。这里需要说明两个词:“输入”“输出”。输入表示从文件里读数据到程序里,输出表示从程序里写数据到文件中。

 

了解了文件及文件存储形式,下面该正式进入文件的读写了,不要太激动,还是慢慢来。细节往往决定成败。在缓冲文件系统中,有一个很重要的一个东西就是文件指针,每个使用的文件都会在内存中开辟一个区,用于存放文件的有关信息,这些文件信息就保存在一个结构体变量中的,这个结构体是由系统定义的,名为FILE,先来看看VC2005在stdio.h下FILE结构体的定义:

struct _iobuf    {          char *_ptr;               // 指向buffer中第一个未读的字节                   int   _cnt;                 // 记录剩余未读字节的个数          char *_base;           // 指向一个字符数组,即这个文件的缓冲          int   _flag;                // FILE结构所代表的打开文件的一些属性          int   _file;                 // 用于获取文件描述,可以使用fileno函数获得此文件的句柄。          int   _charbuf;          // 单字节的缓冲,即缓冲大小仅为1个字节,如果为单字节缓冲,_base将无效          int   _bufsiz;            // 记录这个缓冲的大小          char *_tmpfname;    // temporary file (i.e., one created by tmpfile()                                          // call). delete, if necessary (don't have to on                                          // Windows NT because it was done by the system when                                          // the handle was closed). also, free up the heap                                          // block holding the pathname.  };  typedef struct _iobuf FILE;

 

 

好了,上面的结构体就是这样定义的。这里不得不再次提到缓冲:

缓冲模式

常量(mode)

备注

无缓冲模式

_IONBF

该文件不使用任何缓冲,也可以说是字节缓冲

只能保存一个字节。

行缓冲模式

_IOLBF

仅对文本模式打开的文件有效,所谓行,即是指每收到一个换行符(/n或/r/n),就将缓冲flush掉

全缓冲模式

_IOFBF

仅当缓冲满时才进行flush

 

上面结构体中的_flag就标记了缓冲的信息(我们关心这三个):

#define _IOYOURBUF  0x0100      // 使用用户通过setbuf提供的buffer    #define _IOMYBUF      0x0008      // 这个文件使用内部的缓冲     #define _IONBF          0x0004      // 无缓冲模式    #define _IOLBF           0x0040      // 行缓冲模式    #define _IOFBF           0x0000      // 全缓冲模式

 

 

同时,_flag也标记了读写模式,比如”r+”、”w+”等。

#define _IOREAD         0x0001    // 只读  #define _IOWRT          0x0002    // 只写    #define _IORW            0x0080    // 可读可写 

 

上面的3中模式就是”r”、”w”、”+”任意组合起来表示的意思。

正因为使用缓冲模式,是为了避免频繁的系统调用开销,有了缓冲就不需要每次都访问实际的文件。当然缓冲也会带来隐患,比如写文件时,先是到缓冲,如果此时系统崩溃或者进程意外退出时,有可能导致文件数据的丢失。因此C语言提供了几个基本的函数,弥补缓冲带来的问题:

int fflush( FILE* stream )  // flush指定文件的缓冲,若参数为NULL,则flush所有文件的缓冲。    int setvbuf( FILE *stream, char* buf,  int mode, size_t size )  // 设定缓冲类型,如上面的表格。    void setbuf( FILE* stream,  char* buf )  // 设置文件的缓冲,等价于( void )setvbuf( stream, buf, _IOFBF, BUFSIZ ).

 

所谓flush一个缓冲,是指对写缓冲而言,将缓冲内的数据全部写入实际的文件,并将缓冲清空,这样可以保证文件处于最新的状态。之所以需要flush,是因为写缓冲使得文件处于一种不同步的状态,逻辑上一些数据已经写入了文件,但实际上这些数据仍然在缓冲中,如果此时程序意外地退出(发生异常或断电等),那么缓冲里的数据将没有机会写入文件。flush可以在一定程度上避免这样的情况发生。

在这个表中我们还能看到C语言支持两种缓冲,即行缓冲(Line
Buffer)和全缓冲(Full
Buffer)。全缓冲是经典的缓冲形式,除了用户手动调用fflush外,仅当缓冲满的时候,缓冲才会被自动flush掉。而行缓冲则比较特殊,这种缓冲仅用于文本文件,在输入输出遇到一个换行符时,缓冲就会被自动flush,因此叫行缓冲。

终于把概念性的东西和准备步骤做完了,下面该看看具体的读写文件了。有了前面的准备工作,读写文件将不是难事了,因为有现成的库函数供我们使用,我们下面的段落将是如何使用这些库函数和一些注意事项而已了。

首先看如何打开文件,先看代码:

#include <stdio.h>    int main( void )    {      FILE* pReadFile = fopen( "E://mytest.txt", "r" );   // 打开文件      if ( pReadFile == NULL )          return 0;      fclose( pReadFile );     // 关闭文件      return 0;    } 

上面的这段代码,只是一个简单的打开文件,如果成功打开后直接关闭。这里打开的是一文本文件,是以只读的方式打开。使用fopen函数打开,第一个参数是文件路径,第二个参数是读写模式,返回值为0表示打开失败。先看看读写模式:

 

 文件使用方式

    含义 

"r"(只读) 

为输入打开一个文本文件,不存在则失败

"w"(只写)

为输出打开一个文本文件,不存在则新建,存在则删除后再新建

 "a"(追加)

向文本文件尾部增加数据,不存在则创建,存在则追加

‘rb"(只读) 

为输入打开一个二进制文件,不存在则失败

"wb"(只写) 

为输入打开一个二进制文件,不存在则新建,存在则删除后新建

"ab"(追加) 

向二进制文件尾部增加数据,不存在则创建,存在则追加

"r+"(读写) 

为读写打开一个文本文件,不存在则失败

"w+" (读写)

为读写建立一个新的文本文件,不存在则新建,存在则删除后新建

 "a+"(读写)

为读写打开一个文本文件,不存在则创建,存在则追加

"rb+"(读写)

为读写打开一个二进制文件,不存在则失败

"wb+"(读写)

为读写建立一个新的二进制文件,不存在则新建,存在则删除后新建

 "ab+"(读写)

为读写打开一个二进制文件,不存在则创建,存在则追加

 

1. 在C语言中,规定文件的结构体为FILE,FILE实际上是_iobuf的别名。

struct _iobuf { char *_ptr; int _cnt; char *_base; int _flag; int _file; int _charbuf; int _bufsiz; char *_tmpfname; };typedef struct _iobuf FILE;

C语言文件操作一

 程序为什么需要文件?
1>配置
unix采用文本 Windows采用注册表二进制文件
2>媒体数据
存档在二进制文件中

文件指针变量=fopen(?件名,处理文件方式);
文件不存在等原因造成不能打开? 件,则调?fopen()后将返回一个空指针 NULL。

一、读写字符

C语言为从文件中读写一个字符提供了两个函数:

int __cdecl fgetc( FILE* stream );              // 从文件读入一个字符    int __cdecl fputc( int ch, FILE* stream );   // 写入一个字符到文件

 

看例子:

#include <stdio.h>    int main( void )  {        char cInput;      FILE* pReadFile = fopen( "E://mytest.txt", "r" );   // 打开文件      if ( pReadFile == NULL )          return 0;      while ( ( cInput = fgetc( pReadFile ) ) != EOF )   // 从文件读入一个字符,如果到文件尾部,则返回EOF(-1)          printf( "%c", cInput );      fclose( pReadFile );     // 关闭文件      return 0;  }

假如mytest.txt文件的内容是:

masefee

hello

world

三行,那么我们逐个读入每个字符,直到EOF结束,EOF很简单,其实就是#define
EOF
(-1),WINDOWS为了能够返回失败为-1,因此fgetc的返回值使用是int类型。同时-1也不是某个字符的ASCII,所以不影响,一举两得。上面程序while循环不断从文件中读取单个字符,遇到换行符(WINDOWS下回车符(‘/r’)为13,
换行符(‘/n’)为10),printf输出后变处理成换行符了,因此文件里面3行,逐个读入程序里在终端显示后还是3行。代码很简单,就不用多说了。这里需要提到一点:

问题一:当第一次执行了fgetc后,我们看看pReadFile指针里面的内容与刚执行了fopen函数后的内容有所变化,为什么?

 

再来看fputc函数:

#include <stdio.h>    int main( void )  {      int i = 0;      char szOutput[ 32 ] = "masefee/nhello";        FILE* pWriteFile = fopen( "E://mytest.txt", "w" );   // 打开文件      if ( pWriteFile == NULL )          return 0;        while ( szOutput[ i ] != 0 )      {          fputc( szOutput[ i ], pWriteFile );    // 写入一个字符到文件          i++;      }        fclose( pWriteFile );     // 关闭文件      return 0;  }

 

 

我特意在szOutput数组里写了一个‘/n’字符,此字符就是换行符newline,意图是当输出到e之后,便输出一个换行符,让字符串换行。因此最终mytest.txt文件里面的内容如下:

masefee

hello

到这里,你可能会想到第一个fgetc的例子是我们预先在文件中输入3行字符,然后读入到程序中。我们在用记事本输入3行文本的时候,每当换行的时候我们敲键盘是按的回车。

问题二:既然我们敲的是回车,为什么在文件里存储的是’/n’而不是’/r’?

 

同时,到这里想到第一个问题,我们又来观察一下,当刚使用fopen函数时,pWriteFile里面的内容是:

pWriteFile          0x00437bb0

_ptr                   0x00000000

_cnt                   0 

_base                0x00000000

_flag                  2 

_file                   3

_charbuf            0 

_bufsiz              0 

_tmpfname       0x00000000

 

而执行了fputs函数,到换行符后我们再看pWriteFile里面的内容:

 

pWriteFile          0x00437bb0

_ptr                   0x00385019

_cnt                   4087

_base                0x00385010

_flag                  10

_file                   3

_charbuf            0 

_bufsiz              4096

_tmpfname       0x00000000

 

然后我们再看看_base所在内存的值:

6d 61 73 65 66 65 65 0a 68

 m  a   s   e    f   e    e  /n  h

 

从这个现象我们能够意识到,FILE结构里面_base所指向的缓冲区,_cnt表示还剩下多少个字节没有写。还可以意识到,我们在不设置任何参数时,默认情况下是采用的全缓冲模式,填充4096字节后自动会写入到文件,在这里我们没有那么多字节,因此在fclose函数执行后,文件里便写入了值。你可以打断点在fclose上,等程序断下来后,观察你磁盘里面的mytest.txt是空的,当执行了fclose后大小就变了。这也能体现缓冲区的一个现象。

同样,如果你想立即将缓冲区的数据写到文件里,可以在fclose函数前面加上:

fflush( pWriteFile );

当执行完此函数后,数据便写进了文件,最后再关闭文件。

 

2. 定义一个指向文件的指针。

FILE *fp; //fp指向一个文件

近段复习C语言文件操作,在原文的基础之上总结如下:

还有很多用处

fputc(字符,?文件型指针变量)
? fp为一个文件类型指针变量,上式将字符常
量’A’(也可以是字符型变量)写?文件当前位
置,并且使?件位置指针下移一个字节。如果
写入操作成功,返回值是该字符,否则返回 EOF。

二、读写字符串

C语言为从文件中读写字符串提供了2个函数:

char* __cdecl fgets( char* _Buf, int _MaxCount, FILE* _File );

 

参数一:要从文件中读入字符串的存放空间。

参数二:最大读取字节数。

参数三:文件指针。

返回值:返回读入的字符串指针。

 

int      __cdecl fputs( const char* _Str,  FILE* _File );

 

参数一:要写入文件的字符串

参数二:文件指针

返回值:失败或成功,0表示成功,其它表示失败。

先来看字符串读取:

#include <stdio.h>    int main( void )  {      char   szInput[ 32 ] = { 0 };      char* pRet = NULL;        FILE* pReadFile = fopen( "E://mytest.txt", "r" );   // 打开文件      if ( pReadFile == NULL )          return 0;        pRet = fgets( szInput, 32, pReadFile );    // 从文件中读取一个字符串到szInput数组中        fclose( pReadFile );     // 关闭文件      return 0;  }

 

其它函数不说了,这里只说fgets函数,第二个参数传的是32,实际只能从文件中读取31个字符,因为fgets函数内部会将最后一个字符置为‘/0’,
表示字符串结束。那么我们可以看看fgets函数的内部原理,我这里写写伪代码,为了更清晰的表现出来:

char*  fgets( char* dst, int maxcount, FILE* file )    {      char ch;      while( --maxcount )      {           ch = readFromFile();           if ( ( *dst++ =  ch ) == '/n' )               break;      }      *dst = 0;   // 赋值为'/0'       return dst;    }

如果最大读取字节数量足以读到换行,将停止读取字符,然后阶数本字符串,然后返回。

明白了fgets函数,fputs函数就简单了:

#include <stdio.h>    int main( void )  {      char szOutput[ 32 ] = "masefee/nhello";        FILE* pWriteFile = fopen( "E://mytest.txt", "w" );   // 打开文件      if ( pWriteFile == NULL )          return 0;        fputs( szOutput, pWriteFile );    // 写入一个字符串到文件        fclose( pWriteFile );     // 关闭文件      return 0;  }

 

这里我也专门为字符数组里增加了一个换行符,写入字符串的时候并不会因为换行符而只写换行符前面的字符,同时在fputs内部会求第一个参数的长度strlen(
Str ); 然后再写入这么一个长度的字符串到文件。

 

到这里又得提醒一点,即便是文件里面含有’/0’(ASCII码为0的字符)。fgets函数同样会一直读取到换行符或者读取规定的字符个数(此字符个数小于一行字符数)。虽然是读了一行,中间因为有0,因此字符串被截断,读出来的字符串并没有一行,只有0前面的所有字符。这里大家需要注意。同时fputs函数会以0结束写入文件,这是跟通常情况一样的,可以不用关心。

 

1. fopen();打开文件

  • 函数原型: FILE * fopen(char * Filename,char *Mode);
  • 返回类型是一个FILE*指针
  • Filename 是需要打开的文件路径
  • Mode 是打开文件的方式 如下:
标识符 作用
r 只读
w 只写
a 追加
r+ 读和写
w+ 读和写
a+ 读和写
rb 只读
wb 只写
ab 追加
rb+ 读和写
wb+ 读和写
ab+ 读和写
  • 打开文件时一般需要判断是否打开成功。

#include <stdio.h>int main(){ FILE* fp; if ((fp = fopen("test.txt", "a+")) == NULL) { printf("打开文件失败!\n"); } return 0;}
  • 打开文件失败的原因一般有以下4个:a). 使用只读的方式打开空的文件。b).
    指定的盘符不存在或者指定的路径不存在。c).
    文件名包含一些稀奇古怪的符号 比如123@.txtd).
    有些文件被其他程序占用。

一.文件的概念:


fgetc()函数
fgetc(?件型指针变量)
返回文件当前位置的字符,并且使?件位置指针下移一个字符。如果遇到文件结束,
则返回值为文件结束标志EOF。

三、格式化数据读写

C语言既然有printf、scanf,那么同样也有文件操作的格式化函数:

int __cdecl fprintf( FILE* _File, const char* _Format, ... );    int __cdecl fscanf( FILE* _File, const char* _Format, ... );

 

这两个函数跟printf和scanf的用法非常相似,只是这里输入输出是关于文件的。

直接贴代码:

#include <stdio.h>    typedef struct SStudent  {      int   number;      char  name[ 11 ];  }Student;    int main( void )  {      Student stu;        FILE* pReadFile = fopen( "E://mytest.txt", "r" );   // 打开文件      if ( pReadFile == NULL )          return 0;      fscanf( pReadFile, "%d%s", &stu.number, &stu.name );      fclose( pReadFile );     // 关闭文件      return 0;  }     

 

我定义了一个结构体,里面一个学号,一个姓名。然后打开文件,读取数据到stu结构体变量中。假如文件中是:

345   masefee

346   Tim

然后读到stu结构体变量中,number为345,name为”masefee”。

fscanf读取数据是以空格、制表符、换行符进行分割的,我们可以这样来填充结构体。

 

再来看fprintf:

#include <stdio.h>    typedef struct SStudent  {      int   number;      char  name[ 11 ];  }Student;    int main( void )  {      Student stu;        FILE* pWriteFile = fopen( "E://mytest.txt", "w" );   // 打开文件      if ( pWriteFile == NULL )          return 0;        stu.number = 100;      strcpy( stu.name, "masefee" );        fprintf( pWriteFile, "%d    %s", stu.number, stu.name );        fclose( pWriteFile );     // 关闭文件      return 0;  }

 

 

此程序将把结构体stu的内容写到文件里,注意这里的name不会把结束符’/0’写到文件里。

好了,说到这里,上面几个基本的文件操作函数已经写完了,我只是使用了”r”和”w”两种方式,其它方式你可以自行测试,也没有什么特别的。如果你是用上面的函数去读取二进制序列,也是没有错的,只不过你更不好控制而已。至于和”+”组合也没有什么特别的,无非就是在文件尾部追加,原理一样,大家可以自行测试。

 

2.fclose();关闭文件

  • 函数原型:int fclose(FILE *File);

 fclose;
  • 文件打开使用完成之后,必须要关闭,防止数据丢失。

当我们打开一个文件之后,在文件内部有一个位置指针,用来指向文件当前的读写位置。该指针在文件打开时总是指向第一个字节的。随着我们对文件进行读写的过程,这个内部位置指针会发生变化。

   所谓“文件”是指一组相关数据的有序集合。
这个数据集有一个名称,叫做文件名。
平时接触到的文件诸如源程序文件、目标文件、可执行文件、库文件
(头文件)等。文件通常是驻留在外部介质(如磁盘等)上的,在使用时才调入内存中来。

首先以标准的输入输出谈起~ 

fputs(字符串,?件型指针变量)
写?文件成功,函数 返回值为0,否则为EOF。

四、文件数据块读写

同样C语言也提供了两个函数:

size_t __cdecl fwrite  (    const void *buffer,  // 要写入文件的数据块    size_t size,         // 写入文件的字节数    size_t count,        // 写入count个size大小的数据    FILE *stream         // 文件指针  );     

 

size_t __cdecl fread  (    void * _DstBuf,        // 存放从文件读出来的数据    size_t _ElementSize,   // 读取字节数    size_t _Count,         // 读入次数    FILE * _File           // 文件指针  );

 

先看看fwrite函数:

#include <stdio.h>    typedef struct SStudent  {      int   number;      char  name[ 12 ];  }Student;    int main( void )  {      Student stu;      FILE* pWriteFile = fopen( "E://mytest.txt", "w" );   // 打开文件      if ( pWriteFile == NULL )          return 0;      stu.number = 10000;      strcpy( stu.name, "masefee" );      fwrite( &stu, sizeof( stu ), 1, pWriteFile );      fclose( pWriteFile );     // 关闭文件      return 0;  }

 

这样写入文件后,mytest.txt的内容为:

‘  masefee 烫烫

你可能会疑惑,为什么会有乱码?而且还有可恶的“烫”字。原因很简单,fwrite函数是以数据块的形式写数据到文件的,比如这里的stu结构体变量,我们将它整块写入文件,一共16字节,因此上面的乱码对应的就是stu结构体变量在内存中的存放形式,number占4字节,name占12字节,具体的数值是:

10 27 00 00 6d 61 73 65 66 65 65 00 cc cc cc cc

    10000                 “masefee”               烫     烫

因为我们在为name拷贝字符串时,并没有将name的所有字符清零,因此系统默认初识化为0xcc,为什么初始化为0xcc,之前我应该提过,主要是这个0xcc是汇编中断指令的机器码,主要防止访问越解释,进行中断报错。而0xcccc就是中文编码的“烫”字。

最后面的两个“烫”还不能省略,因为我们是以块写入文件的,如果去掉4个cc,那么将没有16字节,如果有多个结构体变量的数据一块儿写到文件中时,结构体的数据对齐是非常重要的,否则将读写越界,跟内存一样。这里就好比内存的一个映射。

 

至于为什么会出现乱码,是因为超过可现实ASCII码值,看上去就是乱的,其实数据还是正常的。

 

理解了fwrite函数后,fread函数就简单了,由于篇幅原因我这里只写关键:

Student stu_out;    fread( &stu_out, sizeof( Student ), 1, pReadFile );

 

这样就能填充好stu_out结构体变量,我想你已经体会到了数据块读写时,数据对齐的重要性了。在游戏的资源包,就是采用的数据块的存储形式,同时bmp、jpg、exe、dll等文件都是由很多个数据块,通常是结构体的形式直接写入文件的,这样文件头记录了很多偏移,很多大小等就显得非常重要了。

最后,我直接写了一个实例,就是简单的打包,解包程序。可以将多个文件放置到一个包文件里,这个包是二进制包。基本的功能已经实现,只需要添加比如压缩,界面等优化工作了。我初步测试了一下是可以成功打包解包的,也没有太多的条件检查和效率考虑,本文重在解释文件操作的灵活性和重要性。

#include <stdio.h>    #include <string.h>    #include <stdlib.h>  
  typedef unsigned int  uint;    typedef unsigned char byte;               // 包文件中最大可容纳的文件个数    #define MAX_FILE_COUNT 10    // 全局包文件指针    FILE*  g_pMasFile = NULL;       // 资源包文件头结构    typedef struct SMaseFileHeader    {        uint  uFileFlag;       // 包文件头标记: 'MASE'        uint  uFileCount;      // 包内文件个数        uint  uFileListOfs;    // 文件列表偏移        uint  uMaxFileCount;   // 最大子文件个数        uint  uFileSize;       // 包文件的大小    }MaseHeader;        // 包内文件信息结构    typedef struct SFilesMessage    {        uint  uFileOfs;          // 本文件在包内的偏移        uint  uFileSize;         // 本文件的大小        char  szFileName[ 260 ]; // 本文件的路径    }FilesMsg;        // 打开包文件    int OpenMasFile( const char* path, const byte onlyOpen )    {        uint       uWriteCount;       // 写入文件信息次数;        byte       bIsNew = 0;        // 是否新建的        MaseHeader header;            // 文件头结构定义        FilesMsg   msg;        g_pMasFile = fopen( path, "rb" );  // 用来判断是否存在        if ( g_pMasFile == NULL )          // 这里就没有用windows API了        {            if ( onlyOpen == 1 )           // 只打开不新建                return -1;            bIsNew = 1;            g_pMasFile = fopen( path, "wb" );            if ( g_pMasFile == NULL )                return -1;        }          // 先关闭,然后在用"rb+"方式打开        fclose( g_pMasFile );        g_pMasFile = fopen( path, "rb+" );        if ( g_pMasFile == NULL )            return -1;        if ( bIsNew == 1 ) // 新建的文件        {            header.uFileFlag     = 'ESAM';            header.uFileCount    = 0;            header.uFileListOfs  = sizeof( MaseHeader ); // 紧跟着就是文件列表            header.uMaxFileCount = MAX_FILE_COUNT;            header.uFileSize     = sizeof( MaseHeader )                                   + ( MAX_FILE_COUNT * sizeof( FilesMsg ) );              // 写入头信息            fwrite( &header, sizeof( MaseHeader ), 1, g_pMasFile );            memset( &msg, 0, sizeof( FilesMsg ) );            uWriteCount = MAX_FILE_COUNT;            // 写入文件列表用0占位            while( uWriteCount-- )                fwrite( &msg, sizeof( FilesMsg ), 1, g_pMasFile );        }        else  // 文件存在        {            // 则读取头文件信息            fread( &header, sizeof( MaseHeader ), 1, g_pMasFile );        }          // 检查文件头标记        if ( header.uFileFlag != 'ESAM' )        {            fclose( g_pMasFile );            return -1;        }          // 检查数据是否完整        if ( header.uMaxFileCount != MAX_FILE_COUNT )        {            fclose( g_pMasFile );            return -1;        }           return 0;      }        // 写文件到包里    int WriteFileToPak( const char* path )    {        FilesMsg   fileMsg;      // 此文件的文件信息结构        MaseHeader header;       // 包文件头结构定义        uint       uFileSize;        uint       uFileListEndOfs;        byte*      pBuff;        FILE*      pFile = NULL;        if ( g_pMasFile == NULL )            return -1;        memset( &fileMsg, 0, sizeof( FilesMsg ) );        fseek( g_pMasFile, 0, SEEK_SET );        // 则读取头文件信息        fread( &header, sizeof( MaseHeader ), 1, g_pMasFile );        uFileListEndOfs = header.uFileCount * sizeof( FilesMsg ) + header.uFileListOfs;        pFile = fopen( path, "rb" );        if ( pFile == NULL )            return -1;        fseek( pFile, 0, SEEK_END );        uFileSize = ftell( pFile );        fseek( pFile, 0, SEEK_SET );        // 文件名长度不能超过260        strcpy( fileMsg.szFileName, path );        fileMsg.uFileOfs  = header.uFileSize;        fileMsg.uFileSize = uFileSize;        // 写入文件信息        // 将文件指针定位到uFileListEndOfs处,以便写入新的文件信息结构        fseek( g_pMasFile, uFileListEndOfs, SEEK_SET );        fwrite( &fileMsg, sizeof( FilesMsg ), 1, g_pMasFile );        // 申请空间        pBuff = ( byte* )malloc( uFileSize );        fread( pBuff, uFileSize, 1, pFile );        // 写数据到包文件里        fseek( g_pMasFile, header.uFileSize, SEEK_SET );        fwrite( pBuff, uFileSize, 1, g_pMasFile );        // 释放内存        free( pBuff );        // 重新填充header       header.uFileCount += 1;        header.uFileSize  += uFileSize;        fseek( g_pMasFile, 0, SEEK_SET );        // 重新写入包文件头        fwrite( &header, sizeof( MaseHeader ), 1, g_pMasFile );        return 0;    }          // 从包文件里读数据    int ReadFileFromPak( const FilesMsg msg, byte* _dst )    {        if ( g_pMasFile == NULL )            return -1;        fseek( g_pMasFile, msg.uFileOfs, SEEK_SET );        fread( _dst, msg.uFileSize, 1, g_pMasFile );        return 0;    }            // 获取包中某个文件的信息      int GetFileMessage( const char* path, FilesMsg* msg )    {        FilesMsg   fileMsg;      // 此文件的文件信息结构        MaseHeader header;       // 包头结构        uint       uFileCount;   // 文件个数        if ( g_pMasFile == NULL || msg == NULL )            return -1;        // 则读取头文件信息        fseek( g_pMasFile, 0, SEEK_SET );        fread( &header, sizeof( MaseHeader ), 1, g_pMasFile );        uFileCount = header.uFileCount;        while ( uFileCount-- )        {            fread( &fileMsg, sizeof( FilesMsg ), 1, g_pMasFile );            // 判断是否是要获取的文件            if ( stricmp( fileMsg.szFileName, path ) == 0 )            {               *msg = fileMsg;               return 0;            }        }          return -1;      }            // 关闭包文件      int CloseMasFile( void )    {        if ( g_pMasFile == NULL )            return -1;        fclose( g_pMasFile );        g_pMasFile = NULL;        return 0;      }  

 

 

 

上面已经将整个打包解包接口给实现了,我自定义文件扩展名为.mase,
这个随意哈,文件头结构上面已经很清晰了。由于篇幅的原因,这里就不一一解说了,我贴了很多注释。应该能够看懂的。

 

有了上面的接口,我们就可以来操作这个包文件了,先是看怎么写入:

int main( void )      {       int ret;        ret = OpenMasFile( "E://PhotoPak.mase", 0 );        if ( ret == -1 )            goto __exit;        WriteFileToPak( "E://大山.jpg" );        WriteFileToPak( "E://海水.bmp" );        WriteFileToPak( "E://查看.exe" );        WriteFileToPak( "E://加载.dll" );        WriteFileToPak( "E://说明.txt" );    __exit:        CloseMasFile();        return 0;    }   

 

 

在这段代码里,演示了怎么将文件给写进包文件,首先是创建了一个PhotoPak.mase包,然后是向里面写入了:大山.jpg、海水.bmp、查看.exe、加载.dll、说明.txt这么几个文件,注意我的接口里面都是用二进制打开的,因为如果是非二进制打开的话,写入的时候会插入一些物理字符(比如回车符(ASCII:0x0D(
1310 ))等)。那样插入进去后,然后在解包时再采用非二进制方式写入文件就不是原来的文件了,这点大家要注意。

 

好了,写了这么几个文件后,再看看怎么把他们从包里面弄出来,然后能够正常的打开和查看:

int main( void )    {        byte*       pBuff;        FILE*       pOutFile;        FilesMsg    getFileMsg;        int         ret;         ret = OpenMasFile( "E://PhotoPak.mase", 1 );        if ( ret == -1 )            goto __exit;        ret = GetFileMessage( "E://查看.exe", &getFileMsg );        if ( ret == -1 )            goto __exit;        pBuff = ( byte* )malloc( getFileMsg.uFileSize );        ret = ReadFileFromPak( getFileMsg, pBuff );        if ( ret == -1 )            goto __exit_free;        pOutFile = fopen( "E://查看_out.exe", "wb" );  // 注意使用的是二进制模式        if ( ret == -1 )            goto __exit_free;        fwrite( pBuff, getFileMsg.uFileSize, 1, pOutFile );        fclose( pOutFile );    __exit_free:        free( pBuff );    __exit:        CloseMasFile();        return 0;    }   

 

 

很清楚了吧,直接先传入路径,然后获得文件的信息,方便我们分配空间。然后我是将从包里获取出来的文件又写到磁盘里,命名为查看_out.exe,
同样既然是获取了pBuff,你同样可以在内存中使用这个文件,一举两得。然后获取出来,运行这个获取的查看_out.exe看是不是能运行。我在WINDOWS
XP SP3 下是能运行的,你可以用你自己的一个exe来测试,随便用什么文件。

 

1. fseek(); 文件内部指针偏移

  • 函数原型:int fseek(FILE * File, long Offset, int Origin);
  • File 即为文件指针。通常为fp。
  • Offset指的是偏移量,正数向右偏移,负数向做偏移,偏移量为常数,后面通常加上L。
  • Origin 是文件偏移的起始点。0表示文件头 1表示当前位置 2表示文件尾

 fseek(fp, 16L, 0); //从文件头向右偏移16个字节 fseek(fp, -16L, 1); //从当前位置向左偏移16个字节 fseek(fp, 16L, 2); //从文件尾向左偏移16个字节

二.文件的分类:

(1)格式输入输出

fgets(字符数组,字符数,?件型指针变量)
其作?是从fp指向的?件的当前位置开始读取
n-1个字符,并加上字符串结束标志’\0’一起放入字符数组str中。如果从文件读取字符时遇
到换行符或文件结束标志EOF,读取结束。 函数返回值为字符数组str的?地址。

这里还要说到几个注意事项:

1.
这里我只是测试了较小的文件解包和写包,如果文件比较大的话,可以用分块进行读写。

2.
我没有写任何的加密算法和压缩算法,这里只是展示了基本原理。也没有太多的效率和安全考虑。

3.
我这里使用的都是E盘根目录下的文件,你也完全可以不是跟目录,在包文件里面是没有文件夹的概念的,如果没有在根目录,你可以在解包的时候,根据路径先创建好文件夹在磁盘上,然后再将包里读出来的文件写到相应的路径下,这就实现了不同文件夹管理的功能。

 

 

 

 


2. rewind(); 重置文件内部指针为文件头。

  • 函数原型:void rewind(FILE *File);

 rewind; //重置文件内部指针为文件头

   从不同的角度可对文件作不同的分类。

printf("%[flag][width][.prec][hlL]type");

fprintf(文件型指针变量, 格式控制, 输出表列)
它的作?是将变量num、name、score按照%ld、
%s、%5.1f的格式写?入fp指向的文件的当前位置

1. fputc(); 和fgetc();

  • 函数原型:

int fputc(int Ch,FILE *File); //把字符Ch写入文件Fileint fgetc(FILE * File); //从文件当前位置读字符
  • 案例:

#include <stdio.h>int main(){ FILE* fp; //定义一个文件指针 if ((fp = fopen("test.txt", "w+")) == NULL) //打开test.txt { printf("打开文件失败!\n"); } char ch = 'j'; //定义一个字符变量ch,初始化为'j' char ch1; fputc; //将ch写入文件 注意:这里的文件内部指针会变化。 rewind; //将变化后的文件内部指针重置为文件头 ch1 = fgetc; //从文件中读出一个字符 存放到ch1中 printf("%c\n", ch1); fclose; //关闭文件 return 0;}

1.从用户的角度看,文件可分为普通文件和设备文件两种。

1>

fscanf(?件型指针变量, 格式控制,输入表列);
它的作?是从fp指向的文件的当前位置开始,按
照%ld、%s、%5.1f的格式取出数据,赋给变量
num、name和score。

2. fputs(); 和fgets();

  • 函数原型:

int fputs(const char* Str, FILE* File); //把字符串Str写入文件Filechar* fgets(char* Buf, int MaxCount, FILE* File); //从文件File中获取MaxCount个字符存放到Buf指向的字符串中,结尾补'\0'。
  • 案例:

#include <stdio.h>int main(){ FILE* fp; //定义一个文件指针 if ((fp = fopen("test.txt", "w+")) == NULL) //打开test.txt { printf("打开文件失败!\n"); } char str[30] = "+qq:380672980"; //定义一个字符串str,初始化为"+qq:380672980" char str1[30]; fputs; //将str写入文件 注意:这里的文件内部指针会变化。 rewind; //将变化后的文件内部指针重置为文件头 fgets(str1,30,fp); //从文件中获取30个字符存放到str1中,结尾补'\0'。 printf("%s\n", str1); fclose; //关闭文件 return 0;}

  
普通文件是指驻留在磁盘或其它外部介质上的一个有序数据集,可以是源文件、目标文件、可执行程序;也可以是一组待输入处理的原始数据,或者是一组输出的结果。对于源文件、目标文件、可执行程序可以称作程序文件,对输入输出数据可称作数据文件。

flag 含义

按数据块读写的函数fread()、fwrite()

3. fprintf(); 和fscanf();

  • 函数原型:int fprintf(FILE * File, const char * Format, ...);``int fscanf(FILE * File, const char * _Format, ...);
  • 案例:

#include <stdio.h>int main(){ FILE *fp; char str[20] = "C++"; char str1[20] = "Python"; char buf[20]; char buf1[20]; char buf2[20]; if ((fp = fopen("test.txt", "w+")) == NULL) { printf("打开文件失败!"); return 0; } fprintf(fp, "%s Vs %s", str, str1); //把str和str按照"%s Vs %s"的格式写入fp rewind; //重置位置 fscanf(fp, "%s %s %s", buf, buf1, buf2); //读取字符串 printf("%s\n%s\n%s\n", buf, buf1, buf2); //打印 fclose; return 0;}

  
设备文件是指与主机相联的各种外部设备,如显示器、打印机、键盘等。在操作系统中,把外部设备也看作是一个文件来进行管理,把它们的输入、输出等同于对磁盘文件的读和写。通常把显示器定义为标准输出文件,
一般情况下在屏幕上显示有关信息就是向标准输出文件输出。如经常使用的printf,putchar函数就是这类输出。键盘通常被指定标准的输入文件,从键盘上输入就意味着从标准输入文件上输入数据,scanf,getchar函数就属于这类输入。

  • 左对齐
  • 允许显示正号
    0 0填充

fwrite(写?文件的数据块的存放地址,一个数据块的字节数,数据块的个数,?件型指针变量);
例如:已知struct student 类型的数组stu[20],则fwrite(&stu[1],
sizeof(struct student ), 2, fp);
如果成功返回写入的数据块的数量 2

4. fread(); 和fwrite();

  • 函数原型:

//从字符串往文件写 Size大小,写Count次。size_t fwrite(const void * Str, size_t Size,size_t Count, FILE * File);//从文件中每次读取ElementSize个字符,一共读取Count次。size_t fread(const void * DstBuf, size_t ElementSize,size_t Count, FILE * File);
  • 案例:

#include <stdio.h>int main(){ FILE *fp; char str[100] = "Is everyone here taday"; char str1[100]; if ((fp = fopen("test.txt", "w+")) == NULL) { printf("打开文件失败!"); return 0; } fwrite(str, 3, 8, fp); rewind; fread(str1, 1, 23, fp); printf("%s\n", str1); fclose; return 0;}

2.从文件编码的方式来看,文件可分为ASCII码文件和二进制码文件两种。

举例:

fread()函数
函数fread()的作?用是从?件中读出成批的数据块
fread(从文件读取的数据块的存放地址,?个数据块的字节数,数据块的个数,文件型指针变量);

  
ASCII文件也称为文本文件,这种文件在磁盘中存放时每个字符对应一个字节,用于存放对应的ASCII码。例如,数5678的存储形式为:

int main(){
  a = 123;
  printf("-9%d",a);
  printf("+9%d",a);
  a = 123;
  printf("+9%d",a);
  printf("9%d",a);
  return 0;
}

已知stu1是?一个结构体struct student变量,则
fread(&stu1, sizeof(struct student ), 1, fp);
?
从?件类型指针fp指向的文件的当前位置开始,读取1个数据块,该数据块为结构体struct
student类型变量所占字节数,然后将读取的内容放入变量stu1中。
? 注意:fwrite()和fread()函数读写文件时,只有使?二
进制方式,才可以读写任何类型的数据。最常?于读写数组和结构体类型数据

ASCII码: 0011010100110110 00110111 00111000
十进制码: 5 6 7 8 共占用4个字节。ASCII码文件可在屏幕上按字符显示,
例如源程序文件就是ASCII文件,用DOS命令TYPE可显示文件的内容。
由于是按字符显示,因此能读懂文件内容。

屏幕上打印为

文件在使用时,指向打开的文件的指针 可以指?文件的当前位置,当每次读
取或写入数据时,是从文件指针的当 前位置开始读取或写入数据的,然后
指针自动移到读写下?个数据的位 置。所以文件指针的定位非常重要。
在C语言的stdio.h头文件中定义的一 些文件操作中常用的库函数。
函数feof()、rewind()、fseek()、 ftell()

二进制文件是按二进制的编码方式来存放文件的。 例如, 数5678的存储形式为:
00010110 00101110只占二个字节。二进制文件虽然也可在屏幕上显示,
但其内容无法读懂。C系统在处理这些文件时,并不区分类型,都看成是字符流,按字节进行处理。
输入输出字符流的开始和结束只由程序控制而不受物理符号(如回车符)的控制。
因此也把这种文件称作“流式文件”。


feof(文件型指针变量);
如果文件型指针指向的文件的当前位置为结束标志EOF,则函数返回
一个?非零值,否则返回0值。

三.文件操作:

123
+123
-123

函数rewind()将令指向文件的指针 重新指向文件的开始位置。函数无
返回值。其调?形式为:
rewind(?件型指针变量);
fseek(?件型指针变量,偏移量,起始位置);

下面介绍流式文件的打开、关闭、读、写、 定位等各种操作。

-123

2>

width或prec               含义
number               最小字符数
*                  下一个参数是字符数
.number          小数点后保留几位
.*            下一个参数是小数点后的位数
举例:
a = 123.4512;
printf(“9.2%d”,a);
printf(“”,a);
//9代表至少有九个字符,2后面保留的小数数位

?
?函数fseek()
函数fseek()可以将使得指向?件的指针变量
指向?件的任何一个位置,实现随机读写文件。它调用的形式为:
fseek(?文件型指针变量,偏移量,起始位 置);
函数fseek()将以文件的起始位置为基准,根据偏移量往前或往后移动指针。其中偏移量
是?个长整型数,表示从起始位置移动的字节数,正数表示指针往后移、负数表示指针往前移。起始位置用数字0、1、2或者用名字SEEK_SET、SEEK_CUR、SEEK_END
代表?文件开始、文件当前位置和文件结束位
置。如果指针设置成功,返回值为0,否则 为非0值。

文件指针:在C语言中用一个指针变量指向一个文件,这个指针称为文件指针。通过文件指针就可对它所指的文件进行各种操作。

屏幕上打印为*

123.45*


3>hlL修饰符
hh 单个字节
h short
l long
ll long long
L long double

4>type
显示类型
%d 整数类型
%f float
%o 八进制
%x 十六进制
e 指数
n 输入输出的个数
例如
printf(“%nd”,1234,&num) //num储存了输出的个数,在这个例子中是4
hh char
h short
l long double

(2)文件的操作

1>文件的打开与创建

用到fopen函数,先看一个例子

FILE *fp = fopen("12.in","r");
if(fp)
{   
    fscanf(fp,"%d",&num);
    fclose(fp);
}
else
{
    //文件打开是失败
}

 

FILE *fopen(char *pname,char *mode)

上述为函数原型,入口参数如下所示

pname:是一个字符指针,它将指向要打开或建立的文件的文件名字符串。
mode:是一个指向文件处理方式字符串的字符指针。所有可能的文件处理方式如下所示


r 打开只读
r+ 打开读写,从文件开头开始
w 打开只写,文件不存在则新建,如果存在则清空
w+ 打开读写,文件不存在则新建,如果存在则清空
a 打开追加
..x 只新建,如果已经存在则无法打开


返回值为状态:
正常返回:被打开文件的文件指针。
异常返回:NULL,表示打开操作不成功。

总结一下

打开文件的作用是:
(1)分配给打开文件一个FILE
类型的文件结构体变量,并将有关信息填入文件结构体变量;
(2)开辟一个缓冲区;
(3)调用操作系统提供的打开文件或建立新文件功能,打开或建立指定文件;

 2>文件的关闭

文件的关闭简单,就是利用fclose函数

int fclose(FILE *fp);

入口参数为文件指针,返回值为状态(正常为0,错误为EOF)

3>文件的读写操作

文件的读写有多种函数。

举一个例子,格式化读写。

格式化写

1.函数原型

int fprintf(FILE *fp,char *format,arg_list)

2.功能说明
  将变量表列(arg_list)中的数据,按照format指出的格式,写入由fp指定的文件。fprintf()函数与printf()函数的功能相同,只是printf()函数是将数据写入屏幕文件(stdout)。
3.入口参数说明
  fp:这是个文件指针,指出要将数据写入的文件。
  format:这是个指向字符串的字符指针,字符串中含有要写出数据的格式,所以该字符串成为格式串。格式串描述的规则与printf()函数中的格式串相同。
  arg_list:是要写入文件的变量表列,各变量之间用逗号分隔。

void main()
{
    char name[10];
    int nAge,nClass;
    long number;
    FILE *fp;
    if((fp=fopen("student.txt","w"))==NULL)
    {
        printf("The file %s can not be opened.\n","student.txt");
        return;
    }
    fscanf(stdin,"%s %d %d %ld",name,&nClass,&nAge,&number);
    fprintf(fp,"%s %5d %4d %8ld",name,nClass,nAge,number);
    fclose(fp);
    if((fp=fopen("student.txt","r"))==NULL)
    {
        printf("The file %s can not be opened.\n","student.txt");
        return;
    }
    fscanf(fp,"%s %d %d %ld",name,&nClass,&nAge,&number);
    printf("name nClass nAge number\n");
    fprintf(stdout,"%-10s%-8d%-6d%-8ld\n",name,nClass,nAge,number);
    fclose(fp);
}

 

下面一篇文章对文件读写做出详细说明

 

函数ftell()
函数ftell()用于测试指向?件的指针的当前位置。它的调?方式为:
ftell(文件型指针变量);
函数的返回值是?个常整型数,如果 测试成功,则返回指向文件的指针当
前指向的位置距离?件开头的字节 数,否则返回-1L

定义说明文件指针的一般形式为: FILE* 指针变量标识符;

算机根据?文件的名字,完成对?文件的操作
C语?言将?文件看作是字符构成的序列…

其中FILE应为大写,它实际上是由系统定义的一个数据结构, 该结构中含有文件名、文件状态和文件当前位置等信息。
在编写源程序时不必关心FILE结构的细节。例如:FILE *fp;
表示fp是指向FILE结构的指针变量,通过fp
即可找存放某个文件信息的结构变量,然后按结构变量提供的信息找到该文件,
实施对文件的操作。习惯上也笼统地把fp称为指向一个文件的指针。

1.文件的打开与关闭概念:

文件在进行读写操作之前要先打开,使用完毕要关闭。

所谓打开文件,实际上是建立文件的各种有关信息,
并使文件指针指向该文件,以便进行其它操作。

关闭文件则断开指针与文件之间的联系,也就禁止再对该文件进行操作。

在C语言中,文件操作都是由库函数来完成的。

2.文件操作函数:

(1)文件打开函数fopen

    fopen函数用来打开一个文件,其调用的一般形式为:文件指针名=fopen(“文件名”,”使用文件方式”);其中,“文件指针名”必须是被说明为FILE
类型的指针变量,“文件名”是被打开文件的文件名。
“使用文件方式”是指文件的类型和操作要求。“文件名”是字符串常量或字符串数组。例如:

FILE *fp;
fp=fopen(“file b”,”r”);

其意义是在当前目录下打开文件file b,
只允许进行“读”操作,并使fp指向该文件。

又如:

FILE *fp
fp=(“c:\\file_a’,”rb”)

其意义是打开C驱动器磁盘的根目录下的文件file_a,这是一个二进制文件,只允许按二进制方式进行读操作。两个反斜线“\\
”中的第一个表示转义字符,第二个表示根目录。”\\”对于打开磁盘下的文件,要注意两个双斜线的正确应用,在表示绝对路径时,每一级目录都要用”\\”,

例如:fp=fopen(“d:\\study\\file_b”,”r+”);

####使用文件的方式共有12种,下面给出了它们的符号和意义。

文件使用方式       意 义
“rt” 只读打开一个文本文件,只允许读数据
“wt” 只写打开或建立一个文本文件,只允许写数据
“at” 追加打开一个文本文件,并在文件末尾写数据
“rb” 只读打开一个二进制文件,只允许读数据
“wb” 只写打开或建立一个二进制文件,只允许写数据
“ab” 追加打开一个二进制文件,并在文件末尾写数据
“rt+” 读写打开一个文本文件,允许读和写
“wt+” 读写打开或建立一个文本文件,允许读写
“at+” 读写打开一个文本文件,允许读,或在文件末追加数据
“rb+” 读写打开一个二进制文件,允许读和写
“wb+” 读写打开或建立一个二进制文件,允许读和写
“ab+” 读写打开一个二进制文件,允许读,或在文件末追加数据

对于文件使用方式有以下几点说明:

a.
文件使用方式由r,w,a,t,b,+六个字符拼成,各字符的含义是:

r(read): 读
w(write): 写
a(append): 追加
t(text): 文本文件,可省略不写
b(banary): 二进制文件
+: 读和写

b.
凡用“r”打开一个文件时,该文件必须已经存在,
且只能从该文件读出。否则报错。

c. 用“w”打开的文件只能向该文件写入。
若打开的文件不存在,则以指定的文件名建立该文件,若打开的文件已经存在,则将该文件删去,重建一个新文件。

d.
若要向一个已存在的文件追加新的信息,只能用“a
”方式打开文件。但此时该文件必须是存在的,否则将会出错。

e.
在打开一个文件时,如果出错,fopen将返回一个空指针值NULL。

在程序中可以用这一信息来判别是否完成打开文件的工作,并作相应的处理。因此常用以下程序段打开文件:

if((fp=fopen("c:\\file_a","rb"))==NULL)
{
printf("\nerror on open c:\\file_a file!");
getch();
exit(1);
}

这段程序的意义是,如果返回的指针为空,表示不能打开C盘根目录下的file_a文件,则给出提示信息“error
on open c:\
file_afile!”,下一行getch()的功能是从键盘输入一个字符,但不在屏幕上显示。在这里,该行的作用是等待,只有当用户从键盘敲任一键时,程序才继续执行,
因此用户可利用这个等待时间阅读出错提示。敲键后执行exit(1)退出程序。

f.
把一个文本文件读入内存时,要将ASCII码转换成二进制码,
而把文件以文本方式写入磁盘时,也要把二进制码转换成ASCII码,因此文本文件的读写要花费较多的转换时间。对二进制文件的读写不存在这种转换。

g. 标准输入文件(键盘),标准输出文件(显示器
),标准出错输出(出错信息)是由系统打开的,可直接使用。

(2)文件关闭函数fClose:

文件一旦使用完毕,应用关闭文件函数把文件关闭,
以避免文件的数据丢失等错误。

fclose函数 调用的一般形式是: fclose(文件指针);

例如: fclose(fp);
正常完成关闭文件操作时,fclose函数返回值为0。如返回非零值则表示有错误发生。

 

C语言文件操作(2)

文件的读写:


对文件的读和写是最常用的文件操作。

在C语言中提供了多种文件读写的函数:

·字符读写函数 :fgetc和fputc

·字符串读写函数:fgets和fputs

·数据块读写函数:fread和fwrite

·格式化读写函数:fscanf和fprinf


下面分别予以介绍。使用以上函数都要求包含头文件stdio.h。字符读写函数fgetC和fputC字符读写函数是以字符(字节)为单位的读写函数。
每次可从文件读出或向文件写入一个字符。

一、读字符函数fgetc

fgetc函数的功能是从指定的文件中读一个字符,函数调用的形式为:
字符变量=fgetc(文件指针);
例如:ch=fgetc(fp);其意义是从打开的文件fp中读取一个字符并送入ch中。

对于fgetc函数的使用有以下几点说明:

  1. 在fgetc函数调用中,读取的文件必须是以读或读写方式打开的。

2.
读取字符的结果也可以不向字符变量赋值,例如:fgetc(fp);但是读出的字符不能保存。

3.
在文件内部有一个位置指针。用来指向文件的当前读写字节。在文件打开时,该指针总是指向文件的第一个字节。使用fgetc
函数后, 该位置指针将向后移动一个字节。
因此可连续多次使用fgetc函数,读取多个字符。
应注意文件指针和文件内部的位置指针不是一回事。文件指针是指向整个文件的,须在程序中定义说明,只要不重新赋值,文件指针的值是不变的。文件内部的位置指针用以指示文件内部的当前读写位置,每读写一次,该指针均向后移动,它不需在程序中定义说明,而是由系统自动设置的。

[例1]读入文件homework.c,在屏幕上输出。

#include<stdio.h>
main()
{
FILE *fp;
char ch;
if((fp=fopen("c:\\study\\homework.c","rt"))==NULL)
{
printf("Cannot open file press any key to exit!");
getch();
exit(1);
}
ch=fgetc(fp);
while (ch!=EOF)
{
putchar(ch);
ch=fgetc(fp);
}
fclose(fp);
}

本例程序的功能是从文件中逐个读取字符,在屏幕上显示。
程序定义了文件指针fp,以读文本文件方式打开文件“homework.c”,
并使fp指向该文件。如打开文件出错,
给出提示并退出程序。程序第12行先读出一个字符,然后进入循环,
只要读出的字符不是文件结束标志(每个文件末有一结束标志EOF-其值为-1)就把该字符显示在屏幕上,再读入下一字符。每读一次,文件内部的位置指针向后移动一个字符,文件结束时,该指针指向EOF。执行本程序将显示整个文件的内容。


二、写字符函数fputc

fputc函数的功能是把一个字符写入指定的文件中,函数调用的形式为:
fputc(字符量,文件指针); 其中,待写入的字符量可以是字符常量或变量,例如:fputc(‘a’,fp);其意义是把字符a写入fp所指向的文件中。

对于fputc函数的使用也要说明几点:

1.
被写入的文件可以用写、读写以及追加方式打开,用写或读写方式打开一个已存在的文件时将清除原有的文件内容,写入字符从

文件首开始。如需保留原有文件内容,希望写入的字符以文件末开始存放,必须以追加方式打开文件。被写入的文件若不存在,则创建该文件。

  1. 每写入一个字符,文件内部位置指针向后移动一个字节。

  2. fputc函数有一个返回值,如写入成功则返回写入的字符,
    否则返回一个EOF。可用此来判断写入是否成功。

[例2]从键盘输入一行字符,写入一个文件,再把该文件内容读出显示在屏幕上。

#include<stdio.h>
main()
{
FILE *fp;
char ch;
if((fp=fopen("c:\\study\\string.txt","wt+"))==NULL)
{
printf("Can not open file press any key to exit!");
getch();
exit(1);
}
printf("input a string:\n");
ch=getchar();
while (ch!='\n')
{
fputc(ch,fp);
ch=getchar();
}
rewind(fp);
ch=fgetc(fp);
while(ch!=EOF)
{
putchar(ch);
ch=fgetc(fp);
}
printf("\n");
fclose(fp);
}

程序中第6行以读写文本文件方式打开文件string。程序第13行从键盘读入一个字符后进入循环,当读入字符不为回车符时,
则把该字符写入文件之中,然后继续从键盘读入下一字符。
每输入一个字符,文件内部位置指针向后移动一个字节。写入完毕,该指针已指向文件末。如要把文件从头读出,须把指针移向文件头,
程序第19行rewind函数用于把fp所指文件的内部位置指针移到文件头.第20至25行用于读出文件中的内容。

[例3]把命令行参数中的前一个文件名标识的文件,复制到后一个文件名标识的文件中,
如命令行中只有一个文件名则把该文件写到标准输出文件(显示器)中。

#include<stdio.h>
main(int argc,char *argv[])
{
FILE *fp1,*fp2;
char ch;
if(argc==1)
{
printf("have not enter file name press any key to exit");
getch();
exit(0);
}
if((fp1=fopen(argv[1],"rt"))==NULL)
{
printf("Cannot open %s\n",argv[1]);
getch();
exit(1);
}
if(argc==2) fp2=stdout;
else if((fp2=fopen(argv[2],"wt+"))==NULL)
{
printf("Cannot open %s\n",argv[1]);
getch();
exit(1);
}
while((ch=fgetc(fp1))!=EOF)
fputc(ch,fp2);
fclose(fp1);
fclose(fp2);
}

   本程序为带参的main函数。程序中定义了两个文件指针 fp1
和fp2,分别指向命令行参数中给出的文件。如命令行参数中没有给出文件名,即运行后输入a.exe则给出提示信息have
not enter file name press any key to exit。
程序第18行表示如果只给出一个文件名,则使fp2指向标准输出文件(即显示器)。程序第25行至28行用循环语句逐个读出文件1中的字符再送到文件2中。再次运行时,给出了一个文件名(由例2所建立的文件),即输入a.exe
string
则输出给标准输出文件stdout,即在显示器上显示文件内容。第三次运行,给出了二个文件名,如输入a.exestring
ok 则把string中的内容读出,写入到OK之中。

    可用DOS命令type显示OK的内容:例如一个程序的main()函数头部形如int
main(int argc, char
*argv[]),编译链接后生成的可执行文件名为a.exe,那么请注意下面的调用:
a.exe abc def
在上面的命令行中,a.exe是要运行的程序名称,后面的abc和def就是传入程序的两个参数,这两个参数就通过argc和argv传入程序,注意这些参数都是字符串。通过上面的调用,在这个程序中,argc=3,a.exe是第一个参数,这个字符串将保存在argv[0]中,abc和def分别是第二和第三个参数,它们分别被保存在argv[1]和argv[2]中。  


·字符串读写函数:fgets和fputs

·数据块读写函数:freed和fwrite

·格式化读写函数:fscanf和fprinf

C语言文件操作(3)

·字符串读写函数:fgets和fputs

·数据块读写函数:freed和fwrite

·格式化读写函数:fscanf和fprinf


字符串读写函数fgets和fputs

一、读字符串函数:
fgets函数的功能是从指定的文件中读一个字符串到字符数组中,函数调用的形式为:
fgets(字符数组名,n,文件指针);其中的n是一个正整数。表示从文件中读出的字符串不超过
n-1个字符。在读入的最后一个字符后加上串结束标志’\0’。例如:fgets(str,n,fp);的意义是从fp所指的文件中读出n-1个字符送入字符数组str中。
[例4]从string.txt文件中读入一个含10个字符的字符串。

#include<stdio.h>
main()
{
FILE *fp;
char str[11];
if((fp=fopen("c:\\study\\string.txt","rt"))==NULL)
{
printf("Cannot open file strike any keyexit!");
getch();
exit(1);
}
fgets(str,11,fp);
printf("%s",str);
fclose(fp);
}

 

本例定义了一个字符数组str共11个字节,在以读文本文件方式打开文件string.txt后,从中读出10个字符送入str数组,在数组最后一个单元内将加上’\0’,然后在屏幕上显示输出str数组。输出的十个字符正是例2程序的前十个字符。
对fgets函数有两点说明:

  1. 在读出n-1个字符之前,如遇到了换行符或EOF,则读出结束。
  2. fgets函数也有返回值,其返回值是字符数组的首地址。
    二、写字符串函数fputs:
    fputs函数的功能是向指定的文件写入一个字符串,其调用形式为:
    fputs(字符串,文件指针) 其中字符串可以是字符串常量,也可以是字符数组名,
    或指针变量,例如:
    fputs(“abcd”,fp);
    其意义是把字符串“abcd”写入fp所指的文件之中。
    [例10.5]在例2中建立的文件string中追加一个字符串。
#include<stdio.h>
main()
{
FILE *fp;
char ch,st[20];
if((fp=fopen("c:\\study\\string.txt","at+"))==NULL)
{
printf("Cannot open file strike any keyexit!");
getch();
exit(1);
}
printf("input a string:\\n");
scanf("%s",st);
fputs(st,fp);
rewind(fp);
ch=fgetc(fp);
while(ch!=EOF)
{
putchar(ch);
ch=fgetc(fp);
}
printf("\n");
fclose(fp);
}

本例要求在string文件末加写字符串,因此,在程序第6行以追加读写文本文件的方式打开文件string
。然后输入字符串,
并用fputs函数把该串写入文件string。在程序15行用rewind函数把文件内部位置指针移到文件首。再进入循环逐个显示当前文件中的全部内容。
数据块读写函数fread和fwrite:
C语言还提供了用于整块数据的读写函数。可用来读写一组数据,如一个数组元素,一个结构变量的值等。读数据块函数调用的一般形式为: fread(buffer,size,count,fp);
写数据块函数调用的一般形式为:fwrite(buffer,size,count,fp); 其中buffer是一个指针,在fread函数中,它表示存放输入数据的首地址。在fwrite函数中,它表示存放输出数据的首地址。
size 表示数据块的字节数。count 表示要读写的数据块块数。fp
表示文件指针。
例如:fread(fa,4,5,fp);
其意义是从fp所指的文件中,每次读4个字节(一个实数)送入实数组fa中,连续读5次,即读5个实数到fa中。
[例6]从键盘输入两个学生数据,写入一个文件中,再读出这两个学生的数据显示在屏幕上。

#include<stdio.h>
struct stu
{
char name[10];
int num;
int age;
char addr[15];
}boya[2],boyb[2],*pp,*qq;
main()
{
FILE *fp;
char ch;
int i;
pp=boya;
qq=boyb;
if((fp=fopen("stu_list","wb+"))==NULL)
{
printf("Cannot open file strike any keyexit!");
getch();
exit(1);
}
printf("\ninput data\n");
for(i=0;i<2;i++,pp++)
scanf("%s%d%d%s",pp->name,&pp->num,&pp->age,pp->addr);
pp=boya;
fwrite(pp,sizeof(struct stu),2,fp);
rewind(fp);
fread(qq,sizeof(struct stu),2,fp);
printf("\nname:\nnumber:\nage:\naddr:\n");
for(i=0;i<2;i++,qq++)
printf("%s\t%5d%7d%s\n",qq->name,qq->num,qq->age,qq->addr);
fclose(fp);
}

 

  本例程序定义了一个结构stu,说明了两个结构数组boya和
boyb以及两个结构指针变量pp和qq。pp指向boya,qq指向boyb。程序第16行以读写方式打开二进制文件“stu_list”,输入二个学生数据之后,写入该文件中,然后把文件内部位置指针移到文件首,读出两块学生数据后,在屏幕上显示。
格式化读写函数fscanf和fprintf:
  
fscanf函数,fprintf函数与前面使用的scanf和printf
函数的功能相似,都是格式化读写函数。两者的区别在于fscanf函数和fprintf函数的读写对象不是键盘和显示器,而是磁盘文件。这两个函数的调用格式为:
fscanf(文件指针,格式字符串,输入表列);
fprintf(文件指针,格式字符串,输出表列); 例如:
fscanf(fp,”%d%s”,&i,s);
fprintf(fp,”%d%c”,j,ch);
用fscanf和fprintf函数也可以完成例6的问题。修改后的程序如例10.7所示。
[例7]

#include<stdio.h>
struct stu
{
char name[10];
int num;
int age;
char addr[15];
}boya[2],boyb[2],*pp,*qq;
main()
{
FILE *fp;
char ch;
int i;
pp=boya;
qq=boyb;
if((fp=fopen("stu_list","wb+"))==NULL)
{
printf("Cannot open file strike any keyexit!");
getch();
exit(1);
}
printf("\ninput data\n");
for(i=0;i<2;i++,pp++)
scanf("%s%d%d%s",pp->name,&pp->num,&pp->age,pp->addr);
pp=boya;
for(i=0;i<2;i++,pp++)
fprintf(fp,"%s %d %d%s\n",pp->name,pp->num,pp->age,pp->addr);
rewind(fp);
for(i=0;i<2;i++,qq++)
fscanf(fp,"%s %d %d%s\\n",qq->name,&qq->num,&qq->age,qq->addr);
printf("\nname\tnumber age addr\n");
qq=boyb;
for(i=0;i<2;i++,qq++)
printf("%s\t%5d%7d%s\n",qq->name,qq->num,qq->age,qq->addr);
fclose(fp);
}

 

与例6相比,本程序中fscanf和fprintf函数每次只能读写一个结构数组元素,因此采用了循环语句来读写全部数组元素。还要注意指针变量pp,qq由于循环改变了它们的值,因此在程序的25和32行分别对它们重新赋予了数组的首地址。

二、C语言文件操作(4)

前面介绍的对文件的读写方式都是顺序读写,
即读写文件只能从头开始,顺序读写各个数据。但在实际问题中常要求只读写文件中某一指定的部分。为了解决这个问题可移动文件内部的位置指针到需要读写的位置,再进行读写,这种读写称为随机读写。
实现随机读写的关键是要按要求移动位置指针,这称为文件的定位。移动文件内部位置指针的函数主要有两个,即
rewind 函数和fseek函数。
rewind函数前面已多次使用过,其调用形式为: rewind(文件指针);
它的功能是把文件内部的位置指针移到文件首。
下面主要介绍fseek函数。
   fseek函数用来移动文件内部位置指针,其调用形式为: fseek(文件指针,位移量,起始点);其中:“文件指针”指向被移动的文件。“位移量”表示移动的字节数,要求位移量是long型数据,以便在文件长度大于64KB
时不会出错。当用常量表示位移量时,要求加后缀“L”,“起始点”表示从何处开始计算位移量,规定的起始点有三种:文件首,当前位置和文件尾。
其表示方法如下表
起始点     表示符号    数字表示
──────────────────────────
文件首    SEEK—SET     0
当前位置   SEEK—CUR      1
文件末尾   SEEK—END     2
例如:fseek(fp,100L,0);其意义是把位置指针移到离文件首100个字节处。还要说明的是fseek函数一般用于二进制文件。在文本文件中由于要进行转换,故往往计算的位置会出现错误。文件的随机读写在移动位置指针之后,即可用前面介绍的任一种读写函数进行读写。由于一般是读写一个数据据块,因此常用fread和fwrite函数。下面用例题来说明文件的随机读写。
[例8]在学生文件stu list中读出第二个学生的数据。

#include<stdio.h>
struct stu
{
char name[10];
int num;
int age;
char addr[15];
}boy,*qq;
main()
{
FILE *fp;
char ch;
int i=1;
qq=&boy;
if((fp=fopen("stu_list","rb"))==NULL)
{
printf("Cannot open filestrike any key exit!");
getch();
exit(1);
}
rewind(fp);
fseek(fp,i*sizeof(struct stu),0);
fread(qq,sizeof(struct stu),1,fp);
printf("\n\nname\tnumber ageaddr\n");
printf("%s\t%5d %7d%s\n",qq->name,qq->num,qq->age,qq->addr);
}

 


 文件stu_list已由例6的程序建立,本程序用随机读出的方法读出第二个学生的数据。程序中定义boy为stu类型变量,qq为指向boy的指针。以读二进制文件方式打开文件,程序第22行移动文件位置指针。其中的i值为1,表示从文件头开始,移动一个stu类型的长度,然后再读出的数据即为第二个学生的数据。

文件检测函数

C语言中常用的文件检测函数有以下几个。
一、文件结束检测函数feof函数调用格式: feof(文件指针);
功能:判断文件是否处于文件结束位置,如文件结束,则返回值为1,否则为0。

二、读写文件出错检测函数ferror函数调用格式: ferror(文件指针);
功能:检查文件在用各种输入输出函数进行读写时是否出错。
如ferror返回值为0表示未出错,否则表示有错。

三、文件出错标志和文件结束标志置0函数 clearerr函数调用格式:
clearerr(文件指针);
功能:本函数用于清除出错标志和文件结束标志,使它们为0值。

C库文件

C系统提供了丰富的系统文件,称为库文件,C的库文件分为两类,一类是扩展名为”.h”的文件,称为头文件,在前面的包含命令中我们已多次使用过。在”.h”文件中包含了常量定义、
类型定义、宏定义、函数原型以及各种编译选择设置等信息。另一类是函数库,包括了各种函数的目标代码,供用户在程序中调用。通常在程序中调用一个库函数时,要在调用之前包含该函数原型所在的”.h”
文件。
在附录中给出了全部库函数
ALLOC.H    说明内存管理函数(分配、释放等)。
ASSERT.H    定义 assert调试宏。
BIOS.H     说明调用IBM—PC ROM BIOS子程序的各个函数。
CONIO.H    说明调用DOS控制台I/O子程序的各个函数。
CTYPE.H    包含有关字符分类及转换的名类信息(如
isalpha和toascii等)。
DIR.H     包含有关目录和路径的结构、宏定义和函数。
DOS.H     定义和说明MSDOS和8086调用的一些常量和函数。
ERRON.H    定义错误代码的助记符。
FCNTL.H    定义在与open库子程序连接时的符号常量。
FLOAT.H    包含有关浮点运算的一些参数和函数。
GRAPHICS.H  
说明有关图形功能的各个函数,图形错误代码的常量定义,正对不同驱动程序的各种颜色值,及函数用到的一些特殊结构。
IO.H      包含低级I/O子程序的结构和说明。
LIMIT.H    包含各环境参数、编译时间限制、数的范围等信息。
MATH.H     说明数学运算函数,还定了 HUGE VAL
宏,说明了matherr和matherr子程序用到的特殊结构。
MEM.H     说明一些内存操作函数(其中大多数也在STRING.H中说明)。
PROCESS.H   说明进程管理的各个函数,spawn…和EXEC …函数的结构说明。
SETJMP.H    定义longjmp和setjmp函数用到的jmp buf类型,
说明这两个函数。
SHARE.H    定义文件共享函数的参数。
SIGNAL.H    定义SIG[ZZ(Z]
[ZZ)]IGN和SIG[ZZ(Z][ZZ)]DFL常量,说明rajse和signal两个函数。
STDARG.H    定义读函数参数表的宏。(如vprintf,vscarf函数)。
STDDEF.H    定义一些公共数据类型和宏。
STDIO.H    定义Kernighan和Ritchie在Unix System V
中定义的标准和扩展的类型和宏。还定义标准I/O
预定义流:stdin,stdout和stderr,说明 I/O流子程序。
STDLIB.H    说明一些常用的子程序:转换子程序、搜索/ 排序子程序等。
STRING.H    说明一些串操作和内存操作函数。
SYS\\STAT.H   定义在打开和创建文件时用到的一些符号常量。
SYS\\TYPES.H  说明ftime函数和timeb结构。
SYS\\TIME.H   定义时间的类型time[ZZ(Z] [ZZ)]t。
TIME.H    
定义时间转换子程序asctime、localtime和gmtime的结构,ctime、difftime、
gmtime、 localtime和stime用到的类型,并提供这些函数的原型。
VALUE.H    定义一些重要常量, 包括依赖于机器硬件的和为与Unix System
V相兼容而说明的一些常量,包括浮点和双精度值的范围

三、1,两种文件存取方式(输入,输出方式)
顺序存取
直接存取
2,数据的两种存放形式
文本文件
二进制文件
13.2文件指针
定义文件类型指针变量的一般形式:
FILE *指针变量名;
例如:
FILE *fp1,*fp2;
13.3打开文件
在使用文件之前,需打开文件.在C里使用fopen函数打开文件.格式为:
fopen(文件名,文件使用方式);
此函数返回一个指向FILE类型的指针.如:
FILE *fp;
fp=fopen(“file_1″,”r”);
如果调用成功,fp就指向file_1,否则返回为NULL,所以为了保证文件的正确使用,要进行测试.采用如下语句:
If((fp=fopen(“file_1″,”r”))==NULL)
{
printf(“Cannot open this file\n”);
exit(0);
}
最常用的文件使用方式及其含义如下:
1,”r”.为读而打开文本文件.(不存在则出错)
2,”rb”.为读而打开二进制文件.
3,”w”.为写而打开文本文件.(若不存在则新建,反之,则从文件起始位置写,原内容将被覆盖)
4,”wb”.为写而打开二进制文件.
5,”a”.为在文件后面添加数据而打开文本文件.(若不存在,则新建;反之,在原文件后追加)
6,”ab”.为在文件后面添加数据而打开一个二进制文件.
最常用的文件使用方式及其含义如下:
7,”r+”.为读和写而打开文本文件.(读时,从头开始;在写数据时,新数据只覆盖所占的空间,其后不变)
8,”rb+”.为读和写而打开二进制文件.只是在随后的读写时,可以由位置函数设置读和写的起始位置.
9,”w+”.首先建立一个新文件,进行写操作,随后可以从头开始读.(若文件存在,原内容将全部消失)
10,”wb+”.功能与”w+”同.只是在随后的读写时,可以由位置函数设置读和写的起始位置.
最常用的文件使用方式及其含义如下:
11,”a+”.功能与”a”相同;只是在文件尾部添加新的数据后,可以从头开始读.
12,”ab+”.功能与”a+”相同;只是在文件尾部添加新数据之后,可以由位置函数设置开始读的起始位置.
“rt”     只读打开一个文本文件,只允许读数据
“wt”   只写打开或建立一个文本文件,只允许写数据
“at”   追加打开一个文本文件,并在文件末尾写数据
“rb”  只读打开一个二进制文件,只允许读数据
“wb”  只写打开或建立一个二进制文件,只允许写数据
“ab”     追加打开一个二进制文件,并在文件末尾写数据
“rt+”    读写打开一个文本文件,允许读和写。用fseek确定读写位置,写多少覆盖多少,后面的内容保留。因为磁盘空间是连续的,所以你不能在中间插入,在中间一旦写入就是覆盖与写入内容等长的那些内容。
“wt+”    读写打开或建立一个文本文件,允许读写
“at+”    读写打开一个文本文件,允许读,或在文件末追加数据
“rb+”    读写打开一个二进制文件,允许读和写
“wb+”    读写打开或建立一个二进制文件,允许读和写
“ab+”     读写打开一个二进制文件,允许读,或在文件末追加数据

发表评论

电子邮件地址不会被公开。 必填项已用*标注