C语言文件操作全解析
一、什么是文件?
讲文件操作之前要理解文件的概念。
数据文件是程序运行时读/写的数据,是文件操作的对象;数据文件可以按在外存上的存储形式又划分为文本文件和二进制文件。文本文件在外存上以ASCII码的形式存储,而二进制文件(存储前不需要转换为ASCII码)在外存上以二进制的形式存储。
举个例子,有整数10000,如果以ASCII码的形式输出到外存上,需要把每一位转化为对应的ASCII码,即 00110001 00110000 00110000 00110000 00110000,占用5个字节(每个字符一个字节),而以二进制形式输出,则只占用4个字节,即 00000000 00000000 00100111 00010000.
二、文件的打开和关闭
1.标准流
流是一个抽象的概念,我们的输入输出操作都是通过流来完成的。C语言程序在启动时默认打开了3个流。
(1)stdin - 标准输入流,在大多数环境中从键盘输入,如 scanf 函数就是从标准输入流中读取数据
(2)stdout - 标准输出流,在大多数环境中输出至显示器界面,printf函数就是讲信息输出到标准输出流中
(3)stderr - 标准错误流,大多数环境中输出到显示器界面
2.文件指针
stdin、stdout、stderr 三个流的类型是:FILE * ,通常称为⽂件指针。
FILE* pf; // ⽂件指针变量
3.文件的打开和关闭
⽂件在读写之前应该先打开⽂件,在使⽤结束之后应该关闭⽂件。使⽤ fopen 函数来打开⽂件, fclose 来关闭⽂件。
// 打开⽂件
FILE * fopen ( const char * filename, const char * mode );
// filename 表示文件名
// mode 表示文件的打开方式// 关闭⽂件,fclose不会把指针置空
int fclose ( FILE * stream );
//stream 指向要关闭的文件
4.文件的打开方式
从上面的代码块中可看到在打开文件时,还应输入对应的打开方式,下面我们来介绍一下文件的打开方式有哪些。
下面的输入是指从文件输入程序,同理,输出是指从程序输出到文件中。
文件打开方式 | 含义 | 如果指定文件不存在 |
---|---|---|
"r"(只读) | 为了输入数据,打开一个已经存在的文本文件 | 出错 |
"w"(只写) | 为了输出数据,打开一个文本文件 | 创建一个新文件 |
"a"(追加) | 向文本文件末尾添加数据 | 创建一个新文件 |
"rb"(只读) | 为了输入数据,打开一个二进制文件 | 出错 |
"wb"(只写) | 为了输出数据,打开一个二进制文件 | 创建一个新文件 |
"ab"(追加) | 向一个二进制文件末尾添加文件 | 创建一个新文件 |
"r+"(读写) | 为了读和写,打开一个文本文件 | 出错 |
"w+"(读写) | 为了读和写,创建一个新文件 | 创建一个新文件 |
"a+"(读写) | 打开一个文件,在文件末尾进行读写 | 创建一个新文件 |
"rb+"(读写) | 为了读和写,打开一个二进制文件 | 出错 |
"wb+"(读写) | 为了读和写,新建一个新的二进制文件 | 创建一个新文件 |
"ab+"(读写) | 打开一个二进制文件,在文件末尾进行读和写 | 创建一个新文件 |
需要注意的是,“w”会把打开的文件的内容清空,“a”不会。
三、文件的顺序读写
下面介绍一些顺序读写相关的函数。
函数名 | 功能 | 适用于 |
---|---|---|
fgetc | 字符输入函数 | 所有输入流 |
fputc | 字符输出函数 | 所有输出流 |
fgets | 文本行输入函数 | 所有输入流 |
fputs | 文本行输出函数 | 所有输出流 |
fscanf | 格式化输入函数 | 所有输入流 |
fprintf | 格式化输出函数 | 所有输出流 |
fread | 二进制输入 | 文件输入流 |
fwrite | 二进制输出 | 文件输出流 |
所有输入流:文件流,标准输入流(stdin,键盘上输入)
所有输出流:文件流,标准输出流(stdout,屏幕上输出)
文件输入流:文件流
文件输出流:文件流
1.fputc
向文件中写入内容,成功的话返回的是写入内容对应的ASCII码值,失败的话函数会返回 EOF(-1)。
int fputc ( int character, FILE * stream );//要写入的字符对应的ASCII码,指针
举例
int main()
{//打开文件FILE* pf = fopen("test.txt","w");if (pf == NULL){perror("fopen");return 1;}//写文件for (char ch = 'a'; ch <= 'z'; ch++){fputc(ch ,pf); //向pf指向的地址对应的文件中写入 a-z// abcdefghijklmnopqrstuvwxyz}//关闭文件fclose(pf);pf = NULL;return 0;
}
2.fgetc
读取文件内容,成功的话函数返回被读取的字符,但会被提升为 `int` 类型;如果遇到文件末尾或者失败返回 EOF(-1)
int fgetc ( FILE * stream );//指针
举例
int main()
{//打开文件FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return 1;}//读文件int ch = 0;while (( ch = fgetc(pf)) != EOF){printf("%c", ch); //abcdefghijklmnopqrstuvwxyz}//关闭文件fclose(pf);pf = NULL;return 0;
}
3.fputs
把 str 指向的字符串写入文件,注意 \0 不会写进去
int fputs ( const char * str, FILE * stream );
举例
int main()
{//打开文件FILE* pf = fopen("test.txt", "w");if (pf == NULL){perror("fopen");return 1;}//写文件fputs("hello world", pf);fputs("hello mimi", pf);//hello worldhello mimi//关闭文件fclose(pf);pf = NULL;return 0;
}
4.fgets
从 stream 指向的文件中读出最多 num-1 个字节的数据放到 str 指向的字符串中,函数成功读取到字符,会返回指向输入缓冲区的指针 str ;遇到文件末尾或读取错误会返回 NULL。
读出来的字符数为 num-1 是因为要在字符串最后加一个 \0
char * fgets ( char * str, int num, FILE * stream );
举例
int main()
{//打开文件FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return 1;}//读文件char arr[10] = { 0 };fgets(arr, 10, pf);printf("%s", arr); //hello worfgets(arr, 10, pf);printf("%s", arr);//hello worldhello m//关闭文件fclose(pf);pf = NULL;return 0;
}
5.fprintf
格式化输出函数
int fprintf ( FILE * stream, const char * format, ... );
举例
struct S
{char name[20];int age;float score;
};int main()
{struct S s = { "张三",20,65.5f };//想把s中的数据存放在文件中FILE* pf = fopen("test.txt", "w");if (pf == NULL){perror("fopen");return 1;}//写文件 - 是以文本的形式写进去的fprintf(pf,"%s %d %f", s.name, s.age, s.score);//张三 20 65.500000fclose(pf);pf = NULL;return 0;
}
6.fscanf
格式化输入函数
int fscanf ( FILE * stream, const char * format, ... );
举例
int main()
{struct S s = { 0 };//想从文件test.txt中读取数据放在s中FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return 1;}//读文件fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score));//打印在文件上fprintf(stdout, "%s %d %f", s.name, s.age, s.score);//printf("%s %d %f\n", s.name, s.age, s.score);//张三 20 65.500000fclose(pf);pf = NULL;return 0;
}
7.sprintf和sscanf
sprintf - 把数据转化为字符串(不会在屏幕上输出),sscanf - 在字符串中读取格式化的数据。
int sscanf ( const char * s, const char * format, ...);int main()
{char buf[200] = { 0 };struct S s = { "张三",20,65.5f };sprintf(buf, "%s %d %f\n", s.name, s.age, s.score);printf("1.以字符串格式打印:%s\n", buf);//1.以字符串格式打印:张三 20 65.500000struct S t = { 0 };sscanf(buf,"%s %d %f", t.name, &(t.age), &(t.score));printf("2.按照格式打印 :%s %d %f\n", t.name, t.age, t.score);//2.按照格式打印 :张三 20 65.500000return 0;
}
8.fwrite
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
ptr:指向要写什么的数组的指针;size:每个元素的长度,单位为字节;count:每次要写的元素个数;stream:写入哪里
int main()
{int arr[] = { 1,2,3,4,5,6,7 };FILE* pf = fopen("test.txt", "wb");if (pf == NULL){perror("fopen");return 1;}//写数据int sz = sizeof(arr) / sizeof(arr[0]);fwrite(arr, sizeof(arr[0]), sz, pf); //以二进制的形式写进去fclose(pf);pf = NULL;
}
9.fread
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
ptr:指向读取的内容存放的位置的指针,大小最小为一个数据的大小;size:每个元素的长度,单位为字节;count:每次要写的元素个数;stream:从哪里读;返回的是成功读取的元素的个数
int main()
{int arr[7] = { 0 };FILE* pf = fopen("test.txt", "rb");if (pf == NULL){perror("fopen");return 1;}//读数据int i = 0;while (fread(arr + i, sizeof(arr[0]), 1, pf)) //成功读到一个数据会返回1,为真;如果读完或读取失败就停止了{printf("%d ", arr[i]);i++;}//1 2 3 4 5 6 7fclose(pf);pf = NULL;
}
四、文件的随机读写
1.fseek
根据⽂件指针的位置和偏移量来定位⽂件指针(⽂件内容的光标)
int fseek ( FILE * stream, long int offset, int origin );
stream:流
offset:偏移量,单位是字节,即一个字符大小
origin:起始位置
(1)可以是文件的起始位置(SEEK_SET)
(2)文件指针当前位置(SEEK_CUR)
(3)文件末尾(SEEK_END),文件最后一个字节的下一个位置
int main()
{FILE* pf=fopen("test.txt", "r"); //test.txt中存的是abcdefgif (pf == NULL){perror("fopen");return 1;}//读文件--开始时光标在文件开始(即a)int ch=fgetc(pf); //读完之后光标向后移动一位指向bprintf("%c", ch); //afseek(pf,4,SEEK_CUR); //从b开始向后数4个字节,数到fch = fgetc(pf);printf("%c", ch); //ffclose(pf);pf = NULL;return 0;
}
2.ftell
返回值文件指针相对于起始位置的偏移量
int fseek ( FILE * stream, long int offset, int origin );
举例
int main()
{FILE* pf = fopen("test.txt", "r"); //test.txt中存的是abcdefgif (pf == NULL){perror("fopen");return 1;}//读文件--开始时光标在文件开始(即a)int ch = fgetc(pf); //读完之后光标向后移动一位指向bprintf("%c", ch); //afseek(pf, -1, SEEK_END); //如果改成0,则是文件中文本的长度printf("%d", ftell(pf)); //6fclose(pf);pf = NULL;return 0;
}
3.rewind
让文件指针的位置回到文件的起始位置
void rewind ( FILE * stream );
五、文件结束的判定
1.ferror
ferror(pf) 是 C 语言中用于检查文件操作是否发生错误的函数
1. 如果未发生错误,返回 0;
2. 如果发生过错误,返回一个非 0 值
2.feof
在⽂件读取过程中,不能⽤feof函数的返回值直接来判断⽂件的是否结束。
feof 的作⽤是:当⽂件读取结束的时候,判断是读取结束的原因是否是:遇到⽂件尾结束。
(1)文本文件读取是否结束,判断返回值是否为EOF(fgetc),或者NULL(fgets)
1)fgetc 判断是否为 EOF2)fgets 判断返回值是否为 NULL
(2) 二进制文件的读取结束通过返回值是否小于实际要读的个数来判断
int feof(FILE * stream);
如果遇到文件末尾返回一个非0值,否则返回0.
下面以文本文件举例
#include <stdio.h>
#include <stdlib.h>
int main(void)
{FILE* pf = fopen("test.txt", "r");if (pf == NULL){perroe("fopen");return 1;}//读取int ch = 0;while ((ch = fgetc(pf)) != EOF){printf("%c\n", ch);}//判断是什么原因结束的if (feof(pf)){printf("遇到文件末尾,读取正常结束\n");}else if (ferror(pf)){perror("fgetc");}fclose(pf);pf = NULL;return 0;
}