目录
文件
文件分为程序文件和数据文件。使用文件我们可以数据直接存放在电脑硬盘上,做到数据的持久化。
程序文件:包括源文件(.c),目标文件(.obj),可执行程序(.exe)
数据文件:文件的内容不一定是程序,而是程序运行时读写的数据。
文件名:文件路径+文件名主干+文件后缀(c:\code\test.txt)
c:\code\ test .txt
文件的打开和关闭
文件指针(FILE):每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(文件的名字,文件的状态,文件当前的位置等等)。这些信息存在一个结构体变量中的。该结构体类型是由系统声明的,取名FILE。每当打开一个文件时,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中信息。
创建一个FILE*类型的指针变量:
FILE* pf;
文件在读写之前应该先打开文件,在使用结束之后关闭文件。
使用fopen来打开文件,使用fclose来关闭文件。
FILE *fopen(const char *filename, const char *mode); // filename 文件的名称 // mode 打开文件的方式 (r, w, a, rb, wb, ab....)
int fclose(FILE *stream); // stream 关闭的文件流 // 关闭成功返回0,关闭失败返回EOF
文件的使用方式 | 含义 | 如果指定文件不存在 |
---|---|---|
" r " (只读) | 为了输入数据,打卡一个已经存在的文本文件 | 出错 |
" w "(只写) | 为了输出数据,打开一个文本文件 | 建立一个新的文件 |
" a "(追加) | 向文本某位添加数据 | 建立一个新的文件 |
" rb "(只读) | 为了输入数据,打开一个二进制文件 | 出错 |
" wb "(只写) | 为了输出数据,打开一个二进制文件 | 建立一个新的文件 |
" ab "(追加) | 向一个二进制文件末尾添加数据 | 出错 |
" r+ "(读写) | 为了读和写,打开一个文本文件 | 出错 |
" w+ "(读写) | 为了读和写,建立一个新的文件 | 建立一个新的文件 |
" a+ "(读写) | 打开一个文件,在文件尾进行读写 | 建立一个新的文件 |
" rb+ "(读写) | 为了读和写,打开一个二进制文件 | 出错 |
" wb+ "(读写) | 为了读和写,新建一个新的二进制文件 | 建立一个新的文件 |
" ab+ "() | 打开一个新的文件,在文件尾进行读和写 | 建立一个新的文件 |
打开文件时,默认打开的文件在当前文件夹内部,若要打开外部的文件,写清楚相对路径。
以只读的方式打开一个不存在的文件:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL) {
printf("%s", strerror(errno));
return 0;
}
fclose(pf);
pf = NULL;
return 0;
}
运行结果:
以只读的方式打开一个外部文件:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pf = fopen("C:\\Users\\DELL\\Desktop\\新建文件夹\\data.txt", "r");
if (pf == NULL) {
printf("%s", strerror(errno));
return 0;
}
fclose(pf);
pf = NULL;
return 0;
}
运行结果:
文件的顺序读写
功能 | 函数名 | 适用于 |
字符输入函数 | fgetc | 所有输入流 |
字符输出函数 | fputc | 所有输出流 |
文本行输入函数 | fgets | 所有输入流 |
文本行输出函数 | fputs | 所有输出流 |
格式化输入函数 | fscanf | 所有输入流 |
格式化输出函数 | fprintf | 所有输出流 |
二进制输入 | fread | 文件 |
二进制输出 | fwrite | 文件 |
int fgetc(FILE* stream);
// 从stream流中读取一个字符,返回这个字符的ASCII码值;如果读取失败,返回EOF
int fputc(int c, FILE* stream);
// 将c转换为字符,写在stream中
// 返回这个字符的ASCII码值;如果读取失败,返回EOF
char* fgets(char* string, int n, FILE* stream);
// 返回一个字符串,null表示错误
// 从stream中最多读取n个字符到string中
int fputs(const char* string, FILE* stream);
// 把字符串输出到stream文件中
// 如果输出成功,则返回一个非负数;若失败,返回NULL
int fscanf(FILE* stream, const char* format[, argument]...);
// 从指定的流(stream)中读取格式化的数据
// 每个函数都返回成功转换和分配的字段的数量,错误EOF
int fprintf(FILE* stream, const char* format[, argument]...);
// fprintf返回写入的字节数
// 把数据按照格式化的方式输出到指定文件流中
size_t fread(void* buffer, size_t size, size_t count, FILE* stream);
// 从stream流中读取count个数据,每个数据所占内存为size,将他们存在buffer数组中
// fread返回实际读取的完整条目的数量
size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);
// 把buffer中的count个数据,每个大小size,写在stream流中
// 返回值是实际写入的个数
scanf/ fcanf/ sscanf :
scanf:从标准输入流上进行格式化的输入函数
fscanf:从指定的文件流上读取格式化的数据
sscanf:从一个字符串中提取格式化的数据
printf/ fprintf/ sprintf
printf:向标准输出流上进行格式化的输出函数
fprintf:把数据流按照格式化的的方式输出到指定文件流
sprintf:把一个格式化的数据转换成字符串
#include <stdio.h>
typedef struct Stu
{
char name[20];
int age;
float score;
}Stu;
int main()
{
Stu s = { "张三", 20, 92.5 };
Stu tmp = { 0 };
char buffer[40];
sprintf(buffer, "%s %d %f\n", s.name, s.age, s.score);
printf(buffer);
sscanf(buffer, "%s %d %f", tmp.name, &(tmp.age), &(tmp.score));
printf("%s %d %f\n", tmp.name, tmp.age, tmp.score);
return 0;
}
运行结果:
文件的随机读写
fseek:根据文件指针的位置和偏移量来定位文件指针
ftell:返回文件指针相对于起始位置的偏移量
rewind:让文件指针回到文件的起始位置
int fseek(FILE* stream, long offset, int origin); // stream 文件流,offset 偏移量,origin 起始位置 // stream流中,从origin位置偏移offset位置的文件指针 // 偏移成功,返回0;否则返回非0值 // origin :SEEK_CUR(从当前位置指针开始) // :SEEK_END(从文件指针的结尾开始) // :SEEK_SET(从文件指针的开头开始) long ftell(FILE* stream); // 返回文件指针相对于起始位置的偏移量, void rewind(FILE* stream); // 让文件指针返回文件的开头
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
// data.txt:Hello World!abcdefghigklmn
FILE* pf = fopen("data.txt", "r");
if (pf == NULL) {
printf("%s\n", strerror(errno));
}
int ch = fgetc(pf);
printf("%c\n", ch); // "H"
ch = fgetc(pf);
printf("%c\n", ch); // "e"
fseek(pf, 3, SEEK_CUR); // 从当前位置偏移3,
ch = fgetc(pf);
printf("%c\n", ch); // " "
fseek(pf, -3, SEEK_END);// 从最后位置偏移3,
ch = fgetc(pf);
printf("%c\n", ch); // "l"
fseek(pf, 3, SEEK_SET);
ch = fgetc(pf);
printf("%c\n", ch); // "l"
long size = ftell(pf);
printf("%ld\n", size); // 4
rewind(pf);
ch = fgetc(pf);
printf("%c\n", ch); // H
size = ftell(pf);
printf("%ld", size); // 1
fclose(pf);
pf = NULL;
return 0;
}
运行结果:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
int i;
FILE* pf = fopen("data.txt", "w");
if (pf == NULL) {
printf("%s\n", strerror(errno));
return 0;
}
for (i = 'a'; i <= 'z'; i++) {
fputc(i, pf);
}
// pf : abcdefghigklmnopqrstuvwxyz
pf = NULL;
return 0;
}
运行结果:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
int i;
long sz;
FILE* pf = fopen("data.txt", "w");
if (pf == NULL) {
printf("%s\n", strerror(errno));
return 0;
}
for (i = 'a'; i <= 'z'; i++) {
fputc(i, pf);
}
// pf : abcdefghigklmnopqrstuvwxyz
fseek(pf, 3, SEEK_SET);
fputc('#', pf); // pf : abc#efghigklmnopqrstuvwxyz
sz = ftell(pf);
printf("%ld\n", sz); // 4
fseek(pf, 3, SEEK_CUR);
fputc('#', pf);// pf : abc#efg#igklmnopqrstuvwxyz
sz = ftell(pf);
printf("%ld\n", sz); // 8
fseek(pf, -25, SEEK_END);
fputc('#', pf);// pf : a#c#efg#igklmnopqrstuvwxyz
sz = ftell(pf);// 2
printf("%ld\n", sz);
rewind(pf);
fputc('#', pf);// pf : ##c#efg#igklmnopqrstuvwxyz
sz = ftell(pf);
printf("%ld\n", sz);// 1
rewind(pf);
sz = ftell(pf);
printf("%ld\n", sz);// 0
fclose(pf);
pf = NULL;
return 0;
}
运行结果:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL) {
printf("%s\n", strerror(errno));
return 0;
}
char s[100];
fgets(s, 20, pf);
printf("%s\n", s);
fclose(pf);
pf = NULL;
return 0;
}
运行结果:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pf = fopen("data.txt", "w");
if (pf == NULL) {
printf("%s\n", strerror(errno));
return 0;
}
char s[] = "helllo world! ni hao";
fputs(s, pf);
fputs("\nhehe\n", pf);
fclose(pf);
pf = NULL;
return 0;
}
运行结果:
#include <stdio.h>
#include <string.h>
#include <errno.h>
typedef struct Stu
{
char name[20];
int age;
double score;
}Stu;
int main()
{
FILE* pf = fopen("data.txt", "w");
Stu s = { "zhangsan", 20, 92.5 };
if (pf == NULL) {
printf("%s\n", strerror(errno));
return 0;
}
fprintf(pf, "%s %d %lf", s.name, s.age, s.score);
fclose(pf);
pf = NULL;
return 0;
}
运行结果:
#include <stdio.h>
#include <string.h>
#include <errno.h>
typedef struct Stu
{
char name[20];
int age;
double score;
}Stu;
int main()
{
FILE* pf = fopen("data.txt", "r");
Stu s = { 0 };
if (pf == NULL) {
printf("%s\n", strerror(errno));
return 0;
}
fscanf(pf, "%s %d %lf", s.name, &(s.age), &(s.score));
printf("%s %d %lf\n", s.name, s.age, s.score);
fclose(pf);
pf = NULL;
return 0;
}
#include <stdio.h>
#include <string.h>
#include <errno.h>
typedef struct Stu
{
char name[20];
int age;
double score;
}Stu;
int main()
{
Stu s[2] = { {"zhangsan", 20, 95.5}, {"lisi", 25, 55.5} };
FILE* pf = fopen("data.txt", "wb");
if (pf == NULL) {
printf("%s\n", strerror(errno));
return 0;
}
fwrite(s, sizeof(Stu), 2, pf);
fclose(pf);
pf = NULL;
return 0;
}
运行结果:(以二进制的形式存储)
#include <stdio.h>
#include <string.h>
#include <errno.h>
typedef struct Stu
{
char name[20];
int age;
double score;
}Stu;
int main()
{
Stu s[2] = { 0 };
FILE* pf = fopen("data.txt", "rb");
if (pf == NULL) {
printf("%s\n", strerror(errno));
return 0;
}
fread(s, sizeof(Stu), 2, pf);
printf("%s %d %lf\n", s[0].name, s[0].age, s[0].score);
printf("%s %d %lf\n", s[1].name, s[1].age, s[1].score);
fclose(pf);
pf = NULL;
return 0;
}
运行结果:
将一个文件复制到另一文件
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pr = fopen("data.txt", "r");
if (pr == NULL) {
printf("open for reading :: %s\n", strerror(errno));
return 0;
}
FILE* pw = fopen("exercise.txt", "w");
if (pw == NULL) {
printf("open for wrinting :: %s\n", strerror(errno));
fclose(pr);
pr = NULL;
return 0;
}
int ch;
while ((ch = fgetc(pr)) != EOF) fputc(ch, pw);
fclose(pr);
pr = NULL;
fclose(pw);
pw = NULL;
return 0;
}
运行结果:
文本文件和二进制文件
文本文件:以ASCII字符的形式存储的文件就是文本文件
二进制文件:数据在内存中以二进制的形式存储,不加转换地输出到外存中
文件读取结束的判定
feof:当文件读取结束时,判断读取失败结束还是遇到文件尾结束。
文本文件:判断文件读取是否结束,判断返回值是否为EOF(fgetc),NULL(fgets)
二进制文件:判断文件是否读取结束,判断返回值是否小于实际要读的个数。(可以将二进制文件拖进编译器,选二进制打开方式可以打卡)
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE* pr = fopen("data.txt", "r");
if (!pr) {
perror("File opening failed");
return EXIT_FAILURE;
}
int c;
while ((c = fgetc(pr)) != EOF) {
putchar(c);
}
if (ferror(pr)) {
puts("I/O error when reading");
} else if (feof(pr)) {
puts("End of file reached successfully");
}
fclose(pr);
pr = NULL;
return 0;
}
int ferror(FILE* stream);
// 如果没有错误,返回0;有错误,返回非0值
int feof(FILE* stream);
// feof函数在第一次读取操作之后返回一个非零值,该操作试图读取文件的末尾。如果当前位置不是文件的结束位置,则返回0。没有错误返回。
#include <stdio.h>
#include <string.h>
#include <errno.h>
enum
{
SIZE = 5
};
int main()
{
double a[SIZE] = { 1.0, 2.0, 3.0, 4.0, 5.0 };
FILE* pw = fopen("test.bin", "wb");
if (pw == NULL) {
printf("%s\n", strerror(errno));
return 0;
}
fwrite(a, sizeof(a[0]), SIZE, pw);
fclose(pw);
double b[SIZE];
pw = fopen("test.bin", "rb");
unsigned int sz = fread(b, sizeof(b[0]), SIZE, pw);
// fread 返回读取的个数,如果个数相等,则读取成功;
if (sz == SIZE) {
puts("Array read successfully, contents: ");
for (int i = 0; i < sz; i++) {
printf("%lf ", b[i]);
}
printf("\n");
} else {
// 当个数不相等时,可能是读取到中间结束,可能是读取错误
if (feof(pw)) {
printf("Error reading test.bin: unexpected end of file\n");
} else if (ferror(pw)) {
perror("Error reading test.bin");
}
}
fclose(pw);
pw = NULL;
return 0;
}
运行结果:
文件缓冲区
ANSIC 采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。
从内存向磁盘输出数据会先送到内存的缓冲区,装慢缓冲区后再一起送到磁盘上。
从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区,充满缓冲区,然后再从缓冲区逐个将数据送到程序数据区(程序变量)。
缓冲区的大小由编译器决定。