1 / 49

第 10 章 文件操作

C 语言大学实用教程. 第 10 章 文件操作. 哈尔滨工业大学计算机科学与技术学院 苏小红 sxh@hit.edu.cn. I/O 设备. 输入设备 键盘、鼠标 软盘、硬盘、光驱(以文件的形式) 串行口、并行口、 USB 接口、网络端口 扫描仪、视频采集卡、电视卡、游戏杆、话筒 …… 输出设备 显示器、打印机 软盘、硬盘、 CD-R/DVD-R (以文件的形式) 串行口、并行口、 USB 接口、网络端口 音箱 …… 单纯的输入设备或者单纯的输出设备越来越少. 标准输入输出. 字符界面的操作系统一般都提供标准输入与输出设备

Download Presentation

第 10 章 文件操作

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. C语言大学实用教程 第10章 文件操作 哈尔滨工业大学计算机科学与技术学院 苏小红sxh@hit.edu.cn

  2. I/O设备 • 输入设备 • 键盘、鼠标 • 软盘、硬盘、光驱(以文件的形式) • 串行口、并行口、USB接口、网络端口 • 扫描仪、视频采集卡、电视卡、游戏杆、话筒 • …… • 输出设备 • 显示器、打印机 • 软盘、硬盘、CD-R/DVD-R(以文件的形式) • 串行口、并行口、USB接口、网络端口 • 音箱 • …… • 单纯的输入设备或者单纯的输出设备越来越少

  3. 标准输入输出 • 字符界面的操作系统一般都提供标准输入与输出设备 • DOS、Linux、Unix…… • 一般情况,标准输入就是键盘,标准输出就是终端显示器 • 操作系统有能力重定向标准输入与输出,比如让文件作为标准输入,打印机作为标准输出 • 这种重定向程序本身是感觉不到的 自来水厂

  4. DOS下的标准输入输出重定向 • 程序prog如下 • main(){char c;while ((c=getchar()) != '\n') putchar(++c);} • 输入重定向 • prog < infile • 输出重定向 • prog > outfile

  5. 流(Stream) • 水流 • “子在川上曰:逝者如斯夫” • 计算机中的流的概念 • 一般称为数据流,也有叫做字节流、比特流的,还有很具体的文件流、视频流、音频流等 • 时光不能倒流,但计算机中的很多流都是会倒流的 • 如果你想重新读已经读过的数据,或者要修改已经写入的数据,可以发出流控(Flow Control)命令 • 不会倒流的数据流也很多,例如网络上的数据流。网络和数据线等介质只有很小的数据缓冲区,没有大量存储的能力

  6. 文件(File)的概念 • 计算机的内存容易健忘,所以数据必须保存在硬盘、软盘、光盘和磁带等“不健忘”的外存上 • 这些能大量、永久保存信息的媒介,一般都以文件的形式给用户及应用程序使用 • 文件 • 一般指存储在外部介质上具有名字(文件名)的一组相关数据的集合 • 用文件可长期保存数据,并实现数据共享 • 程序中的文件 • 在程序运行时由程序在磁盘上建立一个文件,并通过写操作将数据存入该文件;或由程序打开磁盘上的某个已有文件,并通过读操作将文件中的数据读入内存供程序使用

  7. 文件与流 • 在C语言中,文件可以是磁盘文件、终端显示器或打印机等等。 • 程序通过打开操作把流与设备联系起来,文件打开后,可以在程序和文件之间交换数据。 • 程序通过关闭操作断开流与文件的联系。 • 所有流的性质都一样。因为流与设备无关,所以能写入磁盘文件的同一函数也能写入另一设备,如控制台终端等。 • 但文件的能力则可能不同。例如,磁盘文件可以支持随机存取,而键盘则不行。

  8. 文件的存放 • 可以建立若干目录(文件夹),在目录里保存文件,同一级目录里保存的文件不能同名。 • 对使用者而言,只要知道文件的路径(全目录)和文件名,就能使用该文件 • C:\home\Sunner\main.c • 这都是托OS的福

  9. 文件的格式 • 二进制文件 • 是一种字节序列,没有字符变换 • 按照数据在内存中的存储形式存储到文件 • 如整数127,在内存占2个字节,为0000000001111111,则文件中也存储为0000000001111111,占2个字节 • 文本文件/ASCII码文件 • 是一种字符序列 • 文件中存储每个字符的ASCII码 • 如整数127在文件中占3个字节,分别存放这3个字符的ASCII码,即49,50,55

  10. int 100 float 100.0 字符串"100" 字符串"END" 64 00 00 00 C8 42 31 30 30 00 45 4E 44 00 D È B 1 0 0 E N D TEST.BIN内容(十六进制) 对应的ASCII字符 文件的格式 • 数据必须按照存入的类型读出,才能恢复其本来面貌 • 公开的标准格式 • 如bmp、tif、gif、jpg和mp3等类型的文件 ,有大量软件能生成和使用这些类型的文件 • 也有不公开、甚至加密的文件格式 • 如Microsoft Word的doc格式就不公开,所以至今还没有Word以外的其它软件能完美地读出doc文件

  11. 文件访问 • 文件访问的基本模式 • open:打开文件,获得对此文件的指针、引用和句柄等,以证明可以使用此文件 • read:读文件。参数一般指明要读多少字节,读到哪块内存。每次调用此功能,都是接着上次调用的结束位置读。(所以是个输入流) • write:写文件。参数一般指明把哪块内存的内容写入文件,要写多少字节。每次调用此功能,都是接着上次调用的结束位置写。 (所以是个输出流) • close:关闭文件,表明操作结束,不再使用此文件。文件使用完毕必须关闭,否则影响系统性能 • seek:随机控制流的当前位置,文件定位

  12. C语言中的文件访问 • 下面介绍的函数均定义在<io.h>和<fcntl.h>中 • int open(constchar *pathname, int access); • int fd = open("C:\\CONFIG.SYS", O_RDWR | O_CREAT); • pathname是文件名,包含路径。如果不含路径,表示打开当前目录下的文件 • access是打开方式,常用为O_RDONLY、O_WRONLY、O_RDWR与O_CREAT、O_TRUNC、O_APPEND的或运算 • 返回值为文件句柄(File Handle),留待以后使用。如果打开失败,返回值为-1

  13. C语言中的文件访问 • int read(int handle,void *buf,unsigned len); • int n_read = read(fd, buf, BUFSIZ); • handle是open获得的文件句柄 • buf是保存读入内容的内存指针 • len是最大可以读入的字节数 • 返回值为实际读入的字节数,可能大于len。返回0表示读到了末尾,返回-1表示出错

  14. C语言中的文件访问 • int write(int handle,constvoid *buf,unsigned len); • int n_write = write(fd, buf, BUFSIZ); • handle是open获得的文件句柄 • buf是要写入内容的内存指针 • len是写入多少字节 • 返回值为实际写入的字节数,可能小于len。返回-1表示出错

  15. C语言中的文件访问 • int close(int handle); • int ret = close(fd); • handle是open获得的文件句柄 • 关闭成功返回0,否则返回-1

  16. C语言中的文件访问 • long lseek(int handlelong offsetint fromwhere); • int pos = lseek(fd, 100L, SEEK_CUR); • handle是open获得的文件句柄 • offset是相对fromwhere的位置偏移多少,可以为负数 • fromwhere可以是SEEK_SET、SEEK_CUR或SEEK_END中的一个,分别表示文件头、当前位置和文件结尾 • 成功返回移位后的当前位置,从文件头算起;否则返回-1L • 几个巧妙运用 • curPos = lseek(fh, 0L, SEEK_CUR); • fileLength = lseek(fh, 0L, SEEK_END); • 得到文件长度

  17. C语言独特的文件访问 • 下面介绍的函数均定义在<stdio.h>中 • FILE *fopen(constchar *filename,constchar *mode); • FILE *fp = fopen("C:\\CONFIG.SYS", "rw"); • filename是文件名,包含路径。如果不含路径,表示打开当前目录下的文件 • mode是打开方式,常用为"r"、"w"、"rw"和"a",分别表示只读、只写、读写和添加 • 返回值为指向此文件的指针,留待以后使用。如果打开失败,返回值为NULL

  18. “r”只读 必须是已存在的文件。 “w” 只写 不论该文件是否存在,都新建一个文件。 “a”追加 向文本文件尾增加数据,该文件必须存在“r+” 读写 打开一个已存在的文件,用于读写。 “w+”读写 建立一个新文件,可读可写。 “a+”读写 向文件尾追加数据,也可读。 对应二进制文件 对应文本文件 “rb” “wb” “ab” “rb+” “wb+” “ab+” • mode:

  19. 文件指针 • FILE *fp ; • 是FILE型指针变量 • 标识一个特定的磁盘文件 • 与文件相关联的每个流都有一个FILE类型的控制结构,定义有关文件操作的信息,用户绝对不应修改

  20. 文件指针(File Pointer) • typedefstruct { short level; /*缓冲区‘满’或‘空’的程度*/ unsigned flags; /*文件状态标志*/ char fd; /*文件描述符*/ unsigned char hold; /*如无缓冲区不读字符*/ short bsize; /*缓冲区的大小*/ unsigned char *buffer;/*数据缓冲区的位置*/ unsigned char *curp; /*指针当前的指向*/ unsigned istemp; /*临时文件指示器*/ short token; /*用于有效性检查*/ }FILE; • 在stdio.h文件中定义

  21. C语言独特的文件访问 • int fgetc(FILE *fp); • int fputc(int c, FILE *fp); • char *fgets(char *s,int n, FILE *fp); • int fputs(constchar *s, FILE *fp); • int fscanf(FILE *fp, constchar *format, ...); • int fprintf(FILE *fp,constchar *format, ...);

  22. C语言独特的文件访问 • size_t fread(void *ptr, size_t size, size_t n, FILE *fp); • size_t fwrite(constvoid *ptr, size_t size, size_t n, FILE *fp); • int feof(FILE *fp); • int fseek(FILE *fp, long offset,int whence); • int fclose(FILE *fp);

  23. 错误处理 • 错误处理 • 文件错误一般都是外界造成的,出错率很高 • 被删除、修改、磁盘空间满、被其他文件打开 • 通过判断返回值发现错误 • 所有文件操作出错时都返回-1 • 出错处理 • 打印错误信息给用户,等待用户的处理 • void perror(const char *s); • 向标准错误输出字符串s,随后附上错误的文字说明

  24. 错误处理——例10.1 #include <stdio.h> #include <errno.h> #include <io.h> #include <fcntl.h> main() { /* c:\abc.abc文件并不存在 */ int fh = open("c:\\abc.abc", O_RDONLY | O_BINARY); if (fh == -1) /* fh必然为-1 */ { perror("Can't open c:\\abc.abc. Error"); } } Can't open c:\\abc.abc. Error: No such file or directory

  25. 两种方式的区别 • open族的功能一般由OS直接提供,其使用方式也比较具有通用性,在各种语言里基本一样 • fopen族的函数系包装了open族的函数,提供更强大的功能,但是效率略逊 • open族通常情况能直接反映文件的真实情况,因为它的操作都不假定文件的任何结构 • fopen族比较适合处理文本文件,或者结构单一的文件。会为了处理的方便而改变一些内容

  26. 从文件说开去 • open、read、write、close这种模式成为使用数据流的经典模式 • 比如网络数据流操作就是使用此模式,甚至在一些平台上可以和文件采用完全相同的函数操作 • seek并不是所有的流都支持 • 网络就不支持 • 对于不能进行流控的流,如果当前数据不立即处理,后来流过的数据就会冲走当前数据,永远不能追回。流控成为流处理中的一个专门技术

  27. 例如: fp=fopen(“a1.txt”,”r”); fp=fopen(“d:\\sqs\\a1.txt”,”r”); fopen函数返回指向文件a1.txt的指针并赋给fp,即fp指向文件a1.txt. • 若不能实现打开任务,fopen函数将带回一个空指针值NULL(值为0)。 • 常用打开文件的方法为: if((fp=fopen(“a1.txt”,”r”))==NULL) { printf(“cannot onen this file\n”); exit(0); }

  28. 文件的关闭:fclose函数 fclose(文件指针); 关闭fp所指向的文件。把遗留在缓冲区中的数据写入文件,实施操作系统级的关闭操作。同时,释放与流联系的文件控制快,以后可以再次使用这部分空间。 多数情况下,系统限制同时处于打开状态的文件总数,因此,打开文件前先关闭无用文件是必要的 例如:fclose(fp); fclose函数的返回值: 当顺利地执行了关闭操作,则返回值为0; 如果返回值为非零值,则表示关闭时有错误。一般只有驱动器中无盘或盘空间不够时,才失败。 关闭失败会引起数据丢失、破坏文件和程序中的随机错误

  29. 二、文件的字符读写 fgetc和fputc函数 fputc (ch,fp); ---把字符ch写到fp所指向的文件中去。 该文件应以写或读写方式打开。 返回值:若输出成功,则返回输出的字符,否则返回EOF。 ch=fgetc (fp); ---从指定文件读入一个字符。 该文件应以只读或读写方式打开。 返回值:若输入成功,则返回字符,否则返回一个EOF。 EOF:在stdio.h中定义的常量,表示文件结束 #define EOF (-1)

  30. 例:将键盘输入的字符顺序存入磁盘文件tt.txt中,输入ctrl+z结束例:将键盘输入的字符顺序存入磁盘文件tt.txt中,输入ctrl+z结束 #include “stdio.h” main() { char c; FILE *fp; if((fp=fopen(“tt.txt”, “w”))==NULL) { printf(“error!\n”): exit(0); } c=getchar(); while(c!=EOF) { fputc(c,fp); c=getchar(); } fclose(fp); } 程序文件名为f2.c,程序运行结果是生成文件tt.txt;

  31. 例:从文件tt.txt中顺序读取字符并显示出来。 #include “stdio.h” main() { char c; FILE *fp; if((fp=fopen(“tt.txt”, “r”))==NULL) { printf(“error!\n”): exit(0); } c=fgetc(fp); while(c!=EOF) { putchar(c); c=fgetc(fp); } fclose(fp); }

  32. 例:从文件tt.txt中顺序读取字符并显示出来。 #include “stdio.h” main() { char c; FILE *fp; if((fp=fopen(“tt.txt”, “r”))==NULL) { printf(“error!\n”): exit(0); } c=fgetc(fp); while(c!=EOF) { putchar(c); c=fgetc(fp); } fclose(fp); }

  33. 三、文件的数据块的读写—fread和 fwrite函数 fwrite()函数的功能: 将内存中的一组数据写到fp所指向的文件中。 按照内存中的存储形式输出,为二进制文件。 fwrite(buff, size, count, fp) 指向写入文件的指针 所要写入文件的数据在内存中的起始地址 写入文件的数据项数 写入文件的每个数据项的字节数 函数调用成功返回count的值,否则返回-1。

  34. 例1:文件的数据块写的操作 从键盘输入n个同学 的学号,姓名和成绩存入文件。 #include “stdio.h” typedef struct student STUD; struct student { int num; char name[12]; int score; };

  35. main() { STUD stu; /*stu为结构体变量*/ FILE *fp; char filename[12]; int i,n; scanf(“%s”,filename); if((fp=fopen(filename, “wb” ))==NULL) { printf(“can’t open the file!\n”); exit(0); } scanf(“%d”, &n); fwrite(&n,2,1,fp); for(i=0; i<n; i++) { scanf(“%d %s %d”, &stu.num, stu.name, &stu.score); if( fwrite( &stu, sizeof(STUD), 1, fp) != 1 ) /*写入一个数据项*/ { printf(“file write error!”); exit(0); } } fclose(fp); } program1

  36. program2 main() { STUD stu[30];/*stu[30]为结构体数组*/ FILE *fp; int i,n; char filename[12]; scanf(“%s”,filename); if((fp=fopen(filename, “wb” ))==NULL) { printf(“can’t open the file!\n”); exit(0); } scanf(“%d”,&n); fwrite(&n,2,1,fp); for(i=0; i<n; i++) scanf(“%d %s %d”, &stu[i]. num,stu[i].name, &stu[i].score); fwrite( stu, sizeof(STUD), n, fp);/*一次写入n个数据项*/ fclose(fp); }

  37. fread()函数: 功能: 在fp所指向的文件中读取一组数据并将其放到内存中去。 fread(buff, size, count, fp) 指向待读文件的指针 待读文件的数据在内存中的起始地址 从文件读入的数据项数 从文件读入的每个数据项的字节数 函数调用成功返回count的值,否则返回-1。

  38. 例2:文件的数据块读的操作 #include “stdio.h” typedef struct student STUD; struct student { int num; char name[12]; int score; }; main() { STUD stu; FILE *fp; int i,n; char filename[12]; scanf(“%s”,filename); if((fp=fopen(filename, “rb”))==NULL) { printf(“can’t open the file!\n”); exit(0); }

  39. fread(&n,2,1,fp); printf(“n=%d”,n); for(i=0; i<n; i++) { if(fread( &stu, sizeof(STUD), 1, fp) != 1 )/*读入一个*/ { printf(“file read error!”); exit(0); } printf(“%d\t%s\t%d\n”,stu.num, stu.name, stu.score); } fclose(fp); }

  40. main() { STUD stu[20]; …… fread(&n,2,1,fp); printf(“n=%d”,n); if(fread( stu, sizeof(STUD) , n , fp ) ! = n )/*读入n个*/ { printf(“file read error!”); exit(0); } for(i=0; i<n; i++) printf(“%d\t%s\t%d\n”, stu[i].num, stu[i].name, stu[i].score); fclose(fp); }

  41. 四、文件的格式化读写(文本文件) • fprintf函数 格式:fprintf(fp,format,arg1,arg2,…,argn); 将输出项arg1,arg2…argn按指定format格式写入fp所指的文件中,即输出到磁盘文件; • fscanf函数 格式:fscanf(fp,format,&arg1,&arg2,…&argn); 按指定format格式从fp所指的文件中读取数据依次送入arg1,arg2…argn的内存单元; • fp: 为指向fopen函数打开的文件的指针 • format :为格式字符串 • arg1,arg2,…argn(&arg1,&arg2,…&argn)输出/输入项表列

  42. 最好给出长度,读写统一 fprintf ( fp,“%d,%6.2f”,i,t ); 将整型变量i和实型变量t的值按%d和%6.2的格式输出到fp指向的文件中。 fscanf ( fp,“%d,%6.2f”,&i,&t );

  43. 例1:文件的格式化写操作 #include “stdio.h” typedef struct student STUD; struct student { int num; char name[12]; int score; }; main() { STUD stu; FILE *fp; int i,n; char filename[12]; scanf(“%s”,filename);

  44. if((fp=fopen(filename, “w”))==NULL) /*新建一个文件*/ { printf(“can’t open the file!\n”); exit(0); } scanf(“%d”,&n); fprintf(fp, “%4d”,n); for(i=0; i<n; i++) { scanf(“%d%s%d”,&stu.num, stu.name, &stu.score); fprintf(fp, “%6d%10s%3d”,stu.num, stu.name, stu.score); } fclose(fp); }

  45. 例2:文件的格式化读操作 #include “stdio.h” typedef struct student STUD; struct student { int num; char name[12]; int score; }; main() { STUD stu; FILE *fp; int i,n; char filename[12]; scanf(“%s”,filename);

  46. if((fp=fopen(filename, “r”))==NULL) /*新建一个文件*/ { printf(“can’t open the file!\n”); exit(0); } fscanf(fp,“%6d”,&n); printf(“n=\n”,n); for(i=0; i<n; i++) { fscanf(fp, “%6d%10s%3d”,&stu.num,stu.name,&stu.score); printf(“%d\t%s\t%d\n”,stu.num,stu.name,stu.score); } fclose(fp); }

  47. 五、文件的定位 • 文件指针 : 打开的文件中有一个位置指针指示动迁的读写位置,对文件每进行一次读写,文件指针自动指向下一个读写位置,则可方便地进行顺序读写,利用定位函数还可实现随机读写. • rewind ()函数 功能:使文件指针重新指向文件的开头位置。 此函数没有返回值 形式: rewind(fp) fp为指向fopen函数打开的文件的指针。

  48. fseek()函数: 利用该函数可以改变文件指针的位置,从而实现随机读写 形式: fseek(fp,offset,from); • fp:为指向fopen函数打开的文件的指针; • offset:文件指针的位移量,字节数,long型,加l或L; • from:起始位置 0----文件开始 1-----当前位置 2-----文件末尾 • ftell()函数: 取得文件指针的当前位置,用字节数,long类型。

  49. 六、出错的控制 • ferror()函数 形式:ferror(fp); 功能:检查输入输出函数的调用是否正确,返回非0值,表示出错。 • feof()函数 形式:feof(fp); 功能:检测文件是否结束,返回非0值,表示结束。

More Related