代码改变世界

C语言文件操作

2012-04-15 16:59  java环境变量  阅读(288)  评论(0编辑  收藏  举报

13.3 文件的打开与关闭文件在进行读写操作之前要先打开,使用完毕要关闭。所谓打开文件,实际上是建立文件的各种有关信息,并使文件指针指向该文件,以便进行其它操作。关闭文件则断开指针与文件之间的联系,也就禁止再对该文件进行操作。

  在C语言中,文件操作都是由库函数来完成的。在本章内将介绍主要的文件操作函数。

  13.3.1 文件的打开(fopen函数)

  fopen函数用来打开一个文件,其调用的一般形式为:

  文件指针名=fopen(文件名,使用文件方式);

  其中,

  “文件指针名”必须是被说明为FILE 类型的指针变量:“文件名”是被打开文件的文件名:“使用文件方式”是指文件的类型和操作要求。

  “文件名”是字符串常量或字符串数组。

  例如:

  FILE *fp;fp=("file a","r");

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

  又如:

  FILE *fphzk fphzk=("c:\\hzk16","rb")

  其意义是打开C驱动器磁盘的根目录下的文件hzk16,这是一个二进制文件,只允许按二进制方式进行读操作。两个反斜线“\\ ”中的第一个表示转义字符,第二个表示根目录。

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

  文件使用方式意义

  “rt”

  只读打开一个文本文件,只允许读数据

  “wt”

  只写打开或建立一个文本文件,只允许写数据

  “at”

  追加打开一个文本文件,并在文件末尾写数据

  “rb”

  只读打开一个二进制文件,只允许读数据

  “wb”

  只写打开或建立一个二进制文件,只允许写数据

  “ab”

  追加打开一个二进制文件,并在文件末尾写数据

  “rt+”

  读写打开一个文本文件,允许读和写

  “wt+”

  读写打开或建立一个文本文件,允许读写

  “at+”

  读写打开一个文本文件,允许读,或在文件末追加数据

  “rb+”

  读写打开一个二进制文件,允许读和写

  “wb+”

  读写打开或建立一个二进制文件,允许读和写

  “ab+”

  读写打开一个二进制文件,允许读,或在文件末追加数据

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

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

  r(read): 读

  w(write): 写

  a(append): 追加

  t(text): 文本文件,可省略不写

  b(banary): 二进制文件

  +: 读和写

  2) 凡用“r”打开一个文件时,该文件必须已经存在,且只能从该文件读出。

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

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

  5) 在打开一个文件时,如果出错,fopen将返回一个空指针值NULL.在程序中可以用这一信息来判别是否完成打开文件的工作,并作相应的处理。因此常用以下程序段打开文件:

  6) if((fp=fopen("c:\\hzk16","rb")==NULL)

  {

  printf("\nerror on open c:\\hzk16 file!");

  getch();

  exit(1);

  }

  这段程序的意义是,如果返回的指针为空,表示不能打开C盘根目录下的hzk16文件,则给出提示信息“error on open c:\ hzk16 file!”,下一行getch()的功能是从键盘输入一个字符,但不在屏幕上显示。在这里,该行的作用是等待,只有当用户从键盘敲任一lose函数返回值为0.如返回非零值则表示有错误发生。

  13.4 文件的读写对文件的读和写是最常用的文件操作。在C语言中提供了多种文件读写的函数:

  。字符读写函数 :fgetc和fputc.字符串读写函数:fgets和fputs.数据块读写函数:fread和fwrite.格式化读写函数:fscanf和fprinf

  下面分别予以介绍。使用以上函数都要求包含头文件stdio.h.

  13.4.1 字符读写函数fgetc和fputc字符读写函数是以字符(字节)为单位的读写函数。每次可从文件读出或向文件写入一个字符。

  1. 读字符函数fgetc fgetc函数的功能是从指定的文件中读一个字符,函数调用的形式为:字符变量=fgetc(文件指针);

  例如:

  ch=fgetc(fp);

  其意义是从打开的文件fp中读取一个字符并送入ch中。

  对于fgetc函数的使用有以下几点说明:1) 在fgetc函数调用中,读取的文件必须是以读或读写方式打开的。

  2) 读取字符的结果也可以不向字符变量赋值,

  例如:fgetc(fp);但是读出的字符不能保存。

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

  「例13.1」读入文件c1.doc,在屏幕上输出。

  #include<stdio.h> main()

  { FILE *fp;char ch;if((fp=fopen("d:\\jrzh\\example\\c1.txt","rt"))==NULL)

  { printf("\nCannot open file strike any key exit!");getch();exit(1);} ch=fgetc(fp);while(ch!=EOF)

  { putchar(ch);ch=fgetc(fp);} fclose(fp);}

  本例程序的功能是从文件中逐个读取字符,在屏幕上显示。程序定义了文件指针fp,以读文本文件方式打开文件“d:\\jrzh\\example\\ex1_1.c”,并使fp指向该文件。如打开文件出错,给出提示并退出程序。程序第12行先读出一个字符,然后进入循环,只要读出的字符不是文件结束标志(每上,再读入下一字符。每读一次,文件内部的位置指针向后??本程序将显示整个文件。

  2. 写字符函数fputc

  fputc函数的功能??为:

  fputc(字符量,文件指量或变量,例如:

  fputc('a',fp);

  其意putc函数的使用也要说明几点:

  1) 被写入的文件可以用写、读写、追加方式打开,用写或读写方式??写入字符从文件首开始。如需保留原有文件内容,希望写入的字??被写入的文件若不存在,则创建该文件。

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

  3) fputc函数有一个返回值,如写入成功则返回写入的字符,否则返回一个EOF.可用此来判断写符,写入一个文件,再把该文件内容读出显示在屏幕上。

  #include<stdio.h>

  main()

  {

  FILE *fp;

  char ch;

  if((fp=fopen("d:\\jrzh\\example\\string","wt+"))==NULL)

  {

  printf("Cannot open file strike any key 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行用于读出文件中的一行内容。

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

  #include<stdio.h>

  main(int argc,char *argv[])

  {

  FILE *fp1,*fp2;

  char ch;

  if(argc==1)

  {

  printf("have not enter file name strike any key exit");

  getch();

  exit(0);

  }

  if((fp1=fopen(argv[1],"rt"))==NULL)

  {

  printf("Cannot open %s\n",argv[1]);

  }

  在ANSI C中,对文件的操作分为两种方式,即流式文件操作和I/O文件操作,下面就分别介绍之。

  一、流式文件操作这种方式的文件操作有一个重要的结构FILE,FILE在stdio.h中定义如下:typedef struct { int level; /* fill/empty level of buffer */ unsigned flags; /* File status flags */ char fd; /* File descriptor */ unsigned char hold; /* Ungetc char if no buffer */ int bsize; /* Buffer size */ unsigned char _FAR *buffer; /* Data transfer buffer */ unsigned char _FAR *curp; /* Current active pointer */ unsigned istemp; /* Temporary file indicator */ short token; /* Used for validity checking */ } FILE; /* This is the FILE object */ FILE这个结构包含了文件操作的基本属性,对文件的操作都要通过这个结构的指针来进行,此种文件操作常用的函数见下表 函数 功能fopen() 打开流fclose() 关闭流fputc() 写一个字符到流中fgetc() 从流中读一个字符fseek() 在流中定位到指定的字符fputs() 写字符串到流fgets() 从流中读一行或指定个字符fprintf() 按格式输出到流fscanf() 从流中按格式读取feof() 到达文件尾时返回真值ferror() 发生错误时返回其值rewind() 复位文件定位器到文件开始处remove() 删除文件fread() 从流中读指定个数的字符fwrite() 向流中写指定个数的字符tmpfile() 生成一个临时文件流tmpnam() 生成一个唯一的文件名下面就介绍一下这些函数1.fopen()

  fopen的原型是:FILE *fopen(const char *filename,const char *mode),fopen实现三个功能为使用而打开一个流 ;把一个文件和此流相连接;给此流返回一个FILR指针参数filename指向要打开的文件名,mode表示打开状态的字符串,其取值如下表"r" 以只读方式打开文件                   "w" 以只写方式打开文件               "a" 以追加方式打开文件"r+" 以读/写方式打开文件,如无文件出错             "w+" 以读/写方式打开文件,如无文件生成新文件一个文件可以以文本模式或二进制模式打开,这两种的区别是:在文本模式中回车被当成一个字符'n',而二进制模式认为它是两个字符0x0D,0x0A;如果在文件中读到0x1B,文本模式会认为这是文件结束符,也就是二进制模型不会对文件进行处理,而文本方式会按一定的方式对数据作相应的转换。

  系统默认的是以文本模式打开,可以修改全部变量_fmode的值来修改这个设置,例如_fmode=O_TEXT;就设置默认打开方式为文本模式;而_fmode=O_BINARY;则设置默认打开方式是二进制模式。

  我们也可以在模式字符串中指定打开的模式,如"rb"表示以二进制模式打开只读文件,"w+t"或"wt+"表示以文本模式打开读/写文件。

  此函数返回一个FILE指针,所以申明一个FILE指针后不用初始化,而是用fopen()来返回一个指针并与一个特定的文件相连,如果成败,返回NULL.例:  FILE *fp;if(fp=fopen("123.456","wb"))

  puts("打开文件成功");else puts("打开文件成败");2.fclose() 的功能就是关闭用fopen()打开的文件,其原型是:int fclose(FILE *fp);如果成功,返回0,失败返回EOF.在程序结束时一定要记得关闭打开的文件,不然可能会造成数据丢失的情况,我以前就经常犯这样的毛病。

  3.fputc()向流写一个字符,原型是int fputc(int c, FILE *stream); 成功返回这个字符,失败返回EOF.例:fputc('X',fp);4.fgetc()从流中读一个字符,原型是int fputc(FILE *stream); 成功返回这个字符,失败返回EOF.例:char ch1=fgetc(fp);5. fseek()此函数一般用于二进制模式打开的文件中,功能是定位到流中指定的位置,原型是int fseek(FILE *stream, long offset, int whence);如果成功返回0,参数offset是移动的字符数,whence是移动的基准,取值是符号常量 值 基准位置SEEK_SET 0 文件开头SEEK_CUR 1 当前读写的位置SEEK_END 2 文件尾部例:fseek(fp,1234L,SEEK_CUR);//把读写位置从当前位置向后移动1234字节(L后缀表示长整数)

  fseek(fp,0L,2);//把读写位置移动到文件尾6.fputs() 写一个字符串到流中,原型int fputs(const char *s, FILE *stream);例:fputs("I Love You",fp);7.fgets()从流中读一行或指定个字符,原型是char *fgets(char *s, int n, FILE *stream); 从流中读取n-1个字符,除非读完一行,参数s是来接收字符串,如果成功则返回s的指针,否则返回NULL.例:如果一个文件的当前位置的文本如下Love ,I Have But ……

  如果用 fgets(str1,4,file1);  则执行后str1="Lov",读取了4-1=3个字符,而如果用fgets(str1,23,file1);则执行str="Love ,I Have",读取了一行(不包括行尾的'n')。

  8.fprintf()按格式输入到流,其原型是int fprintf(FILE *stream, const char *format[, argument, ……]);其用法和printf()相同,不过不是写到控制台,而是写到流罢了。例:fprintf(fp,"%2d%s",4,"Hahaha");9.fscanf() 从流中按格式读取,其原型是int fscanf(FILE *stream, const char *format[, address, ……]);其用法和scanf()相同,不过不是从控制台读取,而是从流读取罢了。例:fscanf(fp,"%d%d" ,&x,&y);10.feof() 检测是否已到文件尾,是返回真,否则返回0,其原型是int feof(FILE *stream);例:if(feof(fp))printf("已到文件尾");11.ferror() 原型是int ferror(FILE *stream);返回流最近的错误代码,可用clearerr()来清除它,clearerr()的原型是void clearerr(FILE *stream); 例:printf("%d",ferror(fp));12.rewind()把当前的读写位置回到文件开始,原型是void rewind(FILE *stream);其实本函数相当于fseek(fp,0L,SEEK_SET);例:rewind(fp);12.remove() 删除文件,原型是int remove(const char *filename); 参数就是要删除的文件名,成功返回0.例:remove("c:io.sys");13.fread()  从流中读指定个数的字符,原型是size_t fread(void *ptr, size_t size, size_t n, FILE *stream);参数ptr是保存读取的数据,void*的指针可用任何类型的指针来替换,如char*、int *等等来替换;size是每块的字节数;n是读取的块数,如果成功,返回实际读取的块数(不是字节数),本函数一般用于二进制模式打开的文件中。

  例:char x[4230];FILE *file1=fopen("c:msdos.sys","r");fread(x,200,12 ,file1);//共读取200*12=2400个字节14.fwrite() 与fread对应,向流中写指定的数据,原型是size_t fwrite(const void *ptr, size_t size, size_t n, FILE *stream);参数ptr是要写入的数据指针,void*的指针可用任何类型的指针来替换,如char*、int *等等来替换;size是每块的字节数;n是要写的块数,如果成功,返回实际写入的块数(不是字节数),本函数一般用于二进制模式打开的文件中。

  例:char x[]="I Love You";fwire(x, 6,12,fp);//写入6*12=72字节将把"I Love"写到流fp中12次,共72字节15.tmpfile() 其原型是FILE *tmpfile(void); 生成一个临时文件,以"w+b"的模式打开,并返回这个临时流的指针,如果失败返回NULL.在程序结束时,这个文件会被自动删除。例:FILE *fp=tmpfile();16.tmpnam();其原型为char *tmpnam(char *s); 生成一个唯一的文件名,其实tmpfile()就调用了此函数,参数s用来保存得到的文件名,并返回这个指针,如果失败,返回NULL.例:tmpnam(str1);二、直接I/O文件操作这是C提供的另一种文件操作,它是通过直接存/取文件来完成对文件的处理,而上篇所说流式文件操作是通过缓冲区来进行;流式文件操作是围绕一个FILE 指针来进行,而此类文件操作是围绕一个文件的“句柄”来进行,什么是句柄呢?它是一个整数,是系统用来标识一个文件(在WINDOWS中,句柄的概念扩展到所有设备资源的标识)的唯一的记号。此类文件操作常用的函数如下表,这些函数及其所用的一些符号在io.h和fcntl.h中定义,在使用时要加入相应的头文件。

  函数 说明open() 打开一个文件并返回它的句柄close() 关闭一个句柄lseek() 定位到文件的指定位置read() 块读文件write() 块写文件eof() 测试文件是否结束filelength() 取得文件长度rename() 重命名文件chsize() 改变文件长度下面就对这些函数一一说明:1.open()打开一个文件并返回它的句柄,如果失败,将返回一个小于0的值,原型是int open(const char *path, int access [, unsigned mode]); 参数path是要打开的文件名,access是打开的模式,mode是可选项。表示文件的属性,主要用于UNIX系统中,在DOS/WINDOWS这个参数没有意义。其中文件的打开模式如下表。

  符号 含义 符号 含义 符号 含义O_RDONLY 只读方式 O_WRONLY 只写方式 O_RDWR 读/写方式O_NDELAY 用于UNIX系统 O_APPEND 追加方式 O_CREAT 如果文件不存在就创建O_TRUNC 把文件长度截为0 O_EXCL 和O_CREAT连用,如果文件存在返回错误 O_BINARY 二进制方式O_TEXT 文本方式对于多个要求,可以用"|"运算符来连接,如O_APPEND|O_TEXT表示以文本模式和追加方式打开文件。

  例:int handle=open("c:msdos.sys",O_BINARY|O_CREAT|O_WRITE)

  2.close()关闭一个句柄,原型是int close(int handle);如果成功返回0 .例:close(handle)

  3.lseek() 定位到指定的位置,原型是:long lseek(int handle, long offset, int fromwhere);参数offset是移动的量,fromwhere是移动的基准位置,取值和前面讲的fseek()一样,SEEK_SET:文件首部;SEEK_CUR:文件当前位置;SEEK_END:文件尾。此函数返回执行后文件新的存取位置。

  例:lseek(handle,-1234L,SEEK_CUR);//把存取位置从当前位置向前移动1234个字节。

  x=lseek(hnd1,0L,SEEK_END);//把存取位置移动到文件尾,x=文件尾的位置即文件长度4.read() 从文件读取一块,原型是int read(int handle, void *buf, unsigned len);参数buf保存读出的数据,len是读取的字节。函数返回实际读出的字节。例:char x[200];read(hnd1,x,200);5.write()写一块数据到文件中,原型是int write(int handle, void *buf, unsigned len);参数的含义同read(),返回实际写入的字节。例:char x[]="I Love You";write(handle,x,strlen(x));7.eof()类似feof(),测试文件是否结束,是返回1,否则返回0;原型是:int eof(int handle);例:while(!eof(handle1)){……};8.filelength() 返回文件长度,原型是long filelength(int handle);相当于lseek(handle,0L,SEEK_END)

  例:long x=filelength(handle);9.rename() 重命名文件,原型是int rename(const char *oldname, const char *newname); 参数oldname是旧文件名,newname是新文件名。成功返回0 .例:rename("c:config.sys","c:config.w40");10.chsize(); 改变文件长度,原型是int chsize(int handle, long size);参数size表示文件新的长度,成功返回0,否则返回-1,如果指定的长度小于文件长度,则文件被截短;如果指定的长度大于文件长度,则在文件后面补''.例:chsize(handle,0x12345);如果熟悉汇编可能会发现这种方式和汇编语言的DOS功能调用句柄式文件操作很像,比如open()就像DOS服务的3CH号功能调用,其实这种操作还有两种类型的函数就是直接用DOS功能来完成的,如_open(),_dos_open()等等。有兴趣可自已查询BCB的帮助。

  同流式文件操作相同,这种也提供了Unicode字符操作的函数,如_wopen()等等,用于9X/NT下的宽字符编程,有兴趣可自已查询BCB的帮助。

  另外,此种操作还有lock(),unlock(),locking()等用于多用户操作的函数,但在BCB中用得并不多,我就不介绍了,但如果要用C来写CGI,这些就必要的常识了,如果你有这方面的要求,那就得自已好好看帮助了。

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