突然发现自己的C语言文件部分还没有学,赶紧来补一下~~
1.文件分类
- 文本文件
- 磁盘文件(二进制文件)
- C语言特殊文件标识:stdin(标准输入:通指键盘输入),stdout(标准输出:通指屏幕输出),自定义文件(FILE)
2.文件操作
打开文件与关闭文件:fopen&fclose
打开文件使用的是函数:FILE*fopen(char*strURL,char*mode);第一个参数是文件的路径,第二是读写方式,二者都是以字符串形式存在的。
关闭文件使用的是:int fclose(FILE*strurl);注意,不能关闭空文件!!
下面来介绍一下读写方式的分类以及他们的功能:(注意,写在函数里的时候,虽然是一个字母,但是也要加上双引号代表字符串的地址哦)
读写方式 | 作用 |
r:read | 读的方式打开文件,相当于是一种只读模式, 如果文件不存在函数返回空 |
w:write | 写的方式打开文件,如果文件不存在,那么创建一个文件; 如果文件存在,那么会将源文件清空。 |
a:append | 追加写的方式打开文件,即我们不会清除原文件,而是会原来文件的后面接着写;如果文件不存在,那么返回空。 |
组合方式:w+,r+,a+ | +代表可读可写,加上之后都变成可读可写了,只不过返回的时候有区别 |
rb,wb,ab | b代表以二进制的方式进行读写。我们还可以再加上+,和上面的意思一样 |
下面上一段代码看看具体的操作
FILE*fp=fopen("firstfile.file","w");
fclose(fp);
很神奇的一点在于,你可以在这个fopen函数里面创建任意类型的文件,文件后缀其实没有什么太大关系。比如上面创建一个file类型的文件,并在里面进行写的操作。
我们还需要关心的是,如果这个文件是我们创建的,那么他的具体位置在哪里呢?我们来看下图:
从上面我们可以看出,假如你要打开的文件是之前没有的,那么电脑会把它创建在和原来的c源文件的exe执行文件在一个文件夹里面。string是我这个新文件进行操作的c源文件。
如果我们写了一个新的文件类型,可能计算机无法识别,我在这里选用记事本打开然后进行写入。
然后我们返回看一下文件改变了没有。
很明显文件大小变了,说明写入成功!
字符读写:fgetc与fputc
这两个是以字符形式读取文件和写文件。读取时,fgetc接受文件指针,然后每次读一个字符,然后文件指针会自动进行移动,每次进行下一步操作的时候,他就会读取后面的字符。每个文件的结尾都会有一个结束标识符叫做EOF,当读取到EOF的时候就会停止。
先来看fgetc函数
FILE*fp=fopen("firstfile.file","r");
int ch=fgetc(fp);
while(ch!=EOF){
printf("%c",ch);
ch=fgetc(fp);
}
fclose(fp);
注意点:由于每次调用fgetc函数的时候都会把指针向前进行移动,因此我们必须用另外一个变量来存储得到的结果,否则就会出现有一些字符被跳过的情况。(和指针的++操作类似)
再来看fputc函数,很明显,他需要有两个参数,一个是需要传进去的参数字符c,另外就是需要穿进去的文件名称。
我们尝试用追加的方式在上面的文件中写入一个字符串。
FILE*newfile=fopen("first.file","a");
char str[]={"chicken is beautiful!!"};
int i=0;
while(str[i]!='\0'){
fputc(str[i],newfile);
i++;
}
fclose(newfile);
结果如下:
字符串读写:fgets和fputs
这两个函数是读入字符串的,每次读入一行字符串或者添加一行字符串。
fgets函数是这样的,有三个参数,第一个是要存放字符串的数组的首地址,第二个是从文件流中读入的字符个数,第三个是文件的指针。
fgets函数在读取的时候,会把回车符也进行读取,因此缓冲区不会有回车符了。在使用stdin的时候,输出字符串的时候就不需要加上\n了。
我们先使用这个函数把一系列的字符串读进一个二维数组里面,看看输出的效果如何。
FILE*readbystr=fopen("firstfile.file","r");
char*newstrs[10];
int i=0;
while(!feof(readbystr)){ //feof函数是检查文件指针是否到达了末尾
fgets(*(newstrs+i),10,readbystr); //10代表的是每行字符最大读取10个字符
puts(*(newstrs+i));
i++;
}
fclose(readbystr);
这里我们注意到,输出的时候还多输出了一个换行符(中间空白处),我再修改文件的时候,每一行的确是输入了一个换行符。这是puts函数自己会换行决定的结果,相当于换了两次行。
当然解决的方法第一个是使用printf函数,因为他不会自己换行,%s后面不加\n即可。
第二个是自己检查有没有\n。 我是用的是strcspn函数,这个函数是查找一个字符串的首字母在另个字符串中出现的首位置。strcspn(char*str1,char*str2)str2是那个首字母的字符串,str1是被查找的字符串。返回的是被查到的字符的前面的子串的长度。比如第一个字符串为qwertyu,第二个字符串为wer,那么他会返回1,因为w的前面有一个字符,长度为1。所以我们可以这样操作,查找‘\n’的位置并把它替换成为‘\0’,这样就不会换两次行了。
while(!feof(readbystr)){
fgets(*(newstrs+i),10,readbystr);
newstrs[i][strcspn(newstrs[i],"\n")]='\0';//查找位置并进行替换操作
puts(*(newstrs+i));
i++;
}
fputs函数相当于就是把一行字符串打印到文件中。比较简单。
FILE*newfile=fopen("firstfile.file","a");
fputs("cxk wo ai ni\n",newfile);
fputs("kunkun!!!~~~",newfile);
fclose(newfile);
提醒大家一下,记得要用“a”,不能用“r”,但凡涉及到要对文件进行修改都要用a或者w。
格式化读写:fprintf,fscanf
fprintf相比于printf,就是多了一个打印的文件位置而已,printf打印到stdout(屏幕);同样,对fscanf,使用stdin(控制台)输入,和scanf的效果是一样的。
int num;
fscanf(stdin,"%d",&num);
fprintf(stdout,"%d\n",num);
前面第一个参数是文件的路径,后面和printf,scanf一样。
我们可以使用这两个函数对结构体进行数据输入和输出,并把他们保存到一个文件里面。这个可能会在写管理系统的时候用到。这里我写了两个函数来进行输入数据和输出数据到文件中,来看代码。
typedef struct student
{
char name[10];
int age;
int score;
}student;//这是一个学生成绩姓名年龄的结构体
void InputDataToFile(student array[],const char*filename,int arraynum){
int i=0;
FILE* fp=fopen(filename,"w");
for ( i = 0; i < arraynum; i++)
{
fprintf(fp,"%s %d %d\n",array[i].name,array[i].age,array[i].score);//将三项数据输入到文件当中,我先将数据存储在数组当中。
}
fclose(fp);
}
void GetDataFromFile(student array[], const char*filename){
FILE*fp=fopen(filename,"r");
int i=0;
while (fscanf(fp,"%s %d %d\n",array[i].name,&array[i].age,&array[i].score)!=EOF)//从文件中读取数据并将其存储在数组中。
{
printf("%s %d %d\n",array[i].name,array[i].age,array[i].score);//输出数据
i++;
}
fclose(fp);
}
在这两个函数中,需要传入的参数是文件名,结构体数组,向文件里输出还需要数组大小。
我有这样的感觉:之前用printf是输出,scanf是输入;但是在文件中,给人的感觉是:fprintf是输入,fscanf也是输入。其实本质上,fprintf只是把输出的地点换成了文件这个不可见的位置,输出之后相当于把数据留在了文件中;而fscanf则是把数据从文件中读出,再存储到变量当中。本质上只是从屏幕到文件的转变。
使用时需要注意的点是:你在fprintf用了怎样的格式去把数据输入到文件中,你就应当在fscanf中用怎样的格式去输出数据,因为fscanf是会读取类似于空格,\n,\t这样的字符的,这是fscanf于scanf不同的地方。
这是主函数的操作以及最后的输出,大家可以拿去验证一下,这两个函数也是可以直接用的~
int main(){
student array[3]={{"ww",19,100},{"qq",18,111},{"mm",20,99}};//初始化结构体数组
InputDataToFile(array,"newfile.file",3);
GetDataFromFile(array,"newfile.file");
return 0;
}
字节流读写:fwrite,fread
这两个函数是按照字节来进行读写的,使用的范围更加广泛。
先来看一下函数原型是什么样子的:
size_t fwrite
(const void *__restrict__ _Str,
size_t _Size,
size_t _Count,
FILE *__restrict__ _File)
下面来解释一下fwrite的每行的参数的含义
第一个,是一个空指针,指向你要向文件中输入的内容的首地址(注意,这里可以是任意的数据类型!)
第二个,代表的是你要输入多少长度;
第三个,代表输入的次数;
第四个,代表要输入的文件的指针。
那么其实fread的内部结构也是一样,也是四个参数,意思完全一样,第一个参数就是你要把数据读出来以后存储到的内存的首地址。
还是以上面的结构体数组为例,我们使用这两个函数来进行读写操作。
先给出第一种fwrite的方法:一个一个数据的读入。
student array[3]={{"zzh",19,100},{"ppc",18,111},{"cxk",20,99}};
FILE*fp=fopen("newfile.file","w");
for(int i=0;i<3;i++){
fwrite(&array[i],sizeof(student),1,fp);
}
fclose(fp); //把数据存储进入newfile里面。
FILE*fp1=fopen("newfile.file","r");
student readarray[3];
fread(&readarray[0],sizeof(student),1,fp1);
fprintf(stdout,"%s %d %d\n",readarray[0].name,readarray[0].age,readarray[0].score);//我是把他在屏幕上打印出来了
fclose(fp1);//从newfile中读取一个数据并把它存在新数组的元素readarray里面。
第二种方法是:一次性读入所有数组的元素,只需要把第三个参数——也就是读入几个改成数组的大小即可。
fwrite(array,sizeof(student),3,fp);
相当于是每次找到一个结构体的内存大小,然后写入三个即可。
因此我们使用这个函数可以对任何类型的文件进行存储,比如图片文件jpg,我们也可以把它用fread函数存储在一个整型,字符型等等的数组中,然后再用这个数组,用fwrite把它存放到另外一个jpg文件中,中途我们甚至还可以对数组中的元素进行修改,给图片加密,这很麻烦我就不再写代码来演示了。
不过当我们的文件很大的时候,就不能使用数组了,比如一个几十MB的文件,不能使用静态的数组,必须要使用动态内存申请!!
文件指针操作函数:ftell,fseek
ftell函数和fseek函数在我看来是对文件指针的量化和指针移动的显化,因为我们知道,在平常一般使用的时候每调用一次fopen函数,文件指针都会自动的移动数据类型的大小,这一过程不需要我们自己操作。因此这两个函数是对指针操作的一个具化。
ftell函数的参数只有文件指针一个,他返回的是文件指针现在的位置与文件开头位置的距离。
fseek函数的原型是:int fseek(FILE *_File, long _Offset, int _Origin);这个函数的作用是将指针从origin大小的位置移动offset大小个字节。其中origin在编译器里有三种位置:
分别代表了:当前位置,文件结尾位置,文件开始位置。如果操作后指针仍在文件的大小范围之内,则返回1,;否则返回0。
根据这两个函数,我们知道大文件的fread是需要动态申请内存的,因此我们需要得到文件的所占字节数,因此我们使用这两个函数来写一个文件大小读取函数。
long GetSizeOfFile(const char*filename){
FILE*fp=fopen(filename,"r");
fseek(fp,0,SEEK_END);//将指针移动从文件的末尾移动0个字节,还是末尾
long size=ftell(fp); //返回指针移动的字节数。
fclose(fp);
return size;
}
输出成功啦!
还想强调一点,读和写,这两个操作是分开的!把数据写进文件的时候的文件指针fp=fopen...,在后面从文件中读出的时候的已经是在文件末尾了!因为在这其中我们的函数会自动对指针进行移动的操作(前面所有函数,fgetc,fgets...).所以独读出(使用fputc,fputs)的时候得另外设置一个指针来使起回到文件开头哦。
以上就是文件的操作,一些函数的介绍与功能应用,希望对uu们有帮助~~
笔记整理不易,给个一键三连再走吧~~