目录
文件的打开与关闭
fopen
如果我们在自己编译的目录下存在一个文件,我们要进行对此文件进行VS环境下的读写应该如何做呢,例如我们创建一个文件在我们的目录下
我们如果要在VS对其进行操作,这个时候就得用到fopen函数,fopen即为打开一个文件,这个函数返回的类型为FILE类型的一个指针,所谓FILE类型也就是文件类型,意思就是有个指针指向你打开的文件,类型为FILE类型。但是这个指针肯定是指向一个内存的,这个内存里面装了什么呢?其实里面装的就是这个文件的信息,是一个结构体具体如下:
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。 每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息, 使用者不必关心细节。 一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。
如下接受一个打开文件的信息即可
#include <stdio.h>
void main()
{
FILE* pf = fopen("file_open.txt", "w");
}
即有一个文件类型的指针指向这个文件,其中第一个填入的就是要打开的文件,第二个w代表了对文件进行写入,如这个是个txt文件我们就在里面输入名字以及txt,但是如果不是这个目录下的文件呢?我们只需要输入其在电脑的目录就可以了,如我们装在了C盘的program file里面我们输入如下目录代码
C:\program file\file_open.txt
大家觉得这样可以吗?
答案是不可以的,如果不对\进行进一步修饰程序会认为输入的是转义字符,因此我们要在这个基础上再加上一个转义字符标记,说明这个斜杠不是转义字符,如下所示:
C:\\program file\\file_open.txt
如何填入或者获取这个文件的信息呢?我们慢慢一步步来,先讲如何关闭这个文件
fclose
在对一个文件进行读写要打开,相对应的,如果要对一个程序关闭前或者使用完后应该也对应的关闭文件才对。为什么要关闭呢,一种原因是C语言程序运行的时候可打开文件是有上限的所以我们要在接收到文件信息的时候相对应的关闭它。在打开文件时我们有一个指针指向了这个文件信息,在关闭的时候应该顺应的将其置为空指针,养成良好的习惯。如下代码
#include <stdio.h>
void main()
{
FILE* pf = fopen("file_open.txt", "w");
fclose(pf);
pf = NULL;
}
文件的读与写
记得我们最开始打开文件的时候我们后面有跟一个"w"吗,这个代表了我们打开这个文件是进行写入的操作,即我们从编译器对这个文件进行写入,其中后面第二个代码可以填入的有如下
注意,我们只要在写入的时候是可以建立一个新的文件,但如果读取这个文件的时候不存在便会出错,并且在进行数据写入的时候,写入的信息会覆盖住原文件的信息,即如果要在文件尾添加数据得用“a"来追加,一般来说常用的文件读写方式就是前六种,其中第四第五个读写文件是采用二进制读写,二进制是怎么样读写的呢?这里我们下面慢慢说。
单个字符的读写fgetc、fputc
fgetc和fputc函数是输入或者获取一个字符的数据
其中函数定义的参数如下
输入:fgetc和fputc均为读或者写一个字符给'流',可以给文件流或者标准输出/输入流,文件流即读写我们的文件,标准输出/输入流分别对应着屏幕和键盘
返回值:fputc返回每个字符输入的ASCII码值,而fgetc返回的是读到字符的ASCII码值,fputc和fgetc当文件读取或者写入出错的时候程序会返回EOF,不同的是fgetc当文件读取结束的时候也会返回EOF
例子:
如我们要输入三个字符'b' 'i' 't'给我们的文件
我们可以按照下面这样操作,同样是打开我们的file_open.txt文件
#include <stdio.h>
void main()
{
FILE* pf = fopen("file_open.txt", "w");
fputc('b', pf);
fputc('i', pf);
fputc('t', pf);
fclose(pf);
pf = NULL;
}
因为fputc是按照字符输入的,我们一个个输入bit,我们来看看打开我们的file_open.txt文件看看输出的结果如何
可以看到bit三个字符成功输入了
我们刚才知道,这个函数是可以输出到标准输出流的也就是屏幕,那我们试一下把后面的pf改成stdout是否可以输出到屏幕上呢?
将代码改成如下
#include <stdio.h>
void main()
{
FILE* pf = fopen("file_open.txt", "w");
fputc('b', stdout);
fputc('i', stdout);
fputc('t', stdout);
fclose(pf);
pf = NULL;
}
可以看到我们的成功输出了bit到我们的屏幕上
如果我们要从这个文件拿去bit三个字符如何呢?
这个时候就得用到fgetc函数了
我们一样打开此文件,不过第二个代码从"w"变成了"r"因为我们要读取文件
#include <stdio.h>
void main()
{
FILE* pf = fopen("file_open.txt", "r");
int ret = fgetc(pf);
printf("%c", ret);
ret = fgetc(pf);
printf("%c", ret);
ret=fgetc(pf);
printf("%c", ret);
fclose(pf);
pf = NULL;
}
记得我们刚才放入的三个字符bit吗?这次我们来读取我们文件放入的三个字符,看看输出结果如何
可以看到我们成功读取到了bit这三个字符
我们知道,fgetc在程序读取结束的时候会返回EOF,而EOF的大小为-1的整形,我们试一下当我们读取第四次的时候的返回值,并且利用整形打印出来看看结果如何
#include <stdio.h>
void main()
{
FILE* pf = fopen("file_open.txt", "r");
int ret = fgetc(pf);
printf("%c", ret);
ret = fgetc(pf);
printf("%c", ret);
ret=fgetc(pf);
printf("%c", ret);
ret=fgetc(pf);
printf("%d", ret);
fclose(pf);
pf = NULL;
}
结果跟我们想的一样接收到了一个-1的整形,代表了getc接收到了EOF。
同样的跟上面的fputc一样,fgetc也可以在标准输入流即键盘输入字符,只需要改成下面代码即可
#include <stdio.h>
void main()
{
int ret = fgetc(stdin);
printf("%c", ret);
ret = fgetc(stdin);
printf("%c", ret);
ret = fgetc(stdin);
printf("%c", ret);
}
上面的例子均是讲的一个字符的读取,并且注意,字符的读取如果没有特殊说明都是从前往后依次读取,也可以想象成指针开始指向文件第一个字符,每读取一次都往后偏移一次。
那如果我们想要一行一行的进行读取,那有应该如何做呢?接下来就讲到fputs和fgets的函数了
字符串的读写fgets、fputs
我们先来看一下这两个函数的定义
fgets
输入:fgets第一个输入为你要存储的字符串数组,第二个为要存储多少个字符,第三个为你要读的流,即如果我要从流里面存储三个字符到我的arr1里面就可以写成fgets(arr1,3,FILE)
返回值:fgets返回的是从流内获得的字符串
fputs
输入:fputs的第一个输入就是你要输入的字符串,第二个输入就是你要输入的文件流或者其他流
返回值:当输入成功的时候fputs返回一个非负的值,如果输入失败返回EOF
我们来试着写入一串代码试一下fputs和fgets函数的使用
#include <stdio.h>
void main()
{
FILE* pf = fopen("file_open.txt","w");
if (pf == NULL)
{
perror("fopen");
return;
}
fputs("ABCDEF\n", pf);
fputs("FEDCBA\n", pf);
fclose(pf);
pf = NULL;
}
利用fputs输入两串字符串在txt文件,我们看一下txt文件里面存在的内容。
可以看到数据成功的输入了。
想要从这个文件里面读取这些字符道理也是一样的我们试用fgets来进行读取,但是还是有一些细节需要注意:
#include <stdio.h>
void main()
{
FILE* pf = fopen("file_open.txt", "r");
if (pf == NULL)
{
perror("fopen");
return;
}
char a[4];
char b[4];
printf("%s\n", fgets(a, 4, pf));
printf("%s\n", fgets(b, 4, pf));
printf("%s\n", b);
printf("%s\n", a);
fclose(pf);
pf = NULL;
}
如我们要在a和b里面读取pf四个字符放入,按照我们刚才写入的pf为ABCDEF\n和FEDCBA\n我们应该会赋值给a ABCD 还有b EF\nF 果真如此吗?我们跑一下程序试试
可以看到a被赋值了ABC而b被赋值了DEF,因为我们在读取字符串的时候系统为字符串留了一个\0的位置,所以我们真正意义上读到的字符串为3个
并且要注意,fgets最多读取为一行的数据,如果一行的数据为4个字符串,就算输入要读取的为100也只包含第一行的数据如我们将代码改成如下
#include <stdio.h>
void main()
{
FILE* pf = fopen("file_open.txt", "r");
if (pf == NULL)
{
perror("fopen");
return;
}
char a[10];
char b[10];
printf("%s\n", fgets(a, 10, pf));
printf("%s\n", fgets(b, 10, pf));
printf("%s\n", b);
printf("%s\n", a);
fclose(pf);
pf = NULL;
}
我们获取十个字符串,输出为
即也为六个字符串
现在我们知道了如何获取整行的字符串,那如果我们要进行一个结构体的获取或者输入应该怎么办呢?下面就讲到格式化的输入与输出
格式化的读写fprintf、fscanf
fprintf函数参数
输入:fprintf的输入和printf输入的区别就是,fprintf可以指定一个流,也就是第一个输入参数,我们这里用的就是文件流,其他的输入都是与printf一样。
返回值:fprintf返回的值是输入的字节数
fscanf函数参数
输入:和sancf一样,除了第一个参数可以指定一个流外,其他和scanf一样的用法,我们这里也是指定的读取文件流
返回值:返回成功转换和分配的字段,当返回为0的时候即表示文件中没有部署的数值,如果返回EOF代表了错误或者到达文件的终点
如下使用fprintf和fscanf函数
#include <stdio.h>
typedef struct S
{
char a[10];
int b;
char c;
}S;
void main()
{
FILE* pf = fopen("file_open.txt", "w");
if (pf == NULL)
{
perror("fopen:");
return;
}
S s = { "abcdef",18,'g' };
fprintf(pf, "%s %d %c", s.a, s.b, s.c);
fclose(pf);
pf = NULL;
}
这个时候我们的txt文件中所含内容为
利用fscanf函数来读取这些数值如下
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
typedef struct S
{
char a[10];
int b;
char c;
}S;
void main()
{
FILE* pf = fopen("file_open.txt", "r");
if (pf == NULL)
{
perror("fopen:");
return;
}
S s = { 0 };
fscanf(pf, "%s %d %c", s.a, &s.b, &s.c);
printf("%s\n%d\n%c\n", s.a, s.b, s.c);
fclose(pf);
pf = NULL;
}
编译器正常的输出了我们要的值
以上我们的读写都可以在我们文件中显示出我们输入进去的字符,这些叫做文本读写,但是文本读写有点不好就是比较占内存,优势是我们在看文件的时候可以看得懂,还有一种读写的方式是二进制读写
二进制的读写fwrite、fread
fwrite的参数如下
输入:其中第一个为要输入的数据,第二个为要输入数据的大小如整形为4个字节,第三个数据为要输入的数量,第四个就是数据要输入的流,如文件流
返回值:返回的是真正输入进文件的数量,一般来说是等于你要输入的count,如果返回的为错误即输入的数据小于count。
fread参数如下
输入:第一个为要存储的数据,第二个为存储的单个数据大小,第三个为数据的数量,第四个为从哪个流来存储数据。
返回值:返回的是实际读取的数量,如果达到文件末尾则返回0,错误返回EOF
我们同样利用这两个代码来写一个程序试试
#include <stdio.h>
typedef struct S
{
char a[10];
int b;
char c;
}S;
void main()
{
FILE* pf = fopen("file_open.txt", "w");
if (pf == NULL)
{
perror("fopen:");
return;
}
S s = { "abcdefgg",28,'s' };
fwrite(&s, sizeof(S), 1, pf);
fclose(pf);
pf = NULL;
}
利用fwrite写了如下数据,当我们打开我们的txt文件的时候却发现
文件变成了乱码,因为我们输入的数据在文件中是以二进制的形式进行输入的,正常来说我们输入的数据是以ASCII码值对应的文本显示,但是这里我们直接输入二进制数据,显示的就这一系列二进制对应的ASCII码值,得到一个很奇怪的文案。我们用二进制操作器看一下这些字符对应二进制码
可以看到61-67分别对应了a-g的ASCII码值,1C是十六进制的28,73是w的ASCII码值
那如何读取这些数据呢?我们只需要利用fread即可
如下获取这些数据
#include <stdio.h>
typedef struct S
{
char a[10];
int b;
char c;
}S;
void main()
{
FILE* pf = fopen("file_open.txt", "r");
if (pf == NULL)
{
perror("fopen:");
return;
}
S s = { 0 };
fread(&s, sizeof(S), 1, pf);
printf("%s %d %c", s.a, s.b, s.c);
fclose(pf);
pf = NULL;
}
我们可以看到这样我们输出来的就还原我们刚才输入的数据了
文件的随机读写
fseek、ftell、rewind
上述的代码中所介绍的函数都是按照顺序从前往后从上往下读取数据,如果我们想让数据按照不同地方进行读取应该怎么做呢?这里笔者给大家介绍三个函数,具体例子大家可以自己去试一下,这三个函数还是蛮好理解的
首先是fseek函数
第一个参数就是我们要操作的流,一般来说是文件流,第二个参数是要设置的偏移量(以字节为大小)第三个参数是你要设置的偏移起始位置,一般有三种
从当前点偏移,从文件末尾偏移,和起始点偏移。
fseek ( pFile , 9 , SEEK_SET );比如这个代码意思就是从pFile这个函数的起始点往后偏移九个位置
ftell函数
ftell函数输入文件流返回的是当前位置具体起始位置的偏移量
rewind函数
rewind函数作用是让函数指针偏移量回到起始位置
文件错误或结束的判断标志feof,ferror
feof函数是来判断文件是否结束的
其返回值有两种,如果文件读取到最后结束了那就非零值,如果没有的话那就返回0
还有一种函数是ferror
与feof不同,遇到错误的时候ferror返回为真,没有错误返回0
要记住,feof不能判断文件读取是否结束,因为也有可能是因为文件错误而结束,所以当一个文件读取结束了,我们要判断是因为什么原因读取结束可以利用好这两个代码判断,如下:
#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);
}
以上就是文件使用的时候使用频率高的函数,很多函数需要在自己实战中反复使用才能加深对函数的印象,以后用起来才能更加灵活,感谢各位看到这里~