- Linux是由文件驱动的系统。
一、文件操作
1、概述
-
什么是文件
文件是指存储在外存储器(即硬盘、U盘、闪盘、软盘等)上的数据的集合。
-
文件操作体现在哪几个方面
- 文件内容的读取;
- 文件内容的写入。
数据的读取和写入可被视为针对文件进行输入和输出的操作,此时数据就像水流一样从外存储器流向内存,或者从内存流向外存储器,所以系统形象地称文件操作为文件流。
C语言程序对文件的操作采用“文件缓冲机制”,就是说在程序中对文件的数据读写并不是直接操作文件中的数据,而是系统会为文件在内存中创建“文件缓冲区”,程序对文件的操作,其实是在缓冲区进行的。
-
文件的分类
根据数据的存储方式划分:
- 文本文件(ASCII文件);
- 二进制文件。
-
文件标识
- 文件系统中:路径+文件名,举例:d:/aaa/bbb.txt
- C语言系统中:文件指针(文件类型指针),语法:FILE *指针变量名;
-
文件操作的步骤:
- 打开文件;
- 文件处理(读写文件);
- 关闭文件。
2、文件的操作
1.文件的打开关闭
-
打开文件,让系统为文件创建文件缓冲区;
-
函数名:fopen
-
头文件: #include <stdio.h>
-
函数原型:FILE* fopen(const char *path,const char *mode)
-
函数功能:打开文件,并为文件创建缓冲区;
-
函数参数:
-
path:目标文件的路径;
-
mode:文件打开的方式(读-r、写-w、读写-rw)
-
-
返回值:
-
成功:返回文件指针 File*(缓冲区首地址)
-
失败:返回NULL
-
-
int *arr[] --> 指针数组,即数组内的元素为指针;
int (*arr)[] 等价于 int arr[][] --> 数组指针,操作二维数组时使用,举例:char c[] = {"aaaa","bbbb","cccc"}。
-
文件关闭,文件使用完毕,一定要记得释放
-
函数名:fclose
-
头文件: #include <stdio.h>
-
函数原型: int fclose(FILE* fp);
-
函数功能:关闭文件,释放缓冲区
-
函数参数:
-
fp:已经打开的文件指针
-
-
返回值:
-
成功:返回0
-
失败:返回EOF(-1)
-
-
举例:文件打开与关闭案例
/** * 文件的打开与关闭 */ #include <stdio.h> int main(int argc,char** argv) { // 在命令行执行./a.out的时候,传递一个需要打开的目标文件的地址 if(argc < 2) { printf("输入有误,请按照<%s 文件路径>格式输入\n",argv[0]); return -1; } // 根据提供的文件路径,打开文件,mode(r,w,rw) FILE* fp = fopen(argv[1],"r"); // 校验文件是否读取成功 if(!fp) { perror("文件打开失败!"); return -1; } puts("文件打开成功!"); // 关闭打开的文件 int ret = fclose(fp); // 校验文件是否关闭成功(很多时候这一步会省略掉) if(ret == -1) { perror("文件关闭失败!"); return -1; } puts("文件关闭成功!"); return 0; }
perror专用于输出异常提示!
2.文件的读写
1)读
-
单字符读写
-
函数名:fgets
-
头文件: #include <stdio.h>
-
函数原型:int fgets(FILE* fp)
-
函数功能:从fp代表的文件中获取一个字符
-
函数参数:
-
fp:我们需要操作的文件的描述;
-
-
返回值:返回读取到的字符
-
举例:单字符读取
/** * 单字符读取 */ #include <stdio.h> int main(int argc,char** argv)//参数个数,参数所携带的字符串 { if(argc < 2) { printf("输入有误\n"); return -1; } //打开文件 FILE* fp = fopen(argv[1],"r");//权限:可读 //非空校验 if(!fp) { perror("文件打开失败!"); return -1; } //单字符读取文件 int re = fgetc(fp); if(re == -1)//整数不能这样判断 { perror("文件读取失败!"); return -1; } printf("%c\n",re); //关闭文件 fclose(fp); return 0; }
举例:单字符读取字符串
/** * 单字符读取 */ #include <stdio.h> int main(int argc,char** argv)//参数个数,参数所携带的字符串 { if(argc < 2) { printf("输入有误\n"); return -1; } //打开文件 FILE* fp = fopen(argv[1],"r");//权限:可读 //非空校验 if(!fp) { perror("文件打开失败!"); return -1; } //单字符读取文件 int re = 0; while(re != -1 && re != '\n') { re = fgetc(fp); printf("%c",re); } printf("\n"); //关闭文件 fclose(fp); return 0; }
-
多字符读取
-
函数名:fgets[]
-
头文件:#include <stdio.h>
-
函数原型:char *fgets(char *buf,int size,FILE *fp);
-
函数功能:从fp代表的文件中获取size个字符,放置在buf代表的内存中;
-
函数参数:
-
buf:内存空间首地址用于存放读取的字节;
-
size:待读取的字符,实际读取size-1;
-
fp:已打开的文件指针;
-
-
返回值:
-
成功:返回buf;
-
失败:文件末尾,返回NULL;
-
-
Linux中查阅函数帮助:man 函数名
举例:多字符读取
/** * 多字符读取 */ #include <stdio.h> #include <string.h> int main(int argc,char **argv) { if(argc < 2) { printf("输入有误"); return -1; } FILE* fp = fopen(argv[1],"rw"); if(!fp) { perror("文件打开失败!"); return -1; } //多字符读取 //创建缓冲区大小 char buf[64] = {0}; while(fgets(buf,64,fp) != NULL) { printf("%s",buf); //给buf赋初值(相当于在下一次打印缓冲区数据时清除上一次的数据) memset(buf,0,sizeof(buf)); } printf("\n"); fclose(fp); return 0; }
2)写
-
单字符写入
-
函数名:fputc
-
头文件:#include <stdio.h>
-
函数原型:int fputc(int c,FILE* fp)
-
函数功能:向fp代表的文件中写入一个字符c
-
函数参数:
-
c:待写入的字符;
-
fp:已打开的文件指针;
-
-
返回值:
-
成功:返回字符c;
-
失败:返回EOF(-1);
-
-
举例:单字符写入
/** * 单字符写入 */ #include <stdio.h> int main(int argc,char **argv) { if(argc < 3) { printf("输入有误,请按照<%s 文件路径 文本数据>格式输入\n",argv[0]); return -1; } FILE* fp = fopen(argv[1],"w"); if(!fp) { perror("文件打开失败!"); return -1; } // 单字符写入 while(*argv[2] != '\0') { // ./a.out file1.txt I_Love_Your fputc(*argv[2],fp); argv[2]++;// 指针偏移1位 } fclose(fp); return 0; }
-
多字符写入
-
函数名:fputs
-
头文件:#include <stdio.h>
-
函数原型:int fputs(const char *s,FILE *stream);
-
函数功能:向fp代表的文件中写入一个字符数组s
-
函数参数:
-
s:待写入的字符数组(写入缓冲区);
-
fp:已打开的文件指针;
-
-
返回值:
-
成功:返回非负整数(>=0);
-
失败:返回EOF(-1);
-
-
举例:多字符写入(新写入的内容会覆盖旧内容)
/** * 多字符写入 */ #include <stdio.h> int main(int argc,char **argv) { if(argc < 3) { printf("输入有误,请按照<%s 文件路径 文本数据>格式输入\n",argv[0]); return -1; } FILE* fp = fopen(argv[1],"w"); if(!fp) { perror("文件打开失败!"); return -1; } // 单字符写入 // ./a.out file1.txt I_Love_Your fputs(argv[2],fp); fclose(fp); return 0; }
3)文件结束
-
判文件结束
-
函数:feof(fp)
- 头文件:#include <stdio.h>
- 函数原型:int feof(fp)
- 函数功能:在读fp指向的文件时判断是否遇到文件结束
- 函数参数:
- fp:已打开的文件指针
- 返回值:
- 文件未读取完毕:返回0;
- 失败:文件读取完毕:返回非0;
-
举例:实现一个文本文件的拷贝
/** * feof案例:将一个磁盘文件中的信息复制到另一个磁盘文件中。 */ #include <stdio.h> #include <string.h> int main(int argc,char** argv) { // main函数输入判断 if(argc < 3) { printf("输入有误,请按照<%s 源文件路径 目标文件路径>格式输入\n",argv[0]); return -1; } // 打开文件 FILE* in = fopen(argv[1],"r"); FILE* out = fopen(argv[2],"w"); // 校验 if(!in || !out) { perror("文件打开失败!"); return -1; } // 单个字符读写 // while(!feof(in)) // { // 读写操作 // fputc(fgetc(in),out); //} // 多个字符读写 // 创建一个缓冲区,用来存放读写的数据 char buf[64] = {0}; // 使用循环读写文件 while(fgets(buf,64,in)!=NULL) { // 写入数据 fputs(buf,out); // 重置缓冲区 memset(buf,0,sizeof(buf)); } /** * feof案例:将一个磁盘文件中的信息复制到另一个磁盘文件中。 */ #include <stdio.h> #include <string.h> int main(int argc,char** argv) { // main函数输入判断 if(argc < 3) { printf("输入有误,请按照<%s 源文件路径 目标文件路径>格式输入\n",argv[0]); return -1; } // 打开文件 FILE* in = fopen(argv[1],"r"); FILE* out = fopen(argv[2],"w"); // 校验 if(!in || !out) { perror("文件打开失败!"); return -1; } // 单个字符读写 // while(!feof(in)) // { // 读写操作 // fputc(fgetc(in),out); //} // 多个字符读写 // 创建一个缓冲区,用来存放读写的数据 char buf[64] = {0}; // 使用循环读写文件 while(fgets(buf,64,in)!=NULL) { // 写入数据 fputs(buf,out); // 重置缓冲区 memset(buf,0,sizeof(buf)); }
4)数据块的读写(二进制)
-
fread
-
头文件:#include <stdio.h>
-
函数原型:size_t fread(void *ptr,size_t size,size_t count,FILE* fp)
-
函数功能:从fp代表的文件中以size为单位(一个数据块)读取count个数据块存放在ptr内存中
-
函数参数:
-
ptr:内存空间首地址,用于存放读取到数据(缓冲区)
-
size:数据块大小,以byte为单位
-
count:待读取的数据块的个数
-
fp:已打开的文件指针
-
-
返回值:
-
成功:返回实际写入的字节数;
-
失败:返回 < 0
-
-
举例:数据库读写案例:将二进制文件stu-list中的学生信息读出来
/** * 数据库读写案例:将二进制文件stu-list中的学生信息读出来。 */ #include <stdio.h> #define SIZE 4 // 创建学生结构体 struct Student { char name[20]; int num; int age; char addr[50];// 住址 } stud[SIZE]; void main() { int i; FILE* fp; if((fp = fopen("stu-list","rb")) == NULL) { perror("文件打开失败!"); return; } // 循环读取二进制文件 for(i = 0; i < SIZE; i++) { fread(&stud[i],sizeof(struct Student),1,fp); // 将读到的内容输出到控制台 printf("%-10s%4d%4d%-20s\n",stud[i].name,stud[i].num,stud[i].age,stud[i].addr); } fclose(fp); }
-
fwrite
-
头文件: #include <stdio.h>
-
函数原型:size_t fwrite(const void* ptr,size_t size,size_t count,FILE* fp)
-
函数功能:向fp代表的文件中以size为一个数据块,写入count个数据块到fp
-
函数参数:
-
ptr:内存空间首地址,用于存放待写入的数据,(写入缓冲区)
-
size:数据块大小,以byte为单位
-
count:待写入的数据块个数
-
fp:已打开的文件指针
-
-
返回值:
-
成功:返回实际写入字节数
-
失败:写入完毕,返回 < 0
-
-
举例:数据库读写案例:从键盘输入4个学生的有关数据,然后把它们转存到磁盘文件上去
/** * 数据库读写案例:从键盘输入4个学生的有关数据,然后把它们转存到磁盘文件上去。 */ #include <stdio.h> #define SIZE 4 // 学生数量 // 创建学生结构体 struct Student { char name[20]; int num; int age; char addr[50];// 住址 } stud[SIZE]; // 保存学生信息到文件 void save() { FILE* fp; int i; if((fp = fopen("stu-list","wb")) == NULL)// stu-list对应的目录:./stu-list,wb-二进制写 入,默认是文本写入 { perror("文件打开失败!"); return; } // 写入操作 for(i = 0; i < SIZE; i++) { fwrite(&stud[i],sizeof(struct Student),1,fp); } // 关闭文件 fclose(fp); } void main() { int i; printf("请输入学生的信息:姓名,学号,年龄,住址\n"); for(i = 0; i < SIZE;i++) { scanf("%s%d%d%s",stud[i].name,&stud[i].num,&stud[i].age,stud[i].addr); // 录入一个学生,就保存一个学生到文件中 save(); } }
5)文件的随机读写
- 说明:C语言允许程序员在读写文件内容时,可在指定的位置上读写数据;
- 文件随机读写的核心操作: 文件位置指针的定位
-
文件位置指针的移动方法:
-
rewind
-
头文件: #include <stdio.h>
-
函数原型: void rewind(FILE* fp);
-
函数功能: 将文件位置指针定位到文件开头
-
函数参数:
-
fp: 已经打开的文件指针
-
-
返回值: 无
-
-
fseek
-
头文件: #include <stdio.h>
-
函数原型: int fseek(FILE *fp, long offset, int whence);
-
函数功能: 将文件位置指针定位到指定位置
-
函数参数:
-
fp: 已经打开的文件指针
-
offset: 相对于参考位置的偏移位置
-
whence: 参考位置
- SEEK_SET 或 0 表示文件头
- SEEK_CUR 或 1 表示当前读写的位置
- SEEK_END 或 2 表示文件尾
-
-
返回值:
-
成功 :0;
-
失败 : -1;
-
-
-
ftell
-
头文件: #include <stdio.h>
-
函数原型: long ftell(FILE *fp);
-
函数功能: 获取文件位置指针当前位置
-
函数参数:
-
fp: 已经打开的文件指针
-
-
返回值:
-
成功 :文件位置指针当前位置;
-
失败 : -1;
-
-
-
举例:有一个磁盘文件,第一次将它的内容显示在屏幕上,第二次把它复制到另一文件上
/** * 有一个磁盘文件,第一次将它的内容显示在屏幕上,第二次把它复制到另一文件上。 */ #include <stdio.h> void main() { FILE *fp1, *fp2; fp1 = fopen("file1.c", "r"); fp2 = fopen("file2.c", "w"); while (!feof(fp1)) putchar(getc(fp1)); /*输出到屏幕*/ rewind(fp1); /*位置指针返回到文件头*/ while (!feof(fp1)) putc(getc(fp1), fp2); /*从文件file1的头读起,输出到文件file2中*/ fclose(fp1); fclose(fp2); /*关闭文件*/ }
举例:在磁盘文件上存有10个学生的数据。要求将第1、3、5、7、9个学生数据输入计算机,并在屏幕上显示出来
/** * 在磁盘文件上存有10个学生的数据。要求将第1、3、5、7、9个学生数据输入计算机,并在屏幕上显示出来。 */ #include <stdlib.h> #include <stdio.h> struct student_type { char name[10]; int num; int age; char sex; } stud[10]; void main() { int i; FILE *fp; if ((fp = fopen("stud-dat", "rb")) == NULL) { printf("can not open file\n"); return; } 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("%s %d %d %c\n", stud[i].name, stud[i].num, stud[i].age, stud[i].sex); } fclose(fp); }
举例:下列C程序的功能是,用“追加”的形式打开文件gg.txt,查看文件读写指针的位置,然后向文件写入“data”, 再查看文件读写指针的位置。
/** * 下列C程序的功能是,用“追加”的形式打开文件gg.txt,查看文件读写指针的位置,然后向文件写入“data”, 再查看 文件读写指针的位置。 */ #include "stdio.h" main() { long p; FILE *fp; if ((fp = fopen("gg.txt","a")) == NULL) { printf("cannot open this file!\n"); return; } p = ftell(fp); printf("p=%ld\n",p); fputs("data", fp); p = ftell(fp); printf("p=%ld\n",p); fclose(fp); }
三、作业
1.编写程序实现文件拷贝(用 fgets函数实现)
代码:
运行效果:
2. 从键盘输入一串字符,存放到一个文件中,后打开读取存到内存另一个空间并显示,验证是否正确 键盘接收--> 存放到文件-->读文件-->显示内容
代码:
运行效果:
3. 定义一个表示公交线路的结构体,要求有线路名称,起始站,终点站,里程等成员,定义结构体数 组,用来存储4条公交线路信息,并将数据保存到文件。要求验证是否存储成功。
代码:
运行效果:
4. 自行查找 fscanf 和 fprintf 函数,并尝试用这两个函数进行数据的保存和读取。
scanf("格式控制符",地址列表)
printf("格式控制符",输出列表)
fscanf(文件指针,"格式控制符",地址列表)
fprintf(文件指针,"格式控制符",输出列表)
代码:
运行效果: