文章目录
(一)文件
什么是文件?
为了便于理解,可以把文件看作是一个结构体,里面存放着数据,这些数据就反映了文件的属性。后面将要提到的文件指针fp,实际上就指向这个结构体,当我们要调用一个文件的时候,常常会用到这个指针。
如上所说,文件是一个数据的集合,它常常被储存在外部设备上(如U盘或硬盘),当需要调用的时候,系统得到调用的指令,把目标文件临时取出,放入内存供使用。
而在系统中,文件结构体已经被定义了,如下:
#include<stdio.h>
struct iobuf{
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf
int _bufsiz;
char *_tmpfname;
};
在这些成员当中,只需要理解char*_ptr
,即下面的位置指针。
(二)文件指针和位置指针
1.位置指针
位置指针_ptr是文件结构体中的成员之一。当某个文件要被调用,系统就把它从外部设备临时调到内存缓冲区中,而位置指针_ptr就指向了缓冲区下一个待操作的字节,操作一个就往后移动一位,直到末尾。
很容易看出,_ptr的指向在发生变化。
2.文件指针
#include<stdio.h>
FILE *fp;//定义了一个指向文件的指针
文件指针就是上述的fp,它指向该文件(文件的结构体),是文件类型的指针,因此要用 FILE*fp
来表示。
只要是在同一个文件上操作,fp的指向都不会发生变化。
(三)打开文件fopen()函数
1.函数原型:
#include<stdio.h>
FILE *fp;
fp = FILE *fopen(const char *path,const char *mode);
依次输入(“文件路径”,“使用方式”)
返回值是一个文件指针,所以可以直接把它的返回值赋给fp。
2.文件路径
如果只写出文件名,默认目标文件的路径为当前程序文件所在的路径,如果使用文件本身的名字,需要加上双引号。例如:
fopen("test.txt","r");//只能打开test.text这一种文件
如果要用变量名来表示文件的名字,(只有在这种情况下,才可以给目标文件取别名)就把变量名作为文件的别名。例如:
char name[10];//可以根据需求打开不同的文件
fgets(name,stdin);//用户输入文件名,此处用name替代
fopen(name,"r");
如果目标文件和程序文件不在同一个路径下,需要明确写出目标文件的路径。例如:
fopen("D:\\USERS\\COMPUTER\\test.txt","r");
注意!这个时候,必须要用双引号括出文件的路径,言外之意,这个时候不能用变量名表示!一旦表示,就会变成一个字符串,失去了变量名的意义。
3.文件的使用方式
一共包括了 r,w,a,+,b这几种字符,下面是他们各自的含义。
- r :只读,且目标文件不存在时不会自动创建
- r+ :可读可写,且目标文件不存在时不会自动创建
- w :只写,且目标文件不存在时会自动创建
- w+ :可写可读,且目标文件不存在时会自动创建
- a :追加,且目标文件不存在时会自动创建
- a+ :可追加且可读,且目标文件不存在时会自动创建
注:(只要在上述字母后写一个b,即打开二进制文件)
注意:
(1)当以w或a的形式打开一个不存在的文件,这个时候系统会自动创建一个,如果文件名不加后缀(txt),系统会默认创建一个二进制文件。
(2)当目标文件存在,又以w的形式打开,那么原来的内容会被全部删除,只能新写入内容。
(3)当目标文件存在,又以a的形式打开,那么新写入的内容就会接在原来的内容后面。
(4)当目标文件不存在,以a打开等价于以w打开。
4.附上一个打开文件的小栗子
#include<stdio.h>
int main()
{
FILE *fp;
fp = fopen("example.txt","w");
if(fp!=NULL)
printf("文件打开成功!");
else
printf("文件打开失败!");
return 0;
}
(四)关闭文件fclose()函数
1.函数原型
#include<stdio.h>
int fclose(FILE *fp);
打开了一个文件一定不要忘记关闭它!否则会一直在内存缓冲区中占用内存空间。
fclose()函数比较简单,只需要输入所需要关闭文件的文件指针fp即可。
2.小栗子
把上面打开文件的函数关闭即可。
#include<stdio.h>
#include<stdlib.h>//为了使用exit
int main()
{
FILE *fp;
fp = fopen("example.txt","w");
if(fp!=NULL)
{
printf("文件打开成功!");
else
{
printf("文件打开失败!");
exit(-1);//程序异常终止
}
fclose(fp);
return 0;
}
(五)字符读取函数fgetc()
1.原型
#include<stdio.h>
int fgetc(FILE *stream);//stream是文件指针
2.作用
从目标文件中读取一个字符,如果读取到文件的末尾,就返回EOF(-1)。
对于其返回值:
返回的是读取到的字节,因此可以利用这一点写出如下的表达:
printf("%c",fgetc(fp));
-
如果是一个文本文件,返回的是该字符的ASCLL码,ASCLL码的范围是0~255,这时虽然可以以是否返回
EOF
作为判断到达末尾的标准,但是细想,如果程序出现错误,也会返回EOF,那么,当返回了一个EOF的时候,到底是到达了末尾还是程序出现了错误呢。所以,即使是一个文本文件,还是要用下面讲的feof()来判断是否到达文件末尾。 -
如果是一个二进制文件,返回的是相应的0、1、-1三种情况,这时,不能以是否返回EOF来作为判断标准!!!此时需要用到
feof()
函数专门来判断是否到达文件的末尾。
3.feof()函数——专用于判断是否到达文件末尾
eof=end of file.
原型
#include<stdio.h>
int feof(FILE *stream);
fp指向文件结构体,位置指针_ptr一个字节一个字节向后移动,如果到达末尾,返回非0。
feof()的两种用法
对于文本文件,现在的首要目的是判断函数返回EOF是遇到了错误还是到达了文件末尾。
第一种
#include<stdio.h>
#include<stdlib.h>
int main()//判断一个文本文件返回EOF是遇到了错误,还是到达了末尾
{
FILE *fp;
fp = fopen("example.txt","r");
if(fp!=NULL)
{
printf("文件打开成功!\n");
while(fgetc(fp)!=EOF)
{
printf("文件浏览成功!\n");
}
if(!feof(fp))//返回非零则到达末尾
{
printf("文件没有读到末尾!遇到了错误!\n");
}
else
{
printf("文件已经读到了末尾!\n");
}
}
else
{
printf("文件打开失败!");
exit(-1);
}
fclose(fp);
return 0;
}
第二种
#include<stdio.h>
#include<stdlib.h>
int main()//判断一个文本文件返回EOF是遇到了错误,还是到达了末尾
{
FILE *fp;
fp = fopen("example.txt","r");
if(fp!=NULL)
{
printf("文件打开成功!\n");
int i=0,j=0;
while(fgetc(fp)!=EOF)
{
i++;
}
rewind(fp);
while(!feof(fp))//到达末尾则返回非零
{
j++;
fgetc(fp);
}
printf("浏览循环了%d次,验证是否到达末尾循环了%d次\n",i,j);
}
else
{
printf("文件打开失败!");
exit(-1);
}
fclose(fp);
return 0;
}
输出的结果是:浏览循环了12次,验证是否到达末尾循环了13次
究其原因,无非就是feof再判断的时候需要把末尾的EOF作为循环内容再循环一次。而fgetc一读取到EOF就退出循环了。
因此,有时候使用第二种方法会对程序造成影响,这个时候就需要我们做选择了。当然,第一种用fgetc和feof搭配着使用是最好的。
然而,我在写第二种的时候出现了一点麻烦,下面是我最开始的版本,里面有两个问题。
问题就出在这个循环。
while(!feof(fp))//到达末尾则返回非零
{
j++;
}
把正确版本的这个部分放在下面供比较:
rewind(fp);
while(!feof(fp))//到达末尾则返回非零
{
j++;
fgetc(fp);
}
错误的版本比正常的少了rewind 和循环中的fgetc(fp)。
rewind()是把光标返回文件开头的意思,后面会详细讲到。
这里需要注意!!
feof只能用做判断,它在判断之后并不会自动读取下一个字节!!所以这时候需要借助fgetc()来移动光标。
所以,当我们要用feof作判断的时候,一定要先读取,再判断。
内容有点多,下一篇文章继续…