目录
前言
在平时的的程序运行的时候,一旦程序结束,这个程序相关的内容就消失了,我们也就无法长久的保留相关的信息了,而只要我们掌握了文件的相关操作,我们就可以长久的保留信息了。下面就来讲一讲c语言中关于文件的相关操作。
一、什么是流
要学习文件的操作,首先得了解什么是流。
流可以理解为流淌着字符,数字等内容的河流。我们平时的键盘的输入和屏幕的输入都是对流的输入和读取的操作。 像我们平时在黑窗口输入的就是标准输入流,输出的就是标准输出流。这两个还有标准错误流都是在程序一开始时就打开的。
二、文件的打开和关闭
1.文件的打开
要使用文件,我们就得打开文件,把我们的要存储的信息放到文件里。这里就要用到 fopen 函数了。
下面是对函数的的讲解: 函数原型: FILE * fopen ( const char * filename, const char * mode );1.返回类型:
返回的是 FILE 类型的指针。
FILE 类型其实就是一个结构体,是包含在头文件 stdio.h 里面,在程序中是已经声明好的,我们包含了头文件后就不用自己在声明了。
2.第一参数:
其中的 filename 的就是要打开的文件的名字,也即是文件的地址,是一个字符串。
文件名包括:文件路径 + 文件名主干 + 文件后缀
地址有 绝对路径 与 相对路径:
绝对路径 其实也就是基于当前这台电脑的,也就是地址的全称,什么情况下都可以用。
相对路径 就是基于当前文件夹下的地址,也就是有些被省略了。
两个类型的路径举例:
绝对:"C:\\Users\\zl\\Desktop\\data.txt" \ 要由另一个 \ 来转义,表示的其就只是一个 \
相对:"data.txt" 或 "..\\Debug\\data.txt"
例子中一个 . 表示当前文件夹下(文件后缀前面的点不算),两个 . 表示当前文件夹的上一个文件夹下,而上一级文件夹的上一级就是 ..\\..3.第二参数
其中的 mode 就是要操作文件的模式,也是一个字符串。
下面是部分的操作模式:
小结:
1.操作模式可以分为三类:读 写 追加 (追加其实也是写的一种)
2.在三类的基础上还有别的,如 加上 b ,就是操作二进制文件,加上 + ,就是既要读也要写,也可以有 b 也有 + ,就是对二进制文件进行读写操作的意思(可以参考上表)
3.这三类中,如果操作的文件不存在,会有不同的反应,总的来说,就是写文件不存在,就创建新文件,读文件不存在就报错,而如果有 + 号的话,则要依据三个类型来判断,w 和 a 就是创建新文件,r 就是报错,如 "r+", 就是报错。
4.还有一点就是,用 "w" 模式时,如果文件存在,文件内部的信息会清空
在开启文件后,文件指针会指向一个位置,然后我们的读取和写入都会在当前位置进行,"w" 和 "r" 模式 fopen() 返回的指针都是指向文件的开头位置的。"a" 模式返回的指针就是已有数据的下一个位置。
2.文件的关闭
在程序中使用完一个文件,我们要关闭当前的文件,避免后续不小心对文件进行修改。这就要用到 fclose() 函数了
函数原型: int fclose ( FILE * stream );fclose() 函数的使用就很简单了,把要关闭的文件的指针放在括号中,关闭成功就会返回 0,否则就返回 EOF
三、文件的标准化输入输出
1.概念
标准化输入输出的函数分别是 fscanf() 和 fprintf() 。这两个函数的使用很简单,相较我们平时使用的 scanf() 和 printf() ,就是最前面多了一个参数,这个参数填的就是流,如果我们分别填入 stdin(标准输入流) 和 stdout(标准输出流),那么这两个函数就和我们平时使用的 scanf 和 printf 一样了,都是在控制台里得到和输出数据。
2.例子
下面我们来实践一下:
这是一段代码:
#include<stdio.h>
int main()
{
FILE* pf = fopen("data.text", "w+"); //文件名处在未写明时默认是在当前文件夹下
if (pf == NULL)
{
return 1;
}
fprintf(pf, "%d %c", 1, 'a');
fclose(pf); //文件关闭一般不会有问题
return 0;
}
在运行了这段代码后,该文件夹下的 data.text 文件
没有问题,数据已经被我们输入到文件里面了。
再看看这个代码:
为什么读取的信息有问题呢?相信大家注意到了被我注释掉的 rewind(pf) 了,来看看没有注释掉的情况:
结果正确,这因为在写入和读取的时候,文件指针是在移动的(可以想想我们平时输入和删除时的小横线),而 rewind() 函数可以把文件指针回到文件的开头。这样读取到的数据就没有问题了。
四、fgets() 和 fputs()
利用 fgets() 和 fputs() 就可以实现对字符串的输入输出了。
1.fputs() 字符串输出
fputs() 的用法和平时使用的 puts() 几乎是一样的,不同的点在于,多了一个参数,也就是流。利用这个函数就可以把字符串输出到想要的流里去。
函数原型: int fputs ( const char * str, FILE * stream );
2.fgets() 字符串输入
fgets() 与 gets() 还是有区别的 fgets() 可以限定读取字符的个数,还很有可能把最后的 '\n' 也一起读进来,还有就是多了一个参数,也就是流。别的和 gets() 没什么区别。
函数原型:
char * fgets ( char * str, int num, FILE * stream );从流中读取字符,并将它们作为 C 字符串存储到 str 中,直到读取 (num-1) 个字符或到达换行符或文件末尾,以先发生者为准。(这里的 num-1 可以理解为要读取num个字符,最后一个位置放 '\0',所以前面只有 num-1 个位置能用来放要读取的信息)
换行符使 fgets 停止读取,但它被函数视为有效字符,并包含在复制到 str 的字符串中。(gets() 函数是读到 '\n'之前, '\n' 是不会被读入的)
五、二进制读取与写入
1.fwrite()
fwrite() 的作用就是把一个数据的二进制信息写到文件里面去。
2.fread()
fread() 的作用就是把文件的二进制信息翻译之后读出来。
这里就直接上代码演示,方便理解:
struct S
{
int a;
float s;
char str[10];
};
int main()
{
struct S s = { 99, 3.14f, "hehe" };
FILE* pf = fopen("data.txt", "wb");//"wb"二进制模式写文件
if (pf == NULL)
{
perror("fopen");
return 1;
}
//写文件
fwrite(&s, sizeof(struct S), 1, pf);//第一参数是要写入的信息的所在地址, 第二参数是要写信息的大小, 第三参数是读取信息块的个数, 第四参数是要写到的位置
//即把 &s 处的大小为 sizeof(struct S) 的 1 个数据,写到 pf 处
fclose(pf);
pf = NULL;
struct S x = { 0 };
pf = fopen("data.txt", "rb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
fread(&x, sizeof(struct S), 1, pf);//返回值是实际读取到的数据块的个数.如果到文件结尾就读取到0个数据块
//即把 pf 处的大小为 sizeof(struct S) 的 1 个数据,读取到 &x 处
printf("%d %f %s\n", x.a, x.s, x.str);//打印到黑窗口看看
fclose(pf);
pf = NULL;
return 0;
}
fread() 的返回值时成功读取到的模块的个数。
六、文件指针操作函数
1.rewind()
rewind() 函数在前面有提到(三、2. 例子),这里就不再过多赘述。
2.fseek()
fseek() 是可以帮助我们把指针放到我们想要的位置上的一个函数、
原型: int fseek ( FILE * stream, long int offset, int origin )这些参数中,第一个放的是要调整的文件指针,第二个是要移动的距离(正数后移,负数前移),第三个就是要从哪里开始移动(这个是规定好的)。
这是第三参数可以填的三个值:
实例:
下面是使用fseek()的一段代码:
#include<stdio.h>
int main()
{
//写入一些数据
FILE* pf = fopen("test.txt", "w");
fputs("abcdefghi", pf);
fclose(pf);
pf = NULL;
//读数据
pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//fseek的作用就在于调整文件指针的位置(这里就用SEEK_SET为例)
fseek(pf, 5, SEEK_SET);
//指针位于开头,偏移量为5,指针指向的就是f(如果在文件头还往前移,就会指向第一个,如果往后移的量超过了已经有了的字符,则任然往后但输出就会是空白方框了)
int ch = fgetc(pf);//在读取了字符后,ch拿到f,指针就会后移,指向g
printf("%c\n", ch);//结果为f
ch = fgetc(pf);//在读取了字符后,ch拿到g,指针后移,指向h
printf("%c\n", ch);//结果为g
fclose(pf);
pf = NULL;
return 0;
}
3.ftell()
ftell()就很简单了,只需要在其中放入要查的那个文件指针,它可以告诉我们文件指针当前的位置与文件起始位置的偏移量。
七、文件末尾的判断
要判断文件指针是否到了文件的末尾有多种方法。
1. 观察各种读取函数的返回值
大多读取函数在读取时遇到结尾有着不同返回值,观察返回值可以判断读取是否到了文件的末尾。
例如:
fgets() 在读取到文件末尾的时候会返回NULL。
fread() 在读取的时候,如果返回值小于我们给的个数,就是读取到文件末尾了。
还有很多,可以去官网查查,这里就不多讲了。
2. 使用 feof() 函数
这个函数可以帮助我们知道当前的文件指针是否到达文件末尾。如果放入的文件指针到达了文件末尾,就会返回非零值,否则返回零。
结语
以上就是关于文件操作的一些内容,希望对大家有所帮助,最后也别忘了点赞收藏加关注😁。