前言:只有接触了文件的代码,才算真正的编程,那么,这篇博客就来简单介绍一下文件吧
文件的定义:
文件是一个外存的范畴;文件是计算机表达信息的最小逻辑单位;文件就是信息二进制化后在外存中的存储。
文件的组成:
- 文件名;
- 文件的主体内容;
- 文件属性。
文件内容:
所有的内容都是二进制的!
在这里要提到一点:操作系统(OS)在管理文件时,使用“文件控制块(FCB)”这样的数据。
(所谓“文件控制块”,实质上是关于一个文件的一堆数据,数据越详细,OS对于文件的掌控越详尽)而且,FCB是OS的有限资源。
不同的程序设计语言,都对于文件的操作给出了;
不同的程序设计语言对于FCB进行了各自的表示和封装(C语言用FILE来封装FCB)
对文件进行编程,需要先申请FCB。
现在,就来介绍一下有关文件处理基本的几个函数吧:
首先,要声明一点,所有相关函数都包含在<stdio.h>头文件中
1.FILE *fopen(“文件名”,“打开方式”):
这里的打开方式,有以下几种:
fopen()的本质是申请操作系统的FCB资源!
2.int fclose(FILE *文件指针):
相对地,fclose的本质相当于归还FCB资源。
关于返回值:
如果成功关闭,则该方法返回0。
如果失败,则返回 EOF。
3.int fprintf(FILE *文件指针,“格式符”,...):
功能:向文件指针所指向的文件中写入相应类型的数据。
(写入数据时,本质上是字符串)
关于返回值:
如果成功,则返回写入的字符总数,
如果失败,返回一个负数。
注意:fprintf(stdout,"格式符",...)等价于printf("格式符",...)
4.int fscanf(FILE *文件指针,“格式符”,...):
从文件指针所指向的文件中读取数值并赋值给相应变量。
(读取数据时,首先是字符串,然后根据格式符转换为相应格式)
关于返回值:
如果成功,该函数返回成功匹配和赋值的个数。
如果到达文件末尾或发生读错误,则返回 EOF。
注意:fscanf(stdin,"格式符",...)等价于scanff("格式符",...)
5. char* fgets(char *目标字符串(str), int 最大字符数, FILE *文件指针):
功能:从文件指针所指文件中读取一行,并把它存储在 str 所指向的字符串内。
当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。
关于返回值:
如果成功,该函数返回相同的 目标字符串。
如果到达文件末尾或者没有读取到任何字符,str 的内容保持不变,并返回一个空指针。
如果发生错误,返回一个空指针。
6.int fputs(const char *s, FILE *文件指针)
(说明:s -- 这是一个字符串,包含了要写入的以空字符终止的字符序列)
功能:把字符串写入到文件指针所指向的文件中,但不包括空字符。
关于返回值:
若成功则返回一个非负值,
如果发生错误则返回 EOF。
7.int fgetc(FILE *文件指针):
功能:从文件指针所指向的文件中读取一字节的信息
关于返回值:
该函数以无符号 char 强制转换为 int 的形式返回读取的字符,
如果到达文件末尾或发生读错误,则返回 EOF。
8.int putc(int 要被写入的字符, FILE * 文件指针):
功能:将目标字符写入文件指针所指向的文件中。
关于返回值:
如果成功,则返回被写入的字符。
如果发生错误,则返回 EOF,并设置错误标识符。
9.int feof(FILE *文件指针):
功能:对上一次(紧上一次)的文件操作是否正确判定。
关于返回值:
若成功,返回值为0;
若失败,则为返回值非0
10.size_t fwrite(const void *被写入元素数组的指针, size_t 被写入的每个元素的大小, size_t 元素个数, FILE *文件指针):
说明:被写入元素的大小按照二进制为单位
功能:将数组,结构体等类型的值写入指定文件中
关于返回值:
如果成功,该函数返回一个 size_t 对象,表示元素的总数,该对象是一个整型数据类型。
如果该数字与 元素个数 参数不同,则会显示一个错误。
11.size_t fread(void *要存入元素的数组的指针, size_t 要读取每个元素大小, size_t 元素个数, FILE *文件指针):
说明:被写入元素的大小按照二进制为单位
功能:从数组,结构体中读取指定大小数据
关于返回值:
成功读取的元素总数会以 size_t 对象返回,size_t 对象是一个整型数据类型。
如果总数与 nmemb 参数不同,则可能发生了一个错误或者到达了文件末尾。
12.long int ftell(FILE *文件指针):
功能:用于查看文件大小或指针对于初始位置的相对偏移量
说明,正常使用的返回值为当前位置距离初始位置的偏移量,单位为字节。
关于返回值:
如果使用成功该函数返回位置标识符的当前值。
如果发生错误,则返回 -1L,全局变量 errno 被设置为一个正值。
13.int fseek(FILE *stream, long int 偏移量, int 常量):
功能:将文件指针指向目标位置;
说明:关于常量,一共有三种情况:
关于返回值:
如果成功,则该函数返回零,
否则,返回非零值。
14.int rename(const char *原文件名, const char *新文件名):
功能:顾名思义,给文件改名的函数。
15.int remove(const char *要删除的文件名):
功能;删除指定名的文件。
那么以上就是我们对于文件操作的基本函数了。
现在,我们就来实现一些常用的文件操作吧!
首先,假设已有文件为data.txt
那么,文件的内容展示(showFile.c):
#include <stdio.h>
int main() {
FILE *fp;
int ch;
fp = fopen("data.txt", "r");
ch = fgetc(fp);
while (!feof(fp)) {
printf("%c", ch);
ch = fgetc(fp);
}
printf("
!!!
");
fclose(fp);
return 0;
}
文件内容复制(copyFile.c):
(假设将data.txt的内容复制给data.sav)
#include <stdio.h>
int main() {
FILE *fpIn;
FILE *fpOut;
int ch;
fpIn = fopen("data.txt", "r");
fpOut = fopen("data.sav", "w");
ch = fgetc(fpIn);
while (!feof(fpIn)) {
fputc(ch, fpOut);
ch = fgetc(fpIn);
}
fclose(fpIn);
fclose(fpOut);
return 0;
}
将 数组 写入指定文件中(aboutBinary.c):
#include <stdio.h>
int main() {
FILE *fp;
int arr[10] = {1, 100, 1000, 10000, 100000};
// {0x00000001, 0x00000064, 0x000003E8, 0x00002710, 0x000186A0}
int i;
fp = fopen("abcd.dat", "wb");
fwrite(arr, sizeof(int), 5, fp);
//等同于 for (i = 0; i < 5; i++) {
// fwrite(arr + i, sizeof(int), 1, fp);
// }
fclose(fp);
return 0;
}
将 结构体 写入指定文件中(aboutStruct.c):
#include <stdio.h>
typedef struct {
int one;
char two;
int three;
double four;
}MY_TYPE;
int main() {
MY_TYPE num = {
0x12,
'A', // 0x41
0x135,
3.14,
};
FILE *fp;
fp = fopen("abcde.dat", "wb"); //wb是用二进制方式写入的打开方式
fwrite(&num, sizeof(MY_TYPE), 1, fp);
fclose(fp);
return 0;
}
在之前的一篇博客《(带头节点的链表) 宿舍管理系统)》中,我们提到了文件,那么,现在我们用文件来简单实现一下基本的操作吧:
首先,录入信息函数:
boolean enterInf(NEW_TYPE *ptr, int size, int count, FILE *fp){
if(NULL == fp) {
return FALSE;
}
fwrite(ptr, size, count, fp);
return TRUE;
}
这里对上面这段这段代码做几点说明:
1.这个函数存在前提是文件以“wb”形式打开了(有打开就一定要记得关闭);
2.关于参数size,可以在引用函数时写作sizeof(NEW_TYPE);
3.关于返回值,可以在主函数中用来判断用户是否使用错误
其次,是查看信息函数:
要制作查看函数,我们根据之前制作管理系统的知识,将函数做成查找指定信息和查看单独信息的两个函数
1.查找单独信息:
boolean seekOne(FILE *fp, int count, size_t NEW_TYPE) {
int no;
if(NULL == fp) {
return FALSE;
}
printf("请输入要查看信息编号: ");
scanf("%d", &no);
if(no > count) {
return FALSE;
}
fseek(fp, sizeof(NEW_TYPE) * no, SEEK_SET);
return TRUE;
}
2.显示一个信息:
void showOne(FILE *fp, size_t NEW_TYPE) {
char NEW_TYPE[sizeof(NEW_TYPE)];
if(NULL == fp) {
return FALSE;
}
fread(NEW_TYPE, sizeof(NEW_TYPE), 1, fp);
printf("%s", NEW_TYPE);
}
3.显示指定信息:
void showInf(FILE *fp, size_t NEW_TYPE) {
if(NULL == fp) {
return FALSE;
}
seekOne(fp, sizeof(ftell(fp))/sizeof(NEW_TYPE), size_t NEW_TYPE);
showOne(fp);
}
4.显示信息表:
void showAllInf(FILE *fp, size_t NEW_TYPE) {
printf("表头");
for(i=0, i<count, i++) { //这里的count变量是我们存储的信息的数量,我们可以通过一个全局变量实现
fseek(fp, sizeof(NEW_TYPE) , SEEK_CUR);
showOne(fp);
}
}
再者,增添信息函数:
boolean addInf(FILE *fp, size_t NEW_TYPE) {
int no;
printf("请输入要插入信息编号:");
scanf("%d", no);
if(NULL == fp) {
return FALSE;
}
seekOne(fp, sizeof(ftell(fp))/sizeof(NEW_TYPE), size_t NEW_TYPE);
fwrite(&num, sizeof(MY_TYPE), 1, fp);
}