THE C PROGRAMMING LANGUAGE 计算中心- NEU Computer Center 高克宁-gaokening E_mail:chengxusheji01@necmail.neu.edu.cn
0.本章内容 文件概述 文件的打开与关闭 文件的顺序读写 文件的定位与随机读写
1.文件概述 什么是文件? 文件指存储在外部介质上的数据的集合 例如 磁盘上的每个文件都有唯一的名字,进行标识,依据文件名找到相应的文件 程序文件中保存着源程序 数据文件中保存着数据 声音文件中保存着声音数据 磁盘上的每个文件都有唯一的名字,进行标识,依据文件名找到相应的文件
1.文件概述 什么是文件? 操作系统是以文件为单位对数据进行管理的 如果想使用存在外部介质上的数据,必须先按文件名找到指定的文件,然后再从文件中读取数据 如果要向外部介质上存储数据, 也必须先建立一个文件, 再向它输出数据。 文件除了包括磁盘上存储的内容外, 从操作系统的角度看与主机相连的各种输入输出设备也是文件。 例如, 键盘是输入文件, 显示器是输出文件
1.文件概述 为什么要使用文件? 优点 C语言如何实现文件操作? 文件的改动不能引起程序的改动 不同的程序可以访问同一数据文件中的数据 即程序与数据分离 不同的程序可以访问同一数据文件中的数据 即数据共享 能够长期保存程序运行的中间数据或结果数据 C语言如何实现文件操作? 通过操作系统完成对文件的输入输出操作 通过由C语言的编译系统提供的一套用于文件操作的库函数,也称为“标准输入/输出库”
1.文件概述 文件的分类 C语言把文件看作是一个字节的序列 文本文件(又称ASCII文件) 即文件是由一个一个字节的数据顺序组成的 根据数据的组织形式把文件分为两类:文本文件和二进制文件。 文本文件(又称ASCII文件) 文件的每一个字节存放一个ASCII码, 代表一个字符 优点 ASCII码文件较为直观、可读性好 便于对字符进行处理 可以直接在DOS下的用type命令或Windows下的记事本等工具直接观察 缺点 占用存储空间较多, 二进制形式与ASCII码形式转换需要时间
1.文件概述 文件的分类 二进制文件 把内存中的数据按其在在内存中的存储形式原样输出到磁盘上存放。 优点 输入输出时不需要进行转换 节省磁盘空间和转换时间 缺点 不能直接输出字符形式 不能直接识别内存中的表示形式,不方便人们的阅读和理解,可读性差
1.文件概述 文件的分类 设内存有一整数2460, 内存中的存储形式: 0000 1001 1001 1100 2460在文本文件中的存储形式: 0011 0010 0011 0100 0011 0110 0011 0000 2460在二进制文件中的存储形式: 0000 1001 1001 1100
2.磁盘文件系统 磁盘文件系统对文件的处理方法 读文件 写文件 使用缓冲区的目的是为了减少对磁盘的实际读写次数 从磁盘文件输入数据时(即读文件), 先将数据送到“输入缓冲区”, 再从缓冲区将数据传送给程序数据区(即传给程序中的变量) ; 写文件 向磁盘文件输出数据时(即写文件), 先将程序中变量的值送到“输出缓冲区”, 等缓冲区装满后再将数据一起传送给磁盘文件。 使用缓冲区的目的是为了减少对磁盘的实际读写次数
2.磁盘文件系统 磁盘文件系统对文件的处理方法 程 序 数 文件 据 区 输入文件缓冲区 输出文件缓冲区 计算机内存 输入 读文件 输出 外存 文件 写文件 输出 输入 读文件
2.磁盘文件系统 磁盘文件系统的分类 缓冲文件系统 非缓冲文件系统 系统自动的在内存区为每一个正在使用的文件开辟一个缓冲区 缓冲区的大小由具体的C版本确定, 一般为512字节。 非缓冲文件系统 系统不会自动开辟缓冲区 由程序为每个文件设定缓冲区及其大小(即缓冲区由用户根据需要自己进行设置)
2.文件类型指针 概念 要读取一个文件需要有一定的信息 对已打开文件进行操作,都要通过指向该文件结构的指针进行 如文件当前的读写位置, 与文件对应的内存缓冲区地址, 文件的操作方式等,这些信息都存放在“文件信息区”中 “文件信息区”是一个结构体变量 , 其结构体类型由系统定义, 类型名为 FILE 在头文件stdio.h中定义的结构FILE 对已打开文件进行操作,都要通过指向该文件结构的指针进行 typedef struct { short level; /*文件的缓冲区级别(满或空的程度)*/ unsigned flag; /*文件状态标志*/ char fd; /*文件号*/ int cleft; /*缓冲区的剩余字符(剩余空间)*/ int mode; /*文件的操作模式*/ short size; /*缓冲区大小*/ char *buffer; /*文件缓冲区的地址(位置)*/ char *curp; /*当前地址指针(下一个字符的位置)*/ short token; /*有效性检查控制单元*/ }FILE;
2.文件类型指针 定义形式 文件的操作 FILE * 指针变量名 ; 说明 C语言中文件的操作主要是由C语言库函数实现 只有通过文件指针变量才能调用相应的文件 有n个文件就要定义n个文件指针变量, 分别对应各个文件 FILE必须大写 文件的操作 C语言中文件的操作主要是由C语言库函数实现 文件操作的函数属于标准输入输出库中的函数 需在程序中使用预处理命令 #include <stdio.h>
2.文件的打开与关闭 文件的打开 fopen函数 格式: fopen ( 文件名 , 文件使用方式 ) ; 说明 需要打开的文件名 例 FILE *fp ; fp = fopen ( “file1” , “r” ) ; 使用文件的方式(读或写) 让哪个指针变量指向被打开的文件
2.文件的打开与关闭 文件的打开 使用文件的方式 “r”与“rb” 只读 “w”与“wb” 只写 以此方式打开文件时, 该文件必须已经存在, 如不存在将出错. 打开文件后只能从文件中读数据, 当前读写位置设定于文件开头 “w”与“wb” 只写 以此方式打开文件时, 如有同名文件(即文件已存在), 则将原有文件内容删除并写入新内容; 如没有同名文件(即文件不存在), 则建立一 个新文件. 打开文件后只能向文件中写数据, 当前读写位置设定于文件开头
2.文件的打开与关闭 文件的打开 使用文件的方式 “a”与“ab” 追加 带“+”号的形式: 表示即能读又能写 以此方式打开文件时, 如有同名文件, 则将当前读写位置设定于文件末尾, 可以追加数据; 如无同名文件, 则建立一个新文件 带“+”号的形式: 表示即能读又能写
2.文件的打开与关闭 文件的打开 打开文件的方法 打开文件时 , 因使用方式不对或其他原因, 可能会使打开文件的操作失败 , 这时fopen函数的返回值是NULL, 所以在打开文件时通常会做一个判断 , 看打开操作是否成功。 例 if ( ( fp =fopen(“file1”, “r”) ) = = NULL ) { printf(“Can not open this file!\n”) ; exit( 0 ) ; } 说明: exit 函数的作用是关闭所有文件, 结束程序, 并返回 操作系统, 也可写成 exit ( ) ; 注意: 使用exit函数要包含头文件 < stdlib.h >
2.文件的打开与关闭 【例】以写方式打开一个readme.txt文件。 #include “stdio.h” void main() { FILE *fp; fp=fopen(“readme.txt”,“w”); if(fp=NULL) printf(“file not found!”); fclose(fp); } else printf(“file opened ok!”); 可以在fopen打开文件时 检查函数的返回值是否 为NULL以确定文件是否 真正打开,若文件打开 失败,还使用读写函数 对文件进行读写操作将 会出现错误。
2.文件的打开与关闭 也可以写成下面这种方式: #include “stdio.h” void main() { FILE *fp; fp=fopen(“readme.txt”,“w”); if (fp=NULL) printf(“file not found!”); exit(0); } else printf(“file opened ok!”); /*exit(0)是一个库函数,定义在“stdio.h”中,它起的作用是: (1)关闭所有文件; (2)程序正常退出; (3)返回操作系统; (4)将(0)返回操作系统。
2.文件的打开与关闭 文件的关闭 fclose函数 格式:fclose ( 文件指针变量 ) ; 说明: 作用 将文件指针变量所指向的文件关闭, 即使文件指针变量不再指向该文件,以后不能通过文件指针变量对该文件进行操作 例: fclose( fp ) ;
2.文件的打开与关闭 文件的关闭 关闭文件的重要性 写文件时, 在“输出缓冲区”装满后, 才将数据一起写入文件。 当程序结束时, 缓冲区可能还未满, 如未关闭文件就结束程序, 则缓冲区的数据不能写入文件, 会丢失数据。 使用fclose函数, 不论缓冲区是否已满, 都会将缓冲区的数据写入文件, 再关闭文件。
将字符‘s’写入fp指向的文件file1中 3.文件的顺序读写 输入/输出一个字符 fputc函数(向文件输出一个字符) 格式:fputc ( 字符常量或字符变量 , 文件指针变量 ) ; 说明 函数返回值:成功时返回该字符 , 失败时返回 EOF 作用 将一个字符写到文件指针所指向的文件中去 例: FILE *fp ; char ch ; if ( ( fp =fopen(“file1”, “w”) ) = = NULL ) { printf(“Can not open this file!\n”) ; exit( 0 ) ; } fputc( ‘s’ , fp ) ; ch = getchar( ) ; fputc( ch , fp ) ; 将字符‘s’写入fp指向的文件file1中 将字符变量ch中存放的字符 写入fp指向的文件file1中
3.文件的顺序读写 输入/输出一个字符 fgetc函数 形式: 字符变量 = fgetc( 文件指针变量 ) ; 作用 说明 函数返回值: 成功时返回该字符 , 失败时返回 EOF 作用 从文件指针变量所指向的文件中读一个字符 , 并将它赋给程序中的一个字符变量 注意: EOF 实际是一个符号常量 , 其值为 –1。一般用EOF作字符文件的文件结束符 , 当用 fgetc函数时, 如果遇到文件结束也将返回 EOF 例: FILE *fp ; char ch ; if ( ( fp =fopen(“file1”, “r”) ) = = NULL ) { printf(“Can not open this file!\n”) ; exit( 0 ) ; } ch = fgetc ( fp ) ; printf(“%c” , ch ) ;
3.文件的顺序读写 输入/输出一个字符串 fgets函数 格式: fgets( str , n , fp ) ; 作用 说明 函数返回值: 成功返回str数组的首地址 , 失败返回 NULL 作用 从fp所指向的文件中读(n-1)个字符 , 将它们存放到str数组中, 并在其后自动加一个‘\0’, 如果读入(n-1)个字符前遇到换行符或文件结束符EOF, 则结束读入。
3.文件的顺序读写 输入/输出一个字符串 fputs函数 格式:fputs( str , fp ) ; 作用 说明 函数返回值: 成功返回 0 , 出错返回 非 0 值 作用 将str字符串写到fp所指向的文件中, 但‘\0’不写入文件
3.文件的顺序读写 输入/输出一个字符串 例题 从键盘输入一行字符, 以‘!’结束, 将这 行字符中的小写字母改为大写字母, 然后保存到磁盘文件“test.txt”中 输入/输出一个字符串 例题 #include <stdio.h> #include <stdlib.h> void main( ) { FILE *fp; char str1[20] , str2[20]; int i=0; if ( ( fp =fopen(“test.txt”, “w”) ) = = NULL ) { printf(“can not open this file!\n”); exit( 0 ); } gets(str1); while ( str1[i]!=‘!’ ) { if ( str1[i]>=‘a’ && str1[i]<=‘z’ ) str1[i]=str1[i]-32; fputc( str1[i] , fp); i++ ; } fclose(fp); fp=fopen( “test.txt” , “r” ); fgets(str2 , i+1 , fp ); puts(str1); puts(str2); }
3.文件的顺序读写 按“数据块”的方式输入输出 格式 fread ( buffer , size , count , fp ) ; fwrite ( buffer , size , count , fp ) ; 说明 buffer : 是一个地址 对fread而言 buffer是从文件中读取的数据要存放的存储区的首地址,对fwrite, buffer是向文件中写入数据时存储变量的地址 size : 要读写数据所占用的字节数 count : 要读写数据项的个数 fp : 文件指针变量 注意 用 fread , fwrite 必须采用二进制方式打开文件
3.文件的顺序读写 按“数据块”的方式输入输出 例题 #include <stdio.h> 从键盘输入10个整数,把它们 保存到文件“datafile.txt”中, 再从文件中读取这些整数, 并 输出到显示器上。 按“数据块”的方式输入输出 例题 #include <stdio.h> #include <stdlib.h> void main ( ) { FILE *fp ; int m , n , j ; if ( ( fp =fopen(“datafile.txt”, “wb+”) ) = = NULL ) { printf(“Can not open this file!\n”) ; exit( 0 ) ; } for ( j=0 ; j<10 ; j++ ) { scanf(“%d”, &m ) ; fwrite( &m , 2 , 1 , fp ) ; } rewind ( fp ) ; { fread ( &n , 2 , 1 , fp ) ; printf(“%4d”, n ) ; } fclose (fp) ; }
3.文件的顺序读写 格式化输入输出函数 格式 fprintf ( 文件指针变量 , 格式字符串 , 输出表列 ) ; fscanf ( 文件指针变量 , 格式字符串 , 输入表列 ) ; #include <stdio.h> #include <stdlib.h> void main ( ) { FILE *fp ; int a , b , c , d ; if ( ( fp =fopen(“exfile”, “wb+”) ) = = NULL ) { printf(“Can not open this file!\n”) ; exit( 0 ) ; } scanf(“%d%d”, &a , &b) ; fprintf( fp , “%d , %d\n” , a , b ) ; rewind(fp) ; fscanf( fp , “%d , %d” , &c , &d ) ; printf(“%d , %d\n”, c , d ) ; fclose(fp); }
4.文件定位与随机读写 文件的读写位置指针 文件中有一个读写位置指针 , 指向当前读或写的位置 前面讲的文件读写都是顺序读写 , 此时位置指针对当前所指向的数据项进行读或写操作后 , 位置指针会自动指向下一个数据项。 如能控制位置指针的指向, 就能实现随机读写
4.文件定位与随机读写 文件定位 fseek函数 格式: fseek ( 文件指针变量, 位移量, 起始点 ) ; 说明 位移量 是一个 long int 型数据 , 表示以“起始点”为基点向前或向后要移动的字节数, 正数表示向前移动(文件头文件尾); 负数表示向后移动 起始点 表示从什么位置为基准移动 , 用数字代表 0 -- 文件开始处 1 -- 文件位置指针的当前指向 2 -- 文件末尾处 函数返回值: 成功返回 0 , 出错返回 非 0 值
4.文件定位与随机读写 文件定位 ftell函数 格式: 长整型变量 = ftell ( 文件指针变量 ) ; 作用 说明 得到文件中位置指针的当前指向 , 这个位置是相对于文件开头的位移量 例 : long n ; n = ftell (fp ) ; if ( n= = -1L ) printf(“error!”) ;
4.文件定位与随机读写 文件定位 rewind函数 (无返回值) 例题 格式: rewind(文件指针变量) ; 作用 将文件位置指针重新返回到文件开头 例题 输入学生信息, 保存在文件中, 再从文件中读取学生信息输出到显示器上
4.文件定位与随机读写 文件定位 例题 输入学生信息, 保存在文 件中, 再从文件中读取学 生信息输出到显示器上 #include <stdio.h> #include <stdlib.h> #define SIZE 4 struct stud { char name[10]; int num; int score; } st[SIZE]; long len=sizeof(struct stud); void save( ) { FILE *fp; int i ; if((fp=fopen(“studata”,“wb”))==NULL) { printf(“can not open this file!\n”); exit(0); } for( i=0; i<SIZE; i++) if( fwrite(&st[i], len, 1, fp)!=1 ) printf(“file write error!\n”); fclose(fp);} 文件定位 例题 void main( ) { FILE *fp; int i; struct stud x; for (i=0; i<SIZE; i++) scanf(“%s%d%d”, st[i].name, &st[i].num, &st[i].score); save( ); fp=fopen(“studata”,“rb”); for(i=0; i<SIZE; i++) { fread(&x, len ,1, fp); printf(“%-10s%4d%4d\n”, x.name, x.num, x.score);} fseek(fp, -2*len, 2); fread(&x, len ,1, fp); x.name, x.num, x.score); fclose(fp); }