#include
int main() {
//打开文件
FILE* pf = fopen("data.txt", "r");
//用于判断是否打开成功
if (pf == NULL) {
perror("fopen");//用于打印错误信息
return 1;//直接停止程序
}
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
首先我们如何管理一个文件呢,我们在管理一个文件时会创建一个文件信息区,这个文件信息区会存放比如:文件名、文件大小、文件的位置等等,文件信息区本质上就是个结构体变量,该结构体的类型为FILE,是编译器设计之初就定义的一种专门用于存放文件信息的类型。在vs2013编译环境中FLIE的类型声明如下所示:
struct _iobuf
{
char* _ptr;
int _cnt;
char* _base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
};
typedef struct _iobuf FILE;
这就是我们为什么创建一个FILE*的指针,就是给要处理的文件在内存中开辟一个文件信息区去维护文件。
然后fopen是一个函数,第一个参数是要操作的文件名,第二个参数是要进行哪种操作,比如r是read,w是write,rb是以二进制的形式读,wb是以二进制的形式写。
‘r’:只读。该文件必须已存在。
‘r+’:可读可写。该文件必须已存在,写为追加在文件内容末尾。
‘rb’:表示以二进制方式读取文件。该文件必须已存在。
‘w’:只写。打开即默认创建一个新文件,如果文件已存在,则覆盖写(即文件内原始数据会被新写入的数据清空覆盖)。
‘w+’:写读。打开创建新文件并写入数据,如果文件已存在,则覆盖写。
‘wb’:表示以二进制写方式打开,只能写文件, 如果文件不存在,创建该文件;如果文件已存在,则覆盖写。
‘a’:追加写。若打开的是已有文件则直接对已有文件操作,若打开文件不存在则创建新文件,只能执行写(追加在后面),不能读。
‘a+’:追加读写。打开文件方式与写入方式和’a’一样,但是可以读。需注意的是你若刚用‘a+’打开一个文件,一般不能直接读取,因为此时光标已经是文件末尾,除非你把光标移动到初始位置或任意非末尾的位置。
需要注意的是,这两个参数都需要用双引号去引起来。
下边的是用于判断是否打开成功,因为如果打开不成功的话,fopen
函数会返回空指针。也是有可能打印不成功的,因为如果没有这个文件的话,你还用“r”形式打开,那就会报错,但是用“w”的形式打开就不会,因为这样就是创建一个新的文件。
其实我们想一下也知道,你让程序去读文件中的内容,没有文件,那程序肯定不知道去哪里读,但是你让程序去写,没有就创建一个文件去写呗,这其实很好理解。
然后下一步,就是要关闭文件了,这也是一个函数,最后要将文件指针置为空指针,要不然就成为野指针了。
以上是最基本的打开文件和关闭文件操作
当然了,还需要提一嘴,就是我们创建文件只能在当前目录(就是源文件所在的目录)下创建吗,当然不是了,我们之前写的data.txt这属于相对路径,当然我们也可以在双引号中写绝对路径,比如C:\Users\86193\Desktop\data.txt 这是我的电脑上在桌面上创建一个这样的文件的路径,每个文件都只有一个绝对路径,每一个绝对路径都只有一个文件。
int main() {
FILE* pf = fopen("C:\\Users\\86193\\Desktop\\data.txt", "w");//需要转义一下
if (pf == NULL) {
perror("fopen");
return 1;
}
return 0;
}
当然我们用的时候需要将\转义一下。
int main() {
FILE* pf = fopen(".\\Debug\\data.txt", "w");//当前目录下Debug文件夹下去创建
if (pf == NULL) {
perror("fopen");
return 1;
}
return 0;
}
当然相对路径还可以这么去操作,在前面加一个点表示在当前目录下的一个文件夹中去创建,找到上一级文件夹,就要加两个点。
FILE* pf = fopen("..\\test_9_24\\data.txt", "w");//上一级目录下test_9_24文件夹下去创建
FILE* pf = fopen("..\\..\\Common\\data.txt", "w");//上一级上一级
接下来我们说如何往文件中放东西
int main() {
FILE* pf = fopen("data.txt", "r");
if (pf == NULL) {
perror("fopen");
return 1;
}
char ch = fgetc(pf);
printf("%c", ch);
fclose(pf);
pf = NULL;
return 0;
}
就是从文件中读取num-1个字符放到str中,因为有一个位置要放’\0’
int main() {
FILE* pf = fopen("data.txt", "r");
if (pf == NULL) {
perror("fopen");
return 1;
}
char arr[10] = { 0 };
fgets(arr, 4, pf);
printf("%s", arr);
fclose(pf);
pf = NULL;
return 0;
}
所以这里会打印三个字符,需要注意的是,如果num大于一行的字符个数,那么把’\n’放到字符数组中之后再放个’\0’就不管了。下一回读的时候就从下一行开始读。
我们来试验一下:
就是把给定的字符放到文件中,如果之前的文件中有东西的话,结果相当于删掉之前的所有再写入一个字符,因为之前文件中的光标在文件的最开始。
int main() {
FILE* pf = fopen("data.txt", "w");
if (pf == NULL) {
perror("fopen");
return 1;
}
fputc('a', pf);
fclose(pf);
pf = NULL;
return 0;
}
就是把一个字符串放到文件当中,也是会覆盖掉之前的,原因与上面的一样
int main() {
FILE* pf = fopen("data.txt", "w");
if (pf == NULL) {
perror("fopen");
return 1;
}
fputs("abcdefg", pf);
fclose(pf);
pf = NULL;
return 0;
}
就是以格式化的形式打印数据到文件中,它和printf只差一个文件指针,我们怎么用printf就怎么用fprintf就可以了,只需要最后在最前面加入一个文件指针就可以了。
struct S {
int i;
float f;
char c[10];
};
int main() {
struct S s = { 10,3.14f,"abcdef" };
FILE* pf = fopen("data.txt", "w");
if (pf == NULL) {
perror("fopen");
return 1;
}
fprintf(pf,"%d %f %s", s.i, s.f, s.c);
fclose(pf);
pf = NULL;
return 0;
}
这样就把相关数据写到文件中了。
它们之间的关系其实和上面那组的关系是一样的,这个是从文件中按格式化读取内容,把读到的放到后面,读的话要保证文件中有东西,没东西的话读出来的都是0
struct S {
int i;
float f;
char c[10];
};
int main() {
struct S s = { 0 };
FILE* pf = fopen("data.txt", "r");
if (pf == NULL) {
perror("fopen");
return 1;
}
fscanf(pf, "%d %f %s", &s.i, &s.f, s.c);
printf("%d %f %s", s.i, s.f, s.c);
fclose(pf);
pf = NULL;
return 0;
}
这两个函数其实和文件操作没什么关系,但谁让它们和前面的长得像呢,我们就来区分一下
这个是将后面格式化的数据写成一个字符串(输出到一个字符串中)
这个是从字符串中读取数据并把它们格式化(把字符串转化成对应的整形,浮点型等等)
struct S {
int i;
float f;
char c[10];
};
int main() {
struct S s = {10,3.14,"abcdef" };
struct S s1 = { 0 };
char arr[30] = { 0 };
FILE* pf = fopen("data.txt", "r");
if (pf == NULL) {
perror("fopen");
return 1;
}
sprintf(arr,"%d %f %s", s.i, s.f, s.c);
printf("%s", arr);
sscanf(arr,"%d %f %s", &s1.i, &s1.f, s1.c);
printf("%d %f %s", s.i, s.f, s.c);
fclose(pf);
pf = NULL;
return 0;
}
这就是它们的基本使用
第一个参数是一个地址,用来找到要写入的内容。第二个参数是要写入的每个元素的大小,第三个参数是要写入多少个元素
int main() {
char arr[10] = "abcdef";
FILE* pf = fopen("data.txt", "wb");
if (pf == NULL) {
perror("fopen");
return 1;
}
fwrite(arr, sizeof(char), 4, pf);
fclose(pf);
pf = NULL;
return 0;
}
struct S {
int i;
float f;
char s[10];
};
int main() {
struct S s = { 12,6.18,"abcdef" };
FILE* pf = fopen("data.txt", "wb");
if (pf == NULL) {
perror("fopen");
return 1;
}
fwrite(&s, sizeof(struct S), 1, pf);
fclose(pf);
pf = NULL;
return 0;
}
当然这时存进去的数据你是看不懂的,因为都是二进制数据。那我们用的时候就要用二进制的形式去读。
struct S {
int i;
float f;
char s[10];
};
int main() {
struct S s = { 0 };
FILE* pf = fopen("data.txt", "rb");
if (pf == NULL) {
perror("fopen");
return 1;
}
fread(&s, sizeof(struct S), 1, pf);
printf("%d %f %s", s.i, s.f, s.s);
fclose(pf);
pf = NULL;
return 0;
}
这样就可以读出二进制数据并打印出来了。
这个函数是定位文件指针(光标),就是将光标放到你想要的位置,之前光标不是只能在开头嘛。第二个参数是偏移量,向左为负,向右为正,第三个参数是相对于哪个位置,分别是开头 目前 和最后。
//文件中放的是abcdefg
int main() {
FILE* pf = fopen("data.txt", "r");
if (pf == NULL) {
perror("fopen");
return 1;
}
char ch = fgetc(pf);
printf("%c\n",ch);
ch = fgetc(pf);//每读取一个字符光标会向后移动一个位置
printf("%c\n",ch);
ch = fgetc(pf);
printf("%c\n", ch);
int ret = ftell(pf);//计算光标目前位置
printf("%d\n", ret);
fseek(pf, -2, SEEK_END);//把光标放到f位置
ch = fgetc(pf);
printf("%c\n", ch);
rewind(pf);//把光标放到最开始的位置
ch = fgetc(pf);
printf("%c\n", ch);
return 0;
}