主页:114514的代码大冒险
qq:2188956112(欢迎小伙伴呀hi✿(。◕ᴗ◕。)✿ )
Gitee:庄嘉豪 (zhuang-jiahaoxxx) - Gitee.com
目录
前言
一、文件是什么
二、文件的打开和关闭
1.文件指针
2.文件的打开和关闭
三,文件的顺序读写
三,文件的随机读写(此部分函数以增强对文件内容输入的自由度)
1 fseek
2 ftell
3 rewind
四,文本文件和二进制文件
五,文件读取结束的判定
六,文件缓冲区
总结
前言
一、文件是什么
开启后:
二、文件的打开和关闭
1.文件指针
代码如下:
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
FILE* pf;//文件指针变量
这里的文件信息区是存在于内存中的,它与外存中的那个路径对应的文件是有紧密联系的,相当于将外存中的那个文件部分重要属性(具体参照于不同的编译器,不同的编译器定义不同的文件类型声明)存储于文件信息区
2.文件的打开和关闭
//打开文件
FILE * fopen ( const char * filename, const char * mode );
//关闭文件
int fclose ( FILE * stream );
文件使用方式
|
含义
|
如果指定文件不存在
|
---|---|---|
“r”
(只读)
|
为了输入数据,打开一个已经存在的文本文件
|
出错
|
“w”
(只写)
|
为了输出数据,打开一个文本文件
|
建立一个新的文件
|
“a”
(追加)
|
向文本文件尾添加数据
|
建立一个新的文件
|
“rb”
(只读)
|
为了输入数据,打开一个二进制文件
| 出错 |
“wb”
(只写)
|
为了输出数据,打开一个二进制文件
|
建立一个新的文件
|
“ab”
(追加)
|
向一个二进制文件尾添加数据
|
出错
|
“r+”
(读写)
|
为了读和写,打开一个文本文件
|
出错
|
“w+”
(读写)
|
为了读和写,建议一个新的文件
|
建立一个新的文件
|
“a+”
(读写)
|
打开一个文件,在文件尾进行读写
|
建立一个新的文件
|
“rb+”
(读写)
|
为了读和写打开一个二进制文件
| 出错 |
“wb+”
(读写)
|
为了读和写,新建一个新的二进制文件
| 建立一个新的文件 |
“ab+”
(读写)
|
打开一个二进制文件,在文件尾进行读和写
| 建立一个新的文件 |
这里是不需要一次性记住全部的,随用随查,慢慢就会了,我们这里仅介绍用到的比较多的几个
r:
int main()
{
FILE* pf = fopen("D:\\vs\\test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
else
{
printf("打开文件成功\n");
}
fclose(pf);
pf = NULL;
return 0;
}
这里说一下,未知的东西有点多,我们逐个解释,首先是fopen(我想啊,这里的pf指针就不需多说了吧,有疑问可再次阅读上文对应区域),欧克欧克,我们来讲解一下
速览定义,返回值为文件指针类型,第一个参数为要打开的文件的路径,例如:
此为绝对路径
也可以这样:
此为相对路径,不过这个默认的该程序所存在的文件夹
比如这样:
在代码中使用双斜杠的原因是:斜杆+部分字符会被识别为转义字符如:\n换行符,\t水平制表符
而双斜杠在这里会抵消这种效果,且在双斜杠可以被转义成单斜杠
第二个参数为:文件打开方式,也就是上文的那个表格,这里细说一下“r”的作用,"r"这里是读取打开此文件,然后接下来程序可以进行相应的操作,使用函数等等调用文件中的内容,fopen打开成功返回在内存中文件缓冲区(稍后详解)开辟的对应打开的文件的那块区域的指针,等会详解文件缓冲区会清晰一些。
fclose是将打开到文件关闭的函数,至于为什么要关闭,等会详解。
然后就是文件使用方式“w”了,这个与“r”不同,它会对要打开的文件进行初始化,也就是内容清零,然后进行内容的写入(通过程序向文件夹输入内容)。如果文件夹不存在,该函数会在此路径下,命名一个与输入文件名一致的文件 然后在进行写入操作
然后就是“a”了,它与前两个一致的是,都是对文本文件进行写入,这个与“w”的清零重新写入不同,它是在文件原有内容的基础上进行添加,而且是在内容末尾添加
三,文件的顺序读写
’这些函数除了后两个,其他的都是对文本内容进行操作,而末两个则是对而进制内容进行操作
为了测试函数我们先对文件进行内容的写入
写入单个字符的函数是
参数character需输入你要写入的那个字符
这个是默认在文件的第一个位置进行字符的写入,也就是如图这个位置,
在第一次使用后,下一次该函数的写入位置就会发生变化,比如以下这样
在第一次使用后,光标(输入位置)会移动到如下图红色线条所示的位置
FILE* pf = fopen("test.txt", "w");
if (NULL == pf)
{
perror("fopen");
return 1;
}
//写文件
fputc('a', pf);
fputc('b', pf);
fputc('c', pf);
fputc('d', pf);
该函数参数是文件指针,
功能是在文件内容中读取一个字符,默认位置为内容的第一个字符
int ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
输出为
注:这里的文件输入函数如“fputc”是无法在文件使用为“r”的情况下使用的,因为是“r”是只读操作,如下图:
其他文件操作函数同理,使用前要注意文件的使用方式
fgets读取一行 fputs输入一行,大致功能与上文区别不大,这里就不多赘述了
接下来是fscanf函数,
对比一下,只有函数参数的区别,fscanf多了一个文件指针,我们在使用的时候加上就是
以下是代码实例:
这里要提前在文件夹中放置内容,但在输入名字时,不要输入汉字,因为这里取名字的格式是
字符串
应当是上图这样一个内容
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = { 0 };
FILE* pf = fopen("test.txt", "r");
if (NULL == pf)
{
perror("fopen");
return 1;
}
//从文件中取出数据
fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score));
//打印在屏幕上判断是否成功取出
printf("%s %d %f\n", s.name, s.age, s.score);
fclose(pf);
pf = NULL;
return 0;
}
下图为输出结果
接下来的fprintf请按照上文的这种方式,自行进行解决(内容真的很相似),
剩下的两个是二进制,所以我们单独讲解,这里我们就先聊点别的
OK,这里是相似的几个函数,我们已经聊过scanf与fscanf ,fprintf与printf ,那么我们这里就主要
讨论sscanf与 sprintf
多了个字符指针参数,用法就很简单了,这个参数可以使“abcdef”(会被传成首元素的地址)这样的字符串,也可以是char *ch 这样的字符指针,都可以,举例:
int arr1[30] = {0};
int arr2[30] = {0};
sscanf("zhangsan wangwu", "%s %s", arr1, arr2);
至于sprintf
先放一段代码
#include <stdio.h>
int main()
{
char a[100] = {0};
sprintf(a, "你好,我是%s", "zhangsan");
printf("%s",a);
return 0;
}
很显然,这里可以看到的是,a被赋值成了sprintf第二个参数的内容(且按格式化%s输出“zhansan”),emmm,大致就是这样的一个功能,剩下的深入使用可以另做寻找,这里不是本文
的关键内容
但是这里还是要谈一下输入流的问题,统共有标准输入流(标准输入(stdin)是指键盘输入,标准输出(stdout)是在电脑屏幕上输出)与文件输入流(今天讲的程序文件操作)
也就是说,适用于所有输出or输入流的函数,参数不仅仅可以是文件指针,还可以是stdout or stdin
三,文件的随机读写(此部分函数以增强对文件内容输入的自由度)
1 fseek
int fseek ( FILE * stream, long int offset, int origin );
stream 属于文件指针类型,然后对应的是文件输入流,
offset指的是使用者想让指针偏移的位置,左移就是负数,右移是整数
origin:(指针位置移动时的参照)
SEEK_SET是文件的起始位置
SEEK_CUR是文件指针的当前位置
SEEK_END是文件的末尾位置
在进行指针的偏移之后,就可以再对文件进行操作,这里举个例子(实际效果还请小伙伴自行演示):
fseek ( pFile , 9 , SEEK_SET );
fputs ( " sam" , pFile );
2 ftell
long int ftell ( FILE * stream );
3 rewind
void rewind ( FILE * stream );
四,文本文件和二进制文件
附赠测试代码:
#include <stdio.h>
int main()
{
int a = 10000;
FILE* pf = fopen("test.txt", "wb");
fwrite(&a, 4, 1, pf);//二进制的形式写到文件中
fclose(pf);
pf = NULL;
return 0;
}
以下图的方式查看文本二进制内容:
图一:
图二:
这里再说一下
fread:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
ptr – 这是指向带有最小尺寸 size*nmemb 字节的内存块的指针。
size – 这是要读取的每个元素的大小,以字节为单位。
nmemb – 这是元素的个数,每个元素的大小为 size 字节。
stream – 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输入流。
- 成功读取的元素总数会以 size_t nmemb对象返回,size_t 对象是一个整型数据类型。如果总数与 nmemb 参数不同,则可能发生了一个错误或者到达了文件末尾。
五,文件读取结束的判定
#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);
}
#include <stdio.h>
enum { SIZE = 5 };
int main(void)
{
double a[SIZE] = {1.,2.,3.,4.,5.};
FILE *fp = fopen("test.bin", "wb"); // 必须用二进制模式
fwrite(a, sizeof *a, SIZE, fp); // 写 double 的数组
fclose(fp);
double b[SIZE];
fp = fopen("test.bin","rb");
size_t ret_code = fread(b, sizeof *b, SIZE, fp); // 读 double 的数组
if(ret_code == SIZE) {
puts("Array read successfully, contents: ");
for(int n = 0; n < SIZE; ++n) printf("%f ", b[n]);
putchar('\n');
} else { // error handling
if (feof(fp))
printf("Error reading test.bin: unexpected end of file\n");
else if (ferror(fp)) {
perror("Error reading test.bin");
}
}
fclose(fp);
}
六,文件缓冲区
这里可以使用fflush函数进行缓冲区的刷新,如果是对输出缓冲区刷新,那么就将缓冲区的内容一次性过渡到硬盘中,如果是对输入缓冲区刷新,那么就将缓冲区的内容过渡到程序数据区
还要说的一点是,fclose函数在关闭文件时,会对输出缓冲区进行刷新,将缓冲区剩余内容过渡到硬盘(外存)中去。所以每次打开文件后,要进行文件的关闭,否则可能会导致读写文件的问题。
总结
文件操作使程序运行更加具有生产意义,且实现了程序数据与外存数据的交互,是程序员必备的技能,希望这次的文件操作详解能够帮助到你
okok,这就是本次的文件操作详解了,如果还有疑问欢迎私信,我随时方便,那么下期再见啦