目录
文件指针就指向了文件信息区,文件信息区是一个结构体,里面存储的有文件信息区的数据。
我们举一个例子:
当我们打开一个test.txt的文件时:
当我们打开文件test.txt时,我们会创建一个文件信息区,同时产生一个文件指针pf,该文件指针pf指向文件信息区,通过文件信息区的内容就能够访问该文件,也就是说,通过文件指针可以访问对应的文件。
该函数的作用是打开文件,第一个参数表示打开的文件的文件名,第二个参数表示打开文件的方式。
第二个参数可以是以上的这些:“r"表示以读的形式,当我们以读的形式打开文件时,文件需要存在,否则会打开失败。
"w"表示以写的形式打开文件,文件不需要存在。
其他的四个我们不需要了解。
当成功打开时,我们返回指向该文件的文件信息区的文件指针,当打开失败的时候,我们返回空指针。
实验:
- #include
- #include
- #include
- int main()
- {
- FILE*pf = fopen("test.txt", "r");
- if (pf == NULL)
- {
- printf("%s\n", strerror(errno));
- return 1;
- }
- return 0;
- }
让我们对代码进行逐语句分析:
这串代码的意思是:首先,以读的形式打开文件test.txt,并返回指向该文件信息区的文件指针,但是打开文件可能会失败,所以我们要进行检测:我们判断pf是否为空指针,假如pf为空指针,证明我们打开文件失败,errno表示错误码,strerror表示把错误码转换成对应的错误信息,这里的意思是假如打开文件失败,我们把对应的错误码转换为错误信息并打印出来,当打开失败时,我们直接return 1退出文件。
fclose函数的意思是:关闭文件。
每次打开或使用文件后,当我们要退出时,我们都需要关闭该文件,在这里就相当于资源的释放。
- int main()
- {
- FILE*pf = fopen("test.txt", "r");
- if (pf == NULL)
- {
- printf("%s\n", strerror(errno));
- return 1;
- }
- fclose(pf);
- pf = NULL;
- return 0;
- }
每次fclose之后都需要把pf指针置为空指针。
- #define _CRT_SECURE_NO_WARNINGS 1
- #include
- #include
- #include
- int main()
- {
- FILE*pf = fopen("test.txt", "r");
- if (pf == NULL)
- {
- printf("%s\n", strerror(errno));
- return 1;
- }
- fclose(pf);
- pf = NULL;
- return 0;
- }
我们进行运行:
表示没有文件或目录。
但是假如我们在对应代码的目录下创建一个文件,这时候我们再调用代码:
这时候就不会报错了。
我们对输入流输出流的概念进行解析:
我们写的代码首先是加载到内存中的,假如我们想把我们写的代码或者数据加载到显示器或者硬盘中去,我们可以把代码先加载到输出流,再由输出流进行加载,这样可以简化我们程序员的工作和学习容量。
输入和输出:输入和输出是针对内存的,输出表示从内存中输出到硬件中,输入表示从硬件输入到内存中去。
表示输出字符到流中(写字符到流中)
例如:
- int main()
- {
- FILE*pf = fopen("test", "w");
- if (pf == NULL)
- {
- printf("%s\n", strerror(errno));
- return 1;
- }
- fputc('a', pf);
- fclose(pf);
- pf = NULL;
- return 0;
- }
我们以写的形式打开文件test,假如没有,我们会自定创建test
然后我们往文件中输入'a'。
我们打开test文件,发现test的内容是a,证明我们输出成功。
我们再举一个例子:
- int main()
- {
- FILE*pf = fopen("test", "w");
- if (pf == NULL)
- {
- printf("%s\n", strerror(errno));
- return 1;
- }
- /*fputc('a', pf);*/
- for (int i = 'a'; i <='z' ; i++)
- {
- fputc(i, pf);
- }
- fclose(pf);
- pf = NULL;
- return 0;
- }
我们输出a到z26个字符到文件test中
我们的文件test中的内容就变成了a到z
我们第一次执行写入的是‘a',显示的也是‘a’,第二次执行写入的是’a‘到’z',写入的也是‘a’到‘z’
证明我们每一次执行写入并且不在同一个程序中时,我们会对文件中的内容进行刷新重置。
这里的意思是从文件中取字符。
例如:
- int main()
- {
- FILE*pf = fopen("test", "r");
- if (pf == NULL)
- {
- printf("%s\n", strerror(errno));
- return 1;
- }
- int ch = fgetc(pf);
- printf("%c\n", ch);
- ch = fgetc(pf);
- printf("%c\n", ch);
- fclose(pf);
- pf = NULL;
- return 0;
- }
我们进行运行。
注意:当我们要进行读的操作的时候,我们需要修改foepn的参数'w'修改成为'r'。
并且我们连续读时,我们会进行顺序读。
接下来,我们来看fget的返回值:
当调用成功的时候,返回元素本身,当调用失败或访问到文件末尾时,我们返回eof。
所以完美的写法是这样:
- int main()
- {
- FILE*pf = fopen("test", "r");
- if (pf == NULL)
- {
- printf("%s\n", strerror(errno));
- return 1;
- }
-
- int ch = 0;
- while ((ch = fgetc(pf)) != EOF)
- {
- printf("%c ", ch);
- }
- fclose(pf);
- pf = NULL;
- return 0;
- }
写一行数据到流中(文件中)
例如:
- int main()
- {
- FILE*pf = fopen("test", "w");
- if (pf == NULL)
- {
- printf("%s\n", strerror(errno));
- return 1;
- }
- fputs("hello world", pf);
- fclose(pf);
- pf = NULL;
- return 0;
- }
我们打开文件查看
我们成功把字符串hello world写入到了文件里面。
当我们想要追加时,我们可以这样写:
我们进行运行:
这里就出现了两个hello world
从文件中读一行数据
例如:
- int main()
- {
- FILE*pf = fopen("test", "r");
- if (pf == NULL)
- {
- printf("%s\n", strerror(errno));
- return 1;
- }
- char arr[20] = { 0 };
- fgets(arr, 12, pf);
- for (int i = 0; i < 12; i++)
- {
- printf("%c ", arr[i]);
- }
- fclose(pf);
- pf = NULL;
- return 0;
- }
表示从文件pf中读取12个字符的数据,读取到数组arr中
然后我们循环打印数组arr。
注意:我们读取的12个字符中有一个是'\0',所以我们读取的有效字符只有11个。
接下来,我们查看一下fgets的返回值:
调用成功时,返回字符串,调用失败时,返回空指针。
写格式化数据到字符串中。
假如我们要打印结构体中的数据,我们该如何打印呢?
- struct S
- {
- char arr[10];
- int age;
- float score;
- };
- int main()
- {
- struct S s = { "zhangsan", 25, 50.5f };
- FILE*pf = fopen("test", "w");
- if (pf == NULL)
- {
- perror("fopen");
- return 1;
- }
- printf("%s %d %f", s.arr, s.age, s.score);
- fclose(pf);
- pf = NULL;
- return 0;
- }
假如我们要把这些结构体的数据写入到文件中,我们可以使用fprintf
- struct S
- {
- char arr[10];
- int age;
- float score;
- };
- int main()
- {
- struct S s = { "zhangsan", 25, 50.5f };
- FILE*pf = fopen("test", "w");
- if (pf == NULL)
- {
- perror("fopen");
- return 1;
- }
- fprintf(pf,"%s %d %f", s.arr, s.age, s.score);
- fclose(pf);
- pf = NULL;
- return 0;
- }
我们打开文件进行查看
从文件中读取格式化数据。
例如:我们现在文件中的内容是这样:
我们想要把文件中的数据读取到内存中去。
- struct S
- {
- char arr[10];
- int age;
- float score;
- };
- int main()
- {
- struct S s = { 0 };
- FILE*pf = fopen("test", "r");
- if (pf == NULL)
- {
- perror("fopen");
- return 1;
- }
- fscanf(pf, "%s %d %f", s.arr, &s.age, &s.score);
- printf("%s %d %f", s.arr, s.age, s.score);
- fclose(pf);
- pf = NULL;
- return 0;
- }
我们创建一个空结构体,然后从从文件指针pf中,读取三个不同类型的数据到结构体中,然后我们打印结构体,打印的结果如图所示:
证明我们读取成功。
我们的写(输出)和读(输入)都是针对内存的,从内存中输出文件到硬盘中,从硬盘中输入文件到内存中。
计算机分为内部设备和外部设备,内存设备就是内存,外部设备有很多,例如屏幕 网络 u盘 硬盘等等,我们从内存写入到不同的外部设备的方法也是不同的,但是为了简化程序员的学习成本,我们设计了流,流是一种媒介,对于不同设备之间的输入和输出,流可以自动调节,我们只需要实现数据的输入和输出即可,不需要考虑不同设备之间的不同方法。
写二进位制数据到文件中。
- struct S
- {
- char arr[10];
- int age;
- float score;
- };
- int main()
- {
- struct S s = { "zhangsan",25,50.5f };
- FILE*pf = fopen("test", "wb");
- if (pf == NULL)
- {
- perror("fopen");
- return 1;
- }
- //fscanf(pf, "%s %d %f", s.arr, &s.age, &s.score);
- //printf("%s %d %f", s.arr, s.age, s.score);
-
- fwrite(&s, sizeof(s), 1, pf);
- fclose(pf);
- pf = NULL;
- return 0;
- }
我们创建一个结构体,结构体中有三个不同类型的数据,我们以二进位制写的形式打开文件pf,然后我们以二进位制的形式,从s的地址中,取sizeof(s)个字节的数据,取1个,写入到文件指针指向的文件中去。
我们进行运行:
看不懂的原因是普通类型转化为二进位制,zhangsan能够看懂的原因是我们以文本的形式放进去和二进位制的形式放进文件中显示的结果是相同的。
从文件中读取二进位制数据:
- struct S
- {
- char arr[10];
- int age;
- float score;
- };
- int main()
- {
- struct S s = {0};
- FILE*pf = fopen("test", "wr");
- if (pf == NULL)
- {
- perror("fopen");
- return 1;
- }
- //fscanf(pf, "%s %d %f", s.arr, &s.age, &s.score);
- //printf("%s %d %f", s.arr, s.age, s.score);
- /*fwrite(&s, sizeof(s), 1, pf);*/
- fread(&s, sizeof(s), 1, pf);
- printf("%s %d %f", s.arr, s.age, s.score);
- fclose(pf);
- pf = NULL;
- return 0;
- }
我们进行编译:
写格式化数据到字符串中。
例如:
- struct S
- {
- char arr[10];
- int age;
- float score;
- };
- int main()
- {
- struct S s = { "zhangsan", 20, 50.5f };
- char buf[100] = { 0 };
- sprintf(buf, "%s %d %f", s.arr, s.age, s.score);
- printf("%s\n", buf);
- return 0;
- }
这串代码的意思是:我们创建一个结构体对象s,然后创建一个数字,我们使用sprintf,把结构体s的对象中的数据格式化输出到buf中,本质上是把格式化数据转换为字符串。
我们进行打印:
从字符串中读取格式化数据。
例如:
- struct S
- {
- char arr[10];
- int age;
- float score;
- };
- int main()
- {
- struct S s = { "zhangsan", 20, 50.5f };
- char buf[100] = { 0 };
- sprintf(buf, "%s %d %f", s.arr, s.age, s.score);
- /*printf("%s\n", buf);*/
- struct S tmp = { 0 };
- sscanf(buf, "%s %d %f", tmp.arr, &tmp.age, &tmp.score);
- printf("%s %d %f", tmp.arr, tmp.age, tmp.score);
- return 0;
- }
这串代码的意思是:我们首先创建一个结构体,创建一个空数组,我们使用sprintf函数把格式化数据写入到字符串buf中,然后我们再使用scanf,把字符串中的数据获取格式化数据到结构体tmp中。
总结:sprintf表示把格式化数据写入到字符串
sscanf表示从字符串中获取格式化数据
对指针进行重定向
三个参数分别表示
第一个参数表示文件指针,第二个参数表示偏移量(相对于第三个参数),第三个参数表示指针的三个位置,分别是文件的开头,指针的当前位置和文件的末尾。
得到相对于起始位置的偏移量。