一、C 文件有关的概念
1、什么是文件
文件(file)有不同的类型,在进行 C 语言程序设计中,主要用到两种文件:
- 程序文件
- 源程序文件(.c)
- 目标文件(.obj)
- 可执行文件(.exe)
- 用来存放程序的,以便实现程序的功能
- 数据文件
- 供程序运行时读写的数据
所谓 “ 文件 ” 一般指存储在外部介质上数据的集合
一批数据是以 文件的形式 存放在外部介质上的
操作系统是以文件为单位对数据进行管理的
输入输出是数据传送的过程 ,称为 “ 流 ”,即 输入输出流
流表示了信息从 源 到 目的端 的流动
输入输出是对于内存而言的!!!
C 语言把文件看作一个字符(字节)的序列,即由一个一个字符(字节)的数据顺序组成
在 C 文件中,数据由一连串的字符(字节)组成,中间没有分隔符,对文件的存取是以字符(字节)为单位的,可以从文件读取一个字符或向文件输出一个字符
数入输出数据流的开始与结束仅受程序控制而不受物理符号控制,增加了处理的灵活性,这种文件称为 流式文件
2、文件名
一个文件要有一个唯一的文件标识,以便用户识别和引用
文件标识包括三个部分:
- 文件路径
- 文件名主干
- 文件后缀
文件名主干的命名规则遵循标识符的命名规则
后缀用来表示文件的性质,一般不超过 3 个字母
3、文件的分类
根据数据的组织形式,数据文件可分为 ASCII 文件 和 二进制文件
ASCII 文件又称文本文件,它的每一个字节放一个字符的 ASCII 代码
二进制文件是把内存中的数据按其在内存中的存储形式原样输出到磁盘上存放
字符型数据只能以 ASCII 形式存储,数值型数据可以用 ASCII 形式存储在磁盘上,也可以用二进制形式存储
用 ASCII 码形式输出与字符一一对应,1 字节代表 1 个字符,因而便于对字符进行逐个处理,也便于输出字符。但一般占存储空间较多,而且要话费转换时间(二进制形式与ASCII 码间的转换)
用二进制形式输出数值,可以节省外存空间和转换时间,但 1 字节并不对应 1 个字符,不能直接输出字符形式
一般作为中间结构的数值型数据,需要暂时保存在外存上,以后又需要输入到内存的,常用二进制文件保存
4、文件缓冲区
C 语言采用 “ 缓冲文件系统 ” 处理文件,所谓缓冲文件系统是指系统自动地在内存区为程序中每一个正在使用的文件开辟一个文件缓冲区
缓冲区大小由各个具体的 C 编译系统确定
5、文件类型指针
缓冲文件系统中,关键的概念是文件类型指针,简称文件指针
每个被使用的文件都在内存中开辟了一个相应的 文件信息区 ,用来存放文件的有关信息,这些信息保存在一个结构体变量中
该结构体类型由系统声明的,取名为 FILE
不同的 C 编译系统的 FILE 类型包含的内容不完全相同,但大同小异
定义 FILE 结构体类型的信息包含在 stdio.h 中
在程序中可以直接使用 FILE 类型名定义变量
每一个 FILE 类型变量对应一个文件的信息区
指向文件类型数据的指针变量
FILE *fp;
通常将指向文件信息区的指针变量称为指向文件的指针变量
指向文件的指针变量并不是指向外部介质上的数据文件的开头,而是指向内存中的文件信息区的开头
二、文件的打开与关闭
打开:为文件建立相应的信息区和文件缓冲区,建立指针变量与文件之间的联系,就可以通过指针变量对文件进行读写了
关闭:撤销文件信息区和文件缓冲区,使文件指针变量不再指向该文件,就无法进行对文件的读写了
1、用 fopen 函数打开数据文件
fopen("文件名","使用文件方式");
FILE *fp; // 定义一个指向文件的指针变量 fp
fp = fopen("a1","r"); // 将 fopen 函数的返回值赋给指针变量 fp
使用文件的方式
r、w、a 是三种最基本的方式,在其后加 b 表示二进制文件,不加 b 表示 ASCII 文件,加 + 表示可读可写
如果文件未能打开,fopen 函数将会带回一个出错信息
常用以下方法打开文件
if((fp = fopen("file","r")) == NULL){
printf("connot open this file\n");
exit(0);
}
2、用 fclose 函数关闭文件
在使用完一个文件后应该关闭它,以防止它再被误用
fclose(文件指针);
如果不关闭文件可能会造成数据丢失
fclose 函数也返回一个值,当成功关闭文件时,返回值为 0,否则返回 EOF(-1)
三、文件的顺序读写
1、向文件读写字符
从键盘输入一些字符,逐个把它们送到磁盘上去,直到用户输入一个 # 为止
#include <stdio.h>
#include <stdlib.h>
int main(){
FILE *fp;
char ch,filename[10];
printf("请输入所用的文件名:");
scanf("%s",filename);
if((fp = fopen(filename,"w")) == NULL){
printf("无法打开此文件\n");
exit(0); // 标准 C 的库函数,终止程序,头文件:stdlib.h
}
ch = getchar(); // 接收执行 scanf 语句最后输入的回车符
printf("请输入一个准备存储到磁盘的字符串(以 # 结束):");
ch = getchar(); // 接收输入的第一个字符
while(ch != '#'){
fputc(ch,fp); // 接收到的第一个字符输出到文件
putchar(ch); // 将输出的字符显示到屏幕上
ch = getchar(); // 接收下一个输入的字符
}
fclose(fp); // 关闭文件
putchar(10); // 向屏幕输出一个换行符,ASCII 码为 10
return 0;
}
运行结果
请输入所用的文件名:file.dat
请输入一个准备存储到磁盘的字符串(以 # 结束):Hello C File#
Hello C File
将一个磁盘文件中的信息复制到另一个磁盘文件中
#include <stdio.h>
#include <stdlib.h>
int main(){
FILE *in,*out;
char ch,infile[10],outfile[10];
printf("请输入读入文件的名字:");
scanf("%s",infile);
printf("请输入输出文件的名字:");
scanf("%s",outfile);
if((in = fopen(infile,"r")) == NULL){
printf("无法打开此文件\n");
exit(0);
}
if((out = fopen(outfile,"w")) == NULL){
printf("无法打开此文件\n");
exit(0);
}
ch = fgetc(in);
while(!feof(in)){
fputc(ch,out);
putchar(ch);
ch = fgetc(in);
}
putchar(10); // 换行
fclose(in);
fclose(out);
return 0;
}
运行结果
请输入读入文件的名字:file.dat
请输入输出文件的名字:file1.dat
Hello C File
系统用 “文件读写位置标记” 来表示当前所访问的位置
在一个文件所有有效字符的后面一字节中,系统自动设置了一个文件尾标志 ,用标识符 EOF 表示,在 stdlib.h 头文件中 EOF 被定义为 -1
可以用 feof 函数检测文件尾标志 EOF 是否已被读过
2、向文件读写一个字符串
用 fgets 函数可以从指定的文件读入一个字符串
用 fputs 函数可以向指定的文件输出一个字符串
fgets 和 fputs 这两个函数的功能类似与 gets 和 puts 函数,只是 gets 和 puts 以终端为读写对象,而 fgets 和 gputs 函数以指定的文件作为读写对象
从键盘读入若干个字符串,对它们按字母大小的顺序排序,然后把排好序的字符串送到磁盘文件中保存
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
FILE *fp;
char str[3][10],temp[10];
int i,j,k,n = 3;
printf("Enter strings : \n");
// 从键盘获取 n 个字符串
for(i = 0;i < n;i++){
gets(str[i]);
}
// 选择排序
for(i = 0;i < n - 1;i++){
k = i;
for(j = i + 1;j < n;j++){
if(strcmp(str[k],str[j]) > 0){
k = j;
}
}
if(k != j){
strcpy(temp,str[i]);
strcpy(str[i],str[k]);
strcpy(str[k],temp);
}
}
// 打开文件
if((fp = fopen("F:\\C\\temp\\string.dat","w")) == NULL){
printf("Can't open file!\n");
exit(0);
}
printf("\nThe new sequence : \n");
for(i = 0;i < n;i++){
// 保存数据到文件
fputs(str[i],fp);
fputs("\n",fp);
// 打印到屏幕上
printf("%s\n",str[i]);
}
return 0;
}
运行结果
Enter strings :
Hello
String
File
The new sequence :
File
Hello
String
3、文件的格式化读写
fprintf(文件指针,格式化字符串,输出表列);
fscanf(文件指针,格式化字符串,输出表列);
4、用二进制方式读写文件
fread(buffer,size,count,fp);
fwrite(buffer,size,count,fp);
buffer :是一个地址
size :要读写的字节数
count :要进行读写多少个 size 字节的数据项
fp :文件型指针
四、文件的随机读写
1、文件位置标记及其定位
1)文件位置标记
为了对读写进行控制,系统为每个文件设置了一个文件读写位置标记(简称文件位置标记或文件标记),用来指示当前的读写位置
可以在任何位置写入数据,在任何位置读取数据
2)文件位置标记的定位
- 用 rewind 函数使文件位置指向文件头,没有返回值
rewind(文件类型指针);
- 用 fseek 函数移动文件位置标记
fseek(文件类型指针,位移量,起始点);
起始点用0、1或 2代替
- 0 : 文件开始
- 1 : 当前位置
- 2 : 文件末尾
位移量指以起始点为基点,向前移动的字节数(long 型数据)
一般用于二进制文件
- 用 ftell 函数测定文件位置标记的当前位置
- 返回 -1 表示出错
ftell(文件类型指针);
2、随机读写文件
在文件 stu_dat 中存有 10 个学生的数据。现要求将该文件中的第1、3、5、7、9个学生数据输入计算机,并显示在屏幕上
#include <stdio.h>
#include <stdlib.h>
struct student_type{
char name[10];
int num;
int age;
char addr[15];
} stud[10];
int main(){
int i;
FILE *fp;
if((fp = fopen("stu_dat","rb")) == NULL){
printf("Can not ope file\n");
exit(0);
}
for(i = 0;i < 10;i += 2){
fseek(fp,i * sizeof(struct student_type),0);
fread(&stud[i],sizeof(struct student_type),1,fp);
printf("%-10s %4d %4d %-15s\n",stud[i].name,stud[i].num,stud[i].age,stud[i].addr);
}
fclose(fp);
return 0;
}
五、提高部分
1、系统定义的文件类型指针
这三个 FILE 型的指针变量称为 标准文件指针 或 标准文件
- stdin(标准输入文件指针)
- 指向在内存中与 键盘 相应的文件信息区,因此,用它进行输入就蕴含了从键盘输入
- stdout(标准输出文件指针)
- 指向在内存中与显示器屏幕相应的文件信息区,因此,用它进行输出蕴含了输出到显示器屏幕
- stderr(标准出错文件指针)
- 用来输出出错的信息,它也指向在内存中与显示器屏幕相应的文件信息区,因此,在程序运行时的出错的信息就输出到显示器屏幕
2、文件读写的出错检测
- ferror 函数
ferror(fp);
返回 0,表示未出错;返回非零值,出错
对同一个文件每一次调用输入输出函数,均产生一个新的 ferror 函数值,因此,应当在调用一个输入输出函数后立即检查 ferror 函数的值,否则信息会丢失
在执行 fopen 函数时,ferror 函数的初始值自动置为 0
- clearerr 函数
clearerr 函数的作用是使文件错误标志和文件结束标志置为 0
六、常用的文件操作函数及功能
一 叶 知 秋,奥 妙 玄 心