文件的操作
1.什么是文件
文件就是存储在我们电脑中的一些数据,我们一般说的文件分为两种:程序文件和数据文件
程序文件
包括源程序文件(.c文件),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)
数据文件
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行时需要从中读取数据的文件,或输入的文件
我们这里讨论的就是数据文件
2.文件的类型
数据文件有两种存储模式,分别问二进制文件和文本文件;
二进制文件
电脑中的数据都是以二进制的形式存储的,如果不转换为其他的形式输出到外存,那么就是二进制文件
文本文件
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。
3.文件指针
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的信息,(如文件的名字,文件的状态和文件的位置等。这些信息是保存在一个结构体变量中的。该结构体类型是由系统申明的,取名FILE。我们打开一个文件的时候,系统会返回一个文件指针来操作这个结构体的变量。
我们打开文件时用fopen这个函数,fclose这个函数来关闭文件。就像我们要对一个瓶子里面的东西进行使用,我们的第一步就是打开瓶盖,使用完之后再盖上盖子
FILE * fopen ( const char * filename, const char * mode );
int fclose ( FILE * stream );
我们可以写一些代码来看看如何操作
#include <stdio.h>
int main(){
FILE* pf;
pf=fopen("test.txt","w");//第一个参数是文件的路径,以写的方式打开,如果文件不存在就会创建一个新的文本文件
if(pf!=NULL){//如果文件打开失败,会返回NULL,如果不为空,才能对文件进行操作
//文件操作
fclose(pf);//文件使用结束要关闭文件。
}
return 0;
}
4.文件的顺序读写
字符输入/输出函数
int fgetc ( FILE * stream );//字符输入函数,从文件中读取字符到程序中
int fputc ( int character, FILE * stream );//字符输出函数,往文件中写入字符
这两个函数只能在文件里进行存取单个字符的操作
例如:
fputc
#include <stdio.h>
int main(){
FILE* pf = fopen("test.txt", "w");
if (pf == NULL) {//如果打开文件失败,fopen会返回一个空指针,直接显示错误然后退出程序
perror("fopen:");
return 1;
}
int i = 0;
for (i = 0; i < 26; i++) {//把26个字母写进文本文件中,写完一个pf会自动挪到下一个位置进行写入,不会覆盖。
fputc('a' + i, pf);
}
fclose(pf);
pf=NULL;
return 0;
}
fgetc
#include <stdio.h>
int main(){
FILE* pf = fopen("test.txt", "r");
if (pf == NULL) {//如果打开文件失败,fopen会返回一个空指针,直接显示错误然后退出程序
perror("fopen:");
return 1;
}
int ch = 0;
while ((ch=fgetc(pf))!=EOF)//fgetc函数读到一个字符之后指针就会指向下一位。如果读到了文件的末尾,fgetc会返回EOF
{
printf("%c ", ch);
}
fclose(pf);//一定要记得关闭文件指针
pf=NULL;
return 0;
}
文本行输入/输出函数
fputs
int fputs ( const char * str, FILE * stream );
int main() {
FILE* pf = fopen("test.txt", "w");
if (pf == NULL) {//如果打开文件失败,fopen会返回一个空指针,直接显示错误然后退出程序
perror("fopen:");
return 1;
}
fputs("hello\n", pf);
fputs("world\n", pf);
fclose(pf);//一定要记得关闭文件指针
pf = NULL;
return 0;
}
fgets
char * fgets ( char * str, int num, FILE * stream );
int main() {
FILE* pf = fopen("test.txt", "r");
if (pf == NULL) {//如果打开文件失败,fopen会返回一个空指针,直接显示错误然后退出程序
perror("fopen:");
return 1;
}
char arr[100];
fgets(arr, 3, pf);//第一个参数是数组名,用来接收字符串,第二个参数是int类型,会读取n-1个字符
//如果再调用,第一行没读完继续,如果读完了就会跳到第二行,
fgets(arr, 2, pf);
printf("%s", arr);
fclose(pf);//一定要记得关闭文件指针
pf = NULL;
return 0;
}
格式化输入/输出函数
fprintf
int fprintf ( FILE * stream, const char * format, … );
struct S {
char name[20];
int age;
double score;
};
int main() {
struct S s = { "zhangsan",20,90.2 };
FILE* pf = fopen("test.txt", "w");
if (pf == NULL) {//如果打开文件失败,fopen会返回一个空指针,直接显示错误然后退出程序
perror("fopen:");
return 1;
}
fprintf(pf, "%s %d %f", s.name, s.age, s.score);//这个函数跟printf函数非常相似,
//只是第一个参数增加了一个文件指针,把结构体的数据写到了文件里
fclose(pf);//一定要记得关闭文件指针
pf = NULL;
return 0;
}
fscanf
int fscanf ( FILE * stream, const char * format, … );
struct S {
char name[20];
int age;
double score;
};
int main() {
FILE* pf = fopen("test.txt", "r");
if (pf == NULL) {//如果打开文件失败,fopen会返回一个空指针,直接显示错误然后退出程序
perror("fopen:");
return 1;
}
struct S s = { 0 };
fscanf(pf, "%s %d %lf", s.name, &(s.age), &(s.score));//这个函数和scanf很像,把文件中的数据格式化的读出来
printf("%s %d %f", s.name, s.age, s.score);
fclose(pf);//一定要记得关闭文件指针
pf = NULL;
return 0;
}
二进制输入/输出函数
swrite
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
int main() {
struct S s = { "zhangsan",20,90.2 };
FILE* pf = fopen("test.txt", "wb");
if (pf == NULL) {//如果打开文件失败,fopen会返回一个空指针,直接显示错误然后退出程序
perror("fopen:");
return 1;
}
fwrite(&s, sizeof(struct S), 1, pf);//第一个参数就是一个元素地址,第二个参数传入每个元素的大小,第三个参数传入元素个数
fclose(pf);//一定要记得关闭文件指针
pf = NULL;
return 0;
}
fread
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
int main() {
FILE* pf = fopen("test.txt", "rb");
if (pf == NULL) {//如果打开文件失败,fopen会返回一个空指针,直接显示错误然后退出程序
perror("fopen:");
return 1;
}
struct S s = { 0 };
fread(&s, sizeof(struct S), 1, pf);
printf("%s %d %f", s.name, s.age, s.score);
fclose(pf);//一定要记得关闭文件指针
pf = NULL;
return 0;
}
5.文件的随机读写
int fseek ( FILE * stream, long int offset, int origin );
这个函数可以调整读取字符指针的位置。第三个参数有三个取值,决定了偏移的方式,详情仔细看代码
SEEK_SET //从文件的开始往右偏移offset个字符读取
SEEK_CUR //从文件的中间往右偏移offset个字符读取
SEEK_END //从文件的末尾往左偏移offset个字符读取
这么说可能比较抽象,我用代码给大家解释一下,文件中存储了这些子符
int main() {
FILE* pf = fopen("test.txt", "r");
if (pf == NULL) {
perror("fopen():");
return 1;
}
fseek(pf, 2, SEEK_SET);//从文件的开头往右偏移2个字符读取放到pf文件指针,就会读取到c
int ch = fgetc(pf);
printf("%c", ch);
fclose(pf);
pf = NULL;
return 0;
}
int main() {
FILE* pf = fopen("test.txt", "r");
if (pf == NULL) {
perror("fopen():");
return 1;
}
int ch = fgetc(pf);
fseek(pf, 2, SEEK_CUR);//从文件的中间开始读,上一个fgetc已经读取一个字符,文件指针会向右偏移一个,那么再偏移两个字符就是d
ch = fgetc(pf);
printf("%c", ch);
fclose(pf);
pf = NULL;
return 0;
}
int main() {
FILE* pf = fopen("test.txt", "r");
if (pf == NULL) {
perror("fopen():");
return 1;
}
fseek(pf, -3, SEEK_END);//从文件的末尾往左偏移两个字符,第二个参数是负数.
int ch = fgetc(pf);
printf("%c", ch);
fclose(pf);
pf = NULL;
return 0;
}
6.文件的结束
int feof ( FILE * stream );
这个函数很多人容易用错,这个函数不是判定文件是否结束,而是判断是否是正常的读取到了文件的末尾,还是中途出现错误而结束,如果正常读到了文件末尾结束返回非0值。其他错误返回0。
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int c; // 注意:int,非char,要求处理EOF
FILE* fp = fopen("test.txt", "r");
if(!fp) {
perror("File opening failed");
return EXIT_FAILURE;
}
//fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
while ((c = fgetc(fp)) != EOF) // 标准C I/O读取文件循环
{
putchar(c);
}
//判断是什么原因结束的
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
}
这就是C语言中比较容易用到的一些操作,如果有错误的地方欢迎在评论区留言,一起进步,谢谢。