目录
1.什么是文件
我们写的程序储存在电脑中,如果程序退出,内存被回收数据就丢失了,再次运行时就看不到上次运行的数据,如果要将数据进行持久化的保存,就要使用文件。
在程序设计中,我们所讨论的文件从功能的角度划分为两种:程序文件、数据文件
程序文件:包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)
数据文件:文件内容不一定是程序,而是程序运行时读写的数据
文件名:一个文件要有一个唯一的文件路径,以便用户识别和引用,包括3部分:文件路径+文件名+文件后缀
2.二进制文件和文本文件
根据数据的组织形式,数据文件被称为:文本文件或者二进制文件
二进制文件:数据在内存中以二进制的形式存储,不加转换的输出到外存的文件中
文本文件:如果要求在外存上以ASCII码的形式存储,则需要在存储前转化,以ASCII码字符的形式存储
字符一律以ASCII码形式存储,数值型数据既可以用ASCII码存储,也可以使用二进制的形式存储,例如10000,ASCII码形式输出要占5个字节,因为有5个字符,二进制形式输出,在磁盘上占4个字节
3.文件的打开和关闭
文件在读写之前应该先打开文件,在使用结束结束后关闭文件,在编写程序的时候,打开文件的同时,都会返回一个FILE*的指针变量指向该文件。
我们用fopen函数打开文件,fclose来关闭文件。
//打开文件
FILE* fopen( const char* filename, const char* mode );
//关闭文件
int fclose( FILE* stream );
fopen函数的第一个参数是 要打开的文件名,第二个参数是 打开方式,下面是文件的打开方式
比如我们现在写一个代码
#include <stdio.h>
int main()
{
FILE* pf = fopen("test.txt", "r");//以读的形式打开
if (pf == NULL) //判断是否打开成功
{
perror("fopen");
return 1;
}
fclose(pf);//关闭文件
pf = NULL;//置空
return 0;
}
fclose关闭文件的时候,pf没有指向的东西,此时pf为野指针,所以最好把pf置为NULL
此时"test.txt"文件不存在这个工程里面
当代码运行起来时pf就会 返回NULL,结果报出错误,fopen: No such file or directoryfopen: No such file or directory ,没有这个文件或者文件夹
当我们新建一个"test.txt"的文件之后再运行,程序不会报错,正常运行
当我们用"w"打开时,原本文件中如果有内容,会全部清空,如下,代码为
#include <stdio.h>
int main()
{
FILE* pf = fopen("test.txt", "w");//以写的形式打开
if (pf == NULL)
{
perror("fopen");
return 1;
}
fclose(pf);//关闭文件
pf = NULL;//置空
return 0;
}
运行前的文件
运行时的代码
运行后的文件,内容已被清空
现在,我们再把"test.txt"这个文件删除
以"w"的形式运行,和刚刚代码一样,看看结果
运行不会出错,而且自动创建了"test.txt"这个文件
别的形式步骤也是一样的,得到的结果会不一样,根据上面的表自己研究研究,这里就不一个一个示范了
4.文件的顺序读写
顺序读写相关函数表如下
4.1 fputc 和 fgetc
我们先看fputc,这个函数的功能就是写字符到文件对应的流中去
返回值是,如果成功返回你输入的字符,如果失败,会把这个错误标记起来
比如我们写个'a' 'b' 'c'到文件里面去
#include <stdio.h>
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "w");//我们要写数据进去
if (pf == NULL) //判断
{
perror("fopen");
return 1;
}
//写文件
fputc('a', pf);
fputc('b', pf);
fputc('c', pf);
fclose(pf);//关闭文件
pf = NULL;//置空
return 0;
}
此时没有发生任何错误,成功写入,这里,每一次调用这个函数,文件的光标就会自动后移
当然不止这一种写法,fputc一次写入一个字符,所以也可以用循环进行写入
fgetc
参数就一个文件指针,就是从这个文件里面读数据
读取成功,会返回这个字符的ASCII码值,读取失败,返回EOF
比如我们还是从"test.txt"这个文件中读,此代码只写读文件的部分,打开文件和关闭文件跟上面是一样的,替换一下就好了
//读文件
int ch = fgetc(pf);
printf("%c", ch);
ch = fgetc(pf);
printf("%c", ch);
ch = fgetc(pf);
printf("%c", ch);
读文件,然后打印出来
如果想读文件中所有内容,可以用循环
//读文件
int ch = 0;
while ((ch = fgetc(pf))!=EOF)
{
printf("%c", ch);
}
4.2 fputs 和 fgets
如果想输入或输出一串数据,就可以用fputs 和 fgets
把str指向的字符串写到stream里去,遇到'\0'停止,比如下面这个代码
#include <stdio.h>
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "w");//我们要写数据进去
if (pf == NULL) //判断
{
perror("fopen");
return 1;
}
//写文件
fputs("hello", pf);
fclose(pf);//关闭文件
pf = NULL;//置空
return 0;
}
代码正常运行
当我们再写一行时,是接着"hello"还是会换行呢,看下面的代码
#include <stdio.h>
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "w");//我们要写数据进去
if (pf == NULL) //判断
{
perror("fopen");
return 1;
}
//写文件
fputs("hello", pf);
fputs("world", pf);
fclose(pf);//关闭文件
pf = NULL;//置空
return 0;
}
运行起来看结果,写在了一行上
所以如果要换行,需要自己加换行符
这个函数有3个参数,意思是从stream里面读num个字节的数据到str指向的里面去,这里需要注意的是 要读num个字节的数据,其实读的个数是num-1,因为最后一个字节需要存放'\0'
看下面的代码
#include <stdio.h>
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "r");//我们要读数据进去
if (pf == NULL) //判断
{
perror("fopen");
return 1;
}
//读文件
char arr[10] = { 0 };
fgets(arr, 10, pf);
fclose(pf);//关闭文件
pf = NULL;//置空
return 0;
}
我们打开监视窗口观察一下
看最右边的监视窗口里面的arr数组,可以看到只把前9个字符读进去了,最后1个位置放了'\0'
假如我们第一行不够10个字符怎么办,我们来看下面的代码,现在文件里面只有"hello\n",为了方便观察,把arr数组里面的值改一下
可见,如果这行不够10个字符,连\n都读进去,再加上\0,这个函数就是只读一行,换行不读取,想换行需要再调用一次这个函数
如果读取成功,这个函数的返回值就是str,如果失败,遇到了文件末尾或错误,就会返回NULL,所以现在代码就可以写成下面这样
#include <stdio.h>
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "r");//我们要读数据进去
if (pf == NULL) //判断
{
perror("fopen");
return 1;
}
//读文件
char arr[20] = { 0 };
while (fgets(arr, 20, pf) != NULL)
{
printf("%s", arr);
}
fclose(pf);//关闭文件
pf = NULL;//置空
return 0;
}
运行看结果
还有一种情况是一排的内容比20个还多呢?结果依然是可以完全读取的,自己改变文件的内容试一试
4.3 fprintf 和 fscanf
对比来看fprintf和printf
可见,fprintf只是多了一个参数而已,其他与printf没区别,会用printf,就会fprintf,举个例子
#include <stdio.h>
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "w");//我们要写数据进去
if (pf == NULL) //判断
{
perror("fopen");
return 1;
}
//写文件
char arr[20] = "zhengsan";
//printf("%s", arr); //printf的用法
fprintf(pf, "%s", arr);//fprintf的用法
fclose(pf);//关闭文件
pf = NULL;//置空
return 0;
}
正常运行,写入成功,并且是以文本的形式写进去的
如果要写多条数据,也是一样,循环起来
我们同样来对比一下fscanf和scanf
也是只有参数的区别,会用scanf就会fscanf,举个例子
#include <stdio.h>
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "r");//我们要读数据进去
if (pf == NULL) //判断
{
perror("fopen");
return 1;
}
//读文件
char arr[20] = "zhengsan";
//scanf("%s", arr); //scanf的用法
fscanf(pf, "%s", arr);//fscanf的用法
printf("%s", arr);//打印到屏幕上看看
fclose(pf);//关闭文件
pf = NULL;//置空
return 0;
}
结果为
4.4 fwrite 和 fread
我们4.1 4.2 4.3 所说的函数可以适用于所有的输入输出流,也就是说可以在文件输入输出,也可以键盘输入或者屏幕上输出,且都是以文本的形式读写的,而fwrite和fread只能在文件输入输出,以二进制形式读写
参数的意思是:ptr, 指向要被写的数组 ; size, 每写元素的长度,单位是字节 ;count, 一次要写的元素个数;最后一个参数就是文件流
从ptrr指向的数组里面写count个大小为size个字节的数据放到文件流里
举例如下
#include <stdio.h>
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "wb");//以wb,二进制的形式写
if (pf == NULL) //判断
{
perror("fopen");
return 1;
}
//写文件
int arr[] = { 1,2,3,4,5 };
int sz = sizeof(arr) / sizeof(arr[0]);//算数组大小
fwrite(arr, sizeof(int), sz, pf);
fclose(pf);//关闭文件
pf = NULL;//置空
return 0;
}
运行看结果
写入成功,但是我们看不懂,因为是以二进制的形式写的
我们再以二进制的形式读
参数和fwrite类似: 从流里面读count个大小为size个字节的数据放到ptr指向的数组里,看下面的代码
#include <stdio.h>
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "rb");//以rb,二进制的形式读
if (pf == NULL) //判断
{
perror("fopen");
return 1;
}
//读文件
int arr[5] = { 0 };
fread(arr, sizeof(int), 5, pf);
for (int i = 0; i < 5; i++)
{
printf("%d ", arr[i]);
}
fclose(pf);//关闭文件
pf = NULL;//置空
return 0;
}
运行后结果
这个函数的返回值是读取的个数,如果我们不知道文件有多少个时,我们可以这样写
#include <stdio.h>
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "rb");//以rb,二进制的形式读
if (pf == NULL) //判断
{
perror("fopen");
return 1;
}
//读文件
int arr[5] = { 0 };
int i = 0;
while (fread(arr+i, sizeof(int), 1, pf))//一次读一个
{
printf("%d ", arr[i]);
i++;
}
fclose(pf);//关闭文件
pf = NULL;//置空
return 0;
}
当返回0时退出循环
5.文件的随机读写
5.1 fseek
根据文件指针的位置和偏移量来定位文件指针(也就是文件光标)
第一个参数就是文件指针,第二个参数是偏移量,第三个参数就是从哪个位置偏移
第三个参数有三个选择,如下
SEEK_SET是文件的起始位置;SEEK_CUR是文件指针当前位置;SEEK_END是文件末尾
举个例子,
#include <stdio.h>
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "r");//读的形式打开
if (pf == NULL) //判断
{
perror("fopen");
return 1;
}
//读文件
int ch = 0;
ch = fgetc(pf);//a, 且光标向后移
printf("%c\n", ch);//打印a
fseek(pf, 5, SEEK_CUR);//此时应该指向g
ch = fgetc(pf);//g,且光标向后移
printf("%c\n", ch);//打印g
fclose(pf);//关闭文件
pf = NULL;//置空
return 0;
}
当然,在此情况下,想要文件指针指向g还有别的办法,如
//fseek(pf, 6, SEEK_SET);
//fseek(pf, -2, SEEK_END);
//用SEEK_END的时候注意偏移量为负的
给的起始位置不一样,偏移量就不一样
5.2 ftell
返回文件指针相对于起始位置的偏移量
直接看例子
#include <stdio.h>
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "r");//读的形式打开
if (pf == NULL) //判断
{
perror("fopen");
return 1;
}
//读文件
int ch = 0;
ch = fgetc(pf);//a, 且光标向后移
printf("%c\n", ch);//打印a
fseek(pf, 5, SEEK_CUR);//此时应该指向g,偏移量应该为6
printf("%d\n", ftell(pf));//打印验证
fclose(pf);//关闭文件
pf = NULL;//置空
return 0;
}
结果为6
当我们代码再稍作修改,就可以得到文件长度
#include <stdio.h>
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "r");//读的形式打开
if (pf == NULL) //判断
{
perror("fopen");
return 1;
}
//读文件
int ch = 0;
ch = fgetc(pf);
printf("%c\n", ch);
fseek(pf, 0, SEEK_END);
printf("%d\n", ftell(pf));
fclose(pf);//关闭文件
pf = NULL;//置空
return 0;
}
这个函数就是这样运用的
5.3 rewind
功能就是:让文件指针回到起始位置
这次分享就到这里,感谢观看!