文件指针和文件信息区
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE.
FILE结构体大致是这样,每个编译器都不太相同,但大同小异
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
上面说的都不重要。我们只需要知道有这么一个文件信息区就可以了。
我们使用的时候不需要关注这些细节。我们只需要知道我们可以用文件指针来指向这块文件信息区就可以了。
FILE* pf

二进制文件和文本文件
二进制文件就是指文件里面的数据都是二进制。 文本文件就是指文件里面的数据都是由ASCII翻译而来。
举个例子: 在文件里面存放数字10000. 二进制文件存储的就是10000的十六进制,占用的内存是4个字节(一个整型) 文本文件就是把1变成字符1,0变成字符0,存放进文件里面。占用的内存是5个字节。(5个字符)
如何用vs查看二进制文件
先写一个二进制文件。
int main()
{
FILE* pf = fopen("C:\\Users\\86135\\Desktop\\test.txt", "wb");
if (pf == NULL)
{
perror("fopen:");
return -1;
}
int i = 10000;
fwrite(&i, sizeof(int), 1, pf);
fclose(pf);
pf = NULL;
return 0;
}
我们是看不懂的。  添加现有项,然后用二进制编辑器查看。  我们就可以看到这个信息。这是10000的十六进制形式。 
文件缓冲区
从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。  下面用一段代码来说明:
int main()
{
FILE* pf = fopen("test.txt", "w");
fputs("abcdef", pf);
printf("睡眠10秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");
Sleep(10000);
printf("刷新缓冲区\n");
fflush(pf);
printf("再睡眠10秒-此时,再次打开test.txt文件,文件有内容了\n");
Sleep(10000);
fclose(pf);
pf = NULL;
return 0;
}
刷新缓冲区会让缓冲区的内容写入磁盘里。
文件的打开和关闭
文件打开用fopen函数。 fopen函数会返回一个你想要打开的文件的指针,自己创建一个变量去接受就好了。
FILE* pf = fopen("文件名","打开方式");
文件关闭用fclose函数,传入对应文件的文件指针就可以了。
fclose(pf);
下面三个可以背一下,其他要用的时候查就好了。 很简单,不写例子了。
r:以只读的方式打开文本文件,文件必须存在;
w:以只写的方式打开文本文件,文件若存在则清空文件内容从文件头部开始写,若不存在则根据文件名创建新文件并只写打开;
a:以只写的方式打开文本文件,文件若存在则从文件尾部以追加的方式开始写,文件原来存在的内容不会清除(除了文件尾标志EOF),若不存在则根据文件名创建新文件并只写打开;
文件的顺序读写
大概有这么几个函数。
函数名 适用于 fgetc 所有输入流 fputc 所有输出流 fgets 所有输入流 fputs 所有输出流 fscanf 所有输入流 fprintf 所有输出流 fread 文件 fwrite 文件
在上面的几个函数中都出现了流(stream)这个概念,解释一下。 流是类的对象。 
fgetc
fgetc是从任意输入流读取字符。
注:fgetc的返回值是int。 
我们先测试从文件里面读取字符。
在桌面写了一个txt文件。 
注:在写文件路径的时候,如果只写一个斜杠的话有可能出现转义字符。比如\t等。再写一个斜杠防止转义就可以了。
 我们再测试从屏幕上读入字符。 代码如下:  stdin是标准输入流。其实就是从屏幕上读取字符。 
fputc
fputc和fgetc的用法是类似的。
向文件内写入字符   向标准输出流输出字符 
fgets

fgets的用处是从输入流里面得到字符串。 但是它的使用方法有些奇怪。
 如果你想得到5个字符,他只会返回4个字符给你,最后一个字符帮你变成斜杠0. 
fputs
fputs的用处是把字符串写到输出流。 
结果: 
fscanf
fscanf的用处是格式化读取数据
格式化读取数据的意思就是任意数据类型你都可以读取。和scanf一样。只不过fscanf可以读取文件里面的数据。
对比一下fscanf和scanf两个函数,发现没什么区别,就是fscanf要多传一个文件指针而已。  

struct S
{
char ch;
int i;
char name[20];
};
struct S s = { 0 };
fscanf(pf, "%c %d %s", &(s.ch), &(s.i), s.name);
printf("%c %d %s", s.ch, s.i, s.name);

fprintf
fprintf和fscanf的使用方法差不多。
struct S s = { 'a',1,"zhangsan" };
fprintf(pf,"%c %d %s",s.ch,s.i,s.name);
这样就可以往文件里面写你想要的东西了。
fwrite
fwrite是以二进制形式把内容写入文件里面。 
struct S
{
char ch;
int i;
char str[20];
};
int main()
{
FILE* pf = fopen("C:\\Users\\86135\\Desktop\\test.txt", "wb");
struct S s = { 'a',1,"zhangsan" };
fwrite(&s, sizeof(struct S), 1, pf);
return 0;
}
结果: 
fread
fread是以二进制形式读取文件内容 
struct S
{
char ch;
int i;
char str[20];
};
int main()
{
FILE* pf = fopen("C:\\Users\\86135\\Desktop\\test.txt", "rb");
struct S s = { 0 };
fread(&s, sizeof(struct S), 1, pf);
printf("%c %d %s", s.ch, s.i, s.str);
return 0;
}
结果: 
sscanf和sprintf
有这么一道题目,对比一下下面三个函数。
scanf/fscanf/sscanf
printf/fprintf/sprintf 前面两个函数就不说了。sscanf和sprintf说一下。这两个函数很有意思
sprintf函数可以将任意类型的数据转换成字符串形式。 
int main()
{
struct S s = { 'a',1,"zhangsan" };
char str[20] = { 0 };
sprintf(str, "%c %d %s", s.ch, s.i, s.str);
printf(str);
return 0;
}
这个时候已经是字符串了。 
sscanf的用法也类似。 
struct S s = { 'a',1,"zhangsan" };
char str[20] = { 0 };
sprintf(str, "%c %d %s", s.ch, s.i, s.str);
struct S s2 = { 0 };
sscanf(str, "%c %d %s",&(s2.ch),&(s2.i),(s2.str));
printf("%c %d %s", s2.ch, s2.i, s2.str);
文件的随机读写
文件的随机读写的意思就是可以调整文件指针的位置,进行任意位置的访问。
fseek
 offset是偏移量的意思,origin是现在的位置。库里面定义了几个宏,如下:  原先文件里面放着abcdef。现在用fseek让pf指针往后移动一个位置。原先fgetc得到的应该是a,现在是b了。
int main()
{
FILE* pf = fopen("C:\\Users\\86135\\Desktop\\test.txt", "rb");
fseek(pf, 1, SEEK_SET);
int ch = fgetc(pf);
printf("%c\n", ch);
return 0;
}
ftell
 ftell可以告诉你当前指针的offsetof是多少。
FILE* pf = fopen("C:\\Users\\86135\\Desktop\\test.txt", "rb");
fseek(pf, 3, SEEK_SET);
long offsetof = ftell(pf);
printf("%d\n", offsetof);
输出的是3.
rewind
rewind可以让文件指针重新回到文件开头。
FILE* pf = fopen("C:\\Users\\86135\\Desktop\\test.txt", "rb");
fseek(pf, 3, SEEK_SET);
long offsetof = ftell(pf);
printf("%d\n", offsetof);
rewind(pf);
offsetof = ftell(pf);
printf("%d", offsetof);

文件结束判定
ferror和feof
文件打开失败有几种可能,有可能是出现了错误,也有可能是遇到了文件结束符号eof。当文件打开失败的时候,我们要怎么判断是哪一个错误呢?
这时候我们就要使用ferror和feof了。如果ferror返回0,就是文件没有出错,即代表遇到了文件结束符号eof了。如果feof返回0,就是文件没有遇到eof,即代表文件出现了某些错误。
- 文本文件读取是否结束,判断返回值是否为EOF (fgetc),或者NULL(fgets)
例如: fgetc判断是否为EOF. fgets判断返回值是否为NULL. - 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
例如: fread判断返回值是否小于实际要读的个数。
下面这段代码能很好的说明问题。
int main()
{
int c;
FILE* fp = fopen("test.txt", "r");
if(!fp) {
perror("File opening failed");
return EXIT_FAILURE;
}
while ((c = fgetc(fp)) != EOF)
{
putchar(c);
}
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
}
|