1.标准流
2.文件的打开和关闭
#include<stdio.h>
int main()
{FILE* pf = fopen("text.txt", "w");if (pf == NULL){perror("fopen");return 1;}fclose(pf);pf = NULL;return 0;
}
这个是所有文件,无论进行任何操作都会经历的文件的打开和关闭,中间可能我们会进行文件的读和文件的写,以及对于文件的修改等等操作。
3.二进制文件和文本文件
数据在内存里面以二进制的形式存储,如果不进行转换输出到文件里面就是二进制文件;
如果转换成ASCII形式,以ASCII形式存储的文件就是文本文件;
对于二进制的文件,我们无法看懂,如果是在VS上面,可以使用二进制编辑器进行转换,这样的话,文件里面的内容就会以16进制的形式显示出来,我们这个时候就可以正常阅读文件了;
4.文件的读和写
我们首先要清楚什么是文件的读和写?
文件的读就是从文件里面获取数据,文件的写就是从程序向文件里面写入数据;
int main()
{FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return 1;}fclose(pf);pf = NULL;return 0;
}
我们这段代码是进行了读文件的操作,那么这个文件必须是存在的,只有这样我们才能够进行相应的操作,但是我们路径下面是没有这个文件的,所以perror就会显示对应的错误;如果已经新建了这个文件,那么程序运行就不会显示任何的错误;
如果我们是一些的方式,目录就会自己新建生成test.txt文件的;
5.文件的顺序读写函数
(1)fputc函数
这个函数具有2个参数,我们可见就是把第一个参数(字符)写到第二个参数(文件)里面去;
int main()
{FILE* pf = fopen("test.txt", "w");if (pf == NULL){perror("fopen");return 1;}fputc('a', pf);fputc('b', pf);fputc('c', pf);fclose(pf);pf = NULL;return 0;
}
上面的代码是一次写入一个字符,我们也可以利用for循环语句,一次写入多个字符,下面的这段代码就是写入26个字符;
int main()
{FILE* pf = fopen("test.txt", "w");if (pf == NULL){perror("fopen");return 1;}char ch = 0;for (ch = 'a'; ch < 'z'; ch++){fputc(ch, pf);}fclose(pf);pf = NULL;return 0;
}
(2)fgetc函数
这个函数的返回值是int类型的,如果成功读取返回就是对应字符的ASCII值,如果是遇到文件的末尾或者读取失败,就会返回EOF(数值是-1);我们可以利用while循环读取文件里面的字符;
int main()
{FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return 1;}int i = 0;int ch = 0;while((ch=fgetc(pf))!=EOF){printf("%c", ch);}fclose(pf);pf = NULL;return 0;
}
(3)fputs函数
上面的两个函数一次只能够读一个字符或者是写一个字符,我们写入字符串(也就是一次性写一堆字符)就可以使用fputs函数;
int main()
{FILE* pf = fopen("test.txt", "w");if (pf == NULL){perror("fopen");return 1;}fputs("hello world", pf);fputs("hello china", pf);//关闭文件fclose(pf);pf = NULL;return 0;
}
我们可以多次进行写入,但是这个函数是没有换行的功能的,需要我们手动进行换行,也就是添加斜杠n进行换行;
(4)fgets函数
int main()
{FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return 1;}char arr[10] = { 0 };fgets(arr, 10, pf);//关闭文件fclose(pf);pf = NULL;return 0;
}
这个函数有3个参数,第一个是指针数组(我们读取的数据放进去),第二个参数是读取的个数,第三个参数是文件流(就是从哪个文件里面读取);
实际上,我们这里写的是10个字符,他只会读取9个字符,最后一个留给了斜杠0;
如果我们读取10个字符,文件只有5个字符,这个时候就会读取文件末尾的斜杠0读取进来(这个过程我们通过调试可以观察到),下面是调试的截图,读者可以自行尝试:
(5)fprintf函数
这个函数和我们熟知的printf函数参数很相似,就是多了一个文件流参数:对于初学者,我们可先按照printf函数的形式进行写代码,最后加上我们的文件流就可以了:
struct S
{char name[20];int age;float score;
};
int main()
{struct S s = { "章山",18,99.3f };FILE* pf = fopen("test.txt", "w");if (pf == NULL){perror("fopen");return 1;}fprintf(pf,"%s %d %f", s.name, s.age, s.score);//关闭文件fclose(pf);pf = NULL;return 0;
}
这个里面我们是创建了一个结构体类型的变量,最后打印姓名,年龄和成绩,由此可见,写法和printf相似,就是在前面加上文件流pf,这样就可以写入文件里面去了;
(6)fscanf函数
我们把写进去的数据读出来,最后我们使用printf打印到我们的屏幕上;
因为fscanf要地址,name这个数组已经是地址,不需要加上取地址符号,其他的两个要加上取地址符号;
上面我们介绍的六个函数,适用于所有的流,既可以是文件流pf(我们上面的案例使用的全部是pf文件流),也可以是标准流,例如我们把pf改为stdout这样就可以把内容打印在屏幕上面(使用pf直接写到文件里面去了);
(7)fwrite函数
int main()
{int arr[5] = { 1,2,3,4,5 };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;return 0;
}
这个函数有4个参数,第一个是地址,第二合适单个元素的大小,第三个是个数(写的个数),第四个是文件流,这个是以二进制写入的(wb)我们看不懂,
我们可以使用下面的函数fread同样以二进制的方式读出来;
(8)fread函数
int main()
{int arr[5] = { 1,2,3,4,5 };FILE* pf = fopen("test.txt", "wb");if (pf == NULL){perror("fopen");return 1;}int sz = sizeof(arr) / sizeof(arr[0]);fread(arr, sizeof(arr[0]), sz, pf);int i = 0;for (i = 0; i < 5; i++){printf("%d ", arr[i]);}fclose(pf);pf = NULL;return 0;
}
上面的读出前提是我们已经知道数组大小,如果不知道,我们可以利用循环读取:
我们之所以能够这样做是因为fread的返回值是读取成功的字符的个数,我们利用fread的返回值,读取到最后就可以自动退出循环,这样即使不知道数组的大小,我们也可以进行打印;
78两个函数是以二进制的方式进行读和写的。
6.一组函数的对比
int main()
{char arr[300] = { 0 };struct S s = { "章山",18,99.3f };FILE* pf = fopen("test.txt", "w");if (pf == NULL){perror("fopen");return 1;}sprintf(arr, "%s %d %f", s.name, s.age, s.score);printf("%s\n", arr);struct S t = { 0 };sscanf(arr, "%s %d %f", t.name, &(t.age), &(t.score));printf("%s %d %f", t.name, t.age, t.score);//关闭文件fclose(pf);pf = NULL;return 0;
}
sprintf和sscanf函数,第一个是把格式化的数据转化为字符串,第二个是把字符串转换为格式化的数据;
7.文件的随机读写函数
(1)fseek函数