在我们写完代码后,常常会发现在自己所写的路径下拥有不同的后缀名的文件,这涉及到我们在宏观角度上理解编程的方方面面,及文件操作(不可忽视的一章)。所以接下来我会从几个方面去介绍C语言文件操作专题。
一.为什么使用文件
在编写大型项目时,常常是经过日积月累的工作才能完成,如果第一天的工作不能累计到第二天,那程序猿难免要一天完成了。所以我们就会想到将项目的星系记录下来,只有我们在选择删除数据的时候,数据才不复存在。这将涉及到数据持久化的问题,我们一般数据持久化的方法有,把数据存放在磁盘文件,存放到数据库等方式。
使用文件我们可以将数据直接存放到电脑的硬盘中,做到数据的持久化。
结论:使用文件我们可以将数据直接存放到电脑的硬盘中,做到数据的持久化。
二.什么是文件
磁盘上的文件是文件,但是在程序设计中,我们一般谈的文件有两种:程序文件,数据文件(从文件功能角度来分类)。首先是程序文件:包括源程序文件(后缀为.c),目标文件(widows下的为.obj),可执行程序(widows下的后缀为.exe)。其次是数据文件:文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行时需要从中读取数据的文件,或者输出内容的文件。
本章讨论的内容时数据文件。写程序时计算机处理数据的输入输出都是以终端为对象,即从终端的键盘输入数据,运行结果显示到显示器上。这里我们会把信息输入到磁盘上,当需要的时候再次从磁盘上把数据读取到内存中使用,这里处理的就是磁盘文件。
一个文件要有一个唯一的文件标识,以便用户识别和引用。
三.文件的打开和关闭
<1> 首先在缓冲文件系统中,关键的概念是文件类型指针,简称文件指针。
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的姓名,文件状态以及文件当前的位置等)。这些信息保存在一个结构体变量中,该结构体类型是由操作系统声明的,取名为FILE。不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。每当打开一个文件时,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息。一般都是通过这个FILE指针来维护这个FILE结构的变量,这样使用起来更方便。比如:我们定义FILE*pf;//文件指针变量。定义pf是一个指向FILE类型数据的指针变量,可以使p指向某个文件的文件信息区(结构体变量)。通过文件信息区中的信息就能够访问该文件,也就是说,通过文件指针变量可以找到与它关联的文件。比如:
<2>文件的打开和关闭
文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于确立了指针和文件的关系。ASCII规定使用fopen函数打开文件,使用fclose关闭文件。
FILE* fopen (const char* filename, const char*mode); (操作文件的地址,操作方式)
Int fclose (FILE*stream);
操作方式如下表(引用B站鹏哥C语言):
4.文件的顺序读写
<1>顺序读写函数介绍(引用B站鹏哥C语言):
<1>fgetc/fgets函数
这两个函数功能非常相似,都是输入函数,不同之处在于一次输入的数据个数不同,下面看代码:
//fgetc的使用
#include<stdio.h>
int main()
{
FILE* pf = fopen("text.txt", "w");
if (pf == NULL)//判断文件指针是否为空指针
{
perror("fopen");
return 1;
}
int ch = 0;
for(int i=0;i<3;i++)
{
ch = fgetc(pf);//从文本文件中获取数据到屏幕上
printf("%c", ch);
}
fclose(pf);
pf = NULL;
return 0;
}
//fgets的使用
#include<stdio.h>
int main()
{
FILE* pf = fopen("text.txt", "w");
if (pf == NULL)//判断文件指针是否为空指针
{
perror("fopen");
return 1;
}
char arr[20];
fgets(arr, 10, pf);//将pf文本文件中10个数据获取到arr中
printf("%s\n", arr);
fclose(pf);
pf = NULL;
return 0;
}
<2>fput/fputs函数
//fputc的使用
#include<stdio.h>
int main()
{
FILE* pf = fopen("text.txt", "w");
if (pf == NULL)//判断文件指针是否为空指针
{
perror("fopen");
return 1;
}
for (int i = 0; i < 3; i++)
{
fputc('a' + i, pf);//将数据打印到文本文件text.txt中
}
fclose(pf);
pf = NULL;
return 0;
}
//fputs的使用
#include<stdio.h>
int main()
{
FILE* pf = fopen("text.txt", "w");
if (pf == NULL)//判断文件指针是否为空指针
{
perror("fopen");
return 1;
}
fputs("hello bit\n", pf);//将数据传到文本文件中
fputs("hello world\n", pf);
fclose(pf);
pf = NULL;
return 0;
}
<3>fscanf/fprintf函数
<4>fread/fwrite函数
//二进制的读文件-fread
#include<stdio.h>
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = {0};
//打开文件
FILE* pf = fopen("test.dat", "rb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
fread(&s, sizeof(struct S), 1, pf);
printf("%s %d %f\n", s.name, s.age, s.score);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
//二进制的写文件 - fwrite
#include<stdio.h>
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = { "pengge",18,99.9f };
//打开文件
FILE* pf = fopen("test.dat", "wb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
fwrite(&s, sizeof(struct S), 1, pf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
<2>对比一组函数:scanf/fscanf/sscanf和printf/fprintf/sprintf
scanf/printf:是针对标准输入流/标准输出流的格式化输入输出语句。
fscanf/fprintf:是针对所有输入流/所有输出流的格式化输入输出语句。
Sscanf/sprintf:从流中读取数据。
5.文件的随机读写
<1>fseek函数
#include <stdio.h>
int main()
{
FILE* pFile;
long size;
pFile = fopen("myfile.txt", "rb");
if (pFile == NULL) perror("Error opening file");
else
{
fseek(pFile, 0, SEEK_END); // non-portable
size = ftell(pFile);
fclose(pFile);
printf("Size of myfile.txt: %ld bytes.\n", size);
}
return 0;
}
<2>ftell函数
/* ftell example : getting size of a file */
#include <stdio.h>
int main ()
{
FILE * pFile;
long size;
pFile = fopen ("myfile.txt","rb");
if (pFile==NULL) perror ("Error opening file");
else
{
fseek (pFile, 0, SEEK_END); // non-portable
size=ftell (pFile);
fclose (pFile);
printf ("Size of myfile.txt: %ld bytes.\n",size);
}
return 0;
}
<3>rewind函数
#include<stdio.h>
int main()
{
//打开文件
FILE* pf = fopen("test.dat", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
int ch = fgetc(pf);
printf("%c\n", ch);//p
ch = fgetc(pf);
printf("%c\n", ch);//e
ch = fgetc(pf);
printf("%c\n", ch);//n
ch = fgetc(pf);
printf("%c\n", ch);//g
rewind(pf);
printf("%d\n", ftell(pf));
ch = fgetc(pf);
printf("%c\n", ch);//p
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
6.文本文件和二进制文件
根据数据的组织形式,数据文件被称为文本文件和二进制文件。数据在内存中以二进制的形式存储,如果不加转换的输出到内存,就是二进制文件。如果要求在内存上以ASCII码的形式存储,则需要在存储前进行转换。以ASCII字符的形式存储的文件就是文本文件。那么一个数据在内存中是如何存储的呢?字符一律以ASCII码形式存储,数值型数据既可以用ASCII形式存储,也可以以二进制形式存储。例如有整数10000,如果以ASCII码形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),以二级制形式输出,则在磁盘上只占4个字节。
7.文件读取结束的判定
<1>feof函数
首先,在文件读取过程中,不能用feof函数的返回值直接来判断文件是否结束。相反它的作用是:当文件读取结束的时候,判断文件读取结束的原因是否是:遇见文件尾结束。
文本文件读取是否结束,判断返回值是否为EOF(fgetc专用),或者NULL(fgets专用),二进制文件的读取结束判断,判断返回值是否小于实际要读的个数(fread专用)。
8.文件缓冲区
ASCII标准采用缓冲文件系统处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序每一个正在使用的文件开辟以块文件缓冲区。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个将数据送到程序数据区。缓冲区的大小由C编译系统决定的。
结论:因为有缓冲区的存在,C语言在操作文件的时候,需要刷新缓冲区或者在文件操作结束的时候关闭文件。如果不做,可能导致读写文件的问题。
以上就是文件处理的全部内容,若有不足之处,请不惜赐教。