一、为什么使用文件
在程序中完成数据处理,但是随着程序运行结束,运行结果也会随着运行结束而消失。这时就能把数据保存在文件中,使得数据永久化。
二、文件是什么
在学习文件之前,我们可以先了解了解什么是流。 在计算机中,对于文件、键盘、显示器等外部设备的输入输出操作都是通过流来进行的。
对于任何一个C程序,只要运行起来,就会默认打开3个流。 有以下3种标准流:
stdin ------- 标准输入流 -------- 用于读取从键盘中输入的数据 stdout ------- 标准输入流 -------- 用于写入数据至显示器界面 stderr ------- 标准错误流 -------- 用于写出错误至显示器界面
下面我们来谈谈什么是文件:
在程序设计中,按文件功能的角度来分类有两种:程序文件和数据文件。 程序文件:包括源程序文件(.c)、目标文件(.obj)、可执行程序(.exe)、 数据文件:程序运行中需要读取数据和输出内容的文件。
本文讨论的就是数据文件
三、文件的使用
1、文件指针
在程序我们应该怎样使用文件?
在计算机中,每个被使用的文件都会在内存中开辟一个相应的文件信息区,用来存放相关信息。这些信息一般以结构体的形式实现,是由系统声明的,取名为FILE。
下面我们可以创建一个FILE*的指针变量:
FILE* pf;
可以通过文件指针变量来找到与他关联的文件
2、文件的打开和关闭
在读写文件前,都应该先打开文件,在使用完毕之后就要关闭文件 ANSIC规定使用fopen函数来打开文件,fclose来关闭文件
// 打开文件 FILE * fopen ( const char * filename, const char * mode ); // 关闭文件 int fclosr ( FILE * stream );
文件打开方式如下:
打开方式 | 含义 |
---|
“r”(只读) | 以只读模式打开一个已存在的文件,若指定文件不存在,则出错 | “w”(只写) | 以只写模式建立一个文件,若指定文件不存在,则新建一个文件 | “a”(追加) | 以追加模式向文件末尾写入数据,若指定文件不存在,则新建一个文件 | “rb”(只读) | 以只读模式打开一个二进制文件,若指定文件不存在,则出错 | “wb”(只写) | 以只写模式建立一个二进制文件,若指定文件不存在,则新建一个文件 | “ab”(追加) | 以追加模式向二进制文件末尾写入数据,若指定文件不存在,则出错 | “r+”(读写) | 以更新(读写)模式打开一个文本文件,若指定文件不存在,则出错 | “w+”(读写) | 以更新(读写)模式建立一个新文件,若指定文件不存在,则新建一个文件 | “a+”(读写) | 以追加模式打开一个文件,在末尾进行读写,若指定文件不存在,则新建一个文件 | “rb+”(读写) | 以更新(读写)模式打开一个二进制文件,若指定文件不存在,则出错 | “wb+”(读写) | 以更新(读写)模式建立一个二进制文件,若指定文件不存在,则新建一个文件 | “ab+”(读写) | 以追加模式打开一个二进制文件,在末尾进行读写,若指定文件不存在,则新建一个文件 |
举个例子:
#include <stdio.h>
int main()
{
FILE* pf = fopen("text.txt", "w");
if (NULL == pf)
{
perror("fopen");
return 1;
}
fclose(pf);
pf = NULL;
return 0;
}
3、文件的读写
文件的读写分为两种:顺序读写和随机读写
3.1、顺序读写
下面我们先介绍文件是如何顺序读写? 文件在顺序读写时,会用以下函数进行读写:
函数 | 功能 |
---|
fgetc | 字符输入函数 | fputc | 字符输出函数 | fgets | 文本输入函数 | fputs | 文本输出函数 | fscanf | 格式化输入函数 | fprintf | 格式化输出函数 | fread | 二进制输入 | fwrite | 二进制输出 |
以上函数除了fread和fwrite函数只适用于文件,其余函数均可适用于所以输入输出流。
3.1.1、字符输入输出函数
int fputc( int character, FILE * stream ); int fgetc ( FILE * stream );
举个例子(写文件):
#include <stdio.h>
int main()
{
FILE* pf = fopen("text.txt", "w");
if (NULL == pf)
{
perror("fopen");
return 1;
}
int i = 0;
for (i = 0; i < 26; i++)
{
fputc('a' + i, pf);
}
fclose(pf);
pf = NULL;
return 0;
}
举个例子(读文件):
#include <stdio.h>
int main()
{
FILE* pf = fopen("text.txt", "r");
if (NULL == pf)
{
perror("fopen");
return 1;
}
int ch = fgetc(pf);
printf("%c\n", ch);
fclose(pf);
pf = NULL;
return 0;
}
文件中的数据: 读取结果:
3.1.2、文本输入输出函数
int fputs ( const char * str, FILE * stream ); char * fgets ( char * str, int num, FILE * stream );
举个例子(写文件):
fputs("hello\n", pf);
fputs("word", pf);
举个例子(读文件):
char arr[8] = "########";
fgets(arr, 5, pf);
printf("%s\n", arr);
文件中的数据: 读取结果: 代码若改为:
char arr[8] = "########";
fgets(arr, 8, pf);
printf("%s\n", arr);
结果为:
3.1.3、格式化输入输出函数
int fprintf ( FILE * stream, const char * format, … ); int fscanf ( FILE * stream, const char * format, … );
举个例子(写文件):
#include <stdio.h>
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = { "zhangsan",20,95.5f };
FILE* pf = fopen("text.txt", "w");
if (NULL == pf)
{
perror("fopen");
return 1;
}
fprintf(pf, "%s %d %f", s.name, s.age, s.score);
fclose(pf);
pf = NULL;
return 0;
}
举个例子(读文件):
#include <stdio.h>
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = { 0 };
FILE* pf = fopen("text.txt", "r");
if (NULL == pf)
{
perror("fopen");
return 1;
}
fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score));
printf("%s %d %f\n", s.name, s.age, s.score);
fclose(pf);
pf = NULL;
return 0;
}
文件中的数据: 读取结果:
3.1.4、二进制输入输出函数
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream ); size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
举个例子(写文件):
#include <stdio.h>
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = { "zhangsan",20,95.5f };
FILE* pf = fopen("text.txt", "wb");
if (NULL == pf)
{
perror("fopen");
return 1;
}
fwrite(&s, sizeof(s), 1, pf);
fclose(pf);
pf = NULL;
return 0;
}
由于是二进制写入,以文本形式显示,就会出现以上效果,但是以二进制读取,便会读取相应的结果。
代码如下(读文件):
#include <stdio.h>
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = { 0 };
FILE* pf = fopen("text.txt", "rb");
if (NULL == pf)
{
perror("fopen");
return 1;
}
fread(&s, sizeof(s), 1, pf);
printf("%s %d %f", s.name, s.age, s.score);
fclose(pf);
pf = NULL;
return 0;
}
3.1.5、几个函数的对比
scanf()等价于fgetc(stdin); printf()等价于fputc(ch,stdout);
举个例子:
#include <stdio.h>
int main()
{
char ch = fgetc(stdin);
fputc(ch, stdout);
return 0;
}
scanf:按照一定的格式从键盘输入数据 printf:按照一定的格式把数据打印(输出)到屏幕上 这两个函数适用于标准输入输出流的格式化的输入输出语句
fscanf:按照一定的格式从输入流(文件/stdin)输入数据 fprintf:按照一定的格式向输出流(文件/stdout)输出数据 这两个函数适用于所以的输入输出流的格式化输入输出语句
下面我们先举个例子了解 sprintf 和 sscanf 函数
int sprintf ( char * str, const char * format, … ); int sscanf ( const char * s, const char * format, …);
代码如下:
#include <stdio.h>
struct S
{
char name[20];
int age;
float score;
};
int main()
{
char buf[100] = { 0 };
struct S tmp = { 0 };
struct S s = { "zhangsan",20,95.5f };
sprintf(buf, "%s %d %f", s.name, s.age, s.score);
printf("%s\n", buf);
sscanf(buf, "%s %d %f", tmp.name, &(tmp.age), &(tmp.score));
printf("%s %d %f", tmp.name, tmp.age, tmp.score);
return 0;
}
sscanf:从字符串中按照一定的格式读取出格式化的数据 sprintf:把格式化的数据按照一定的格式转换成字符串
3.2、随机读写
3.2.1、fseek 根据文件指针的位置和偏移量来定位文件指针
int fseek ( FILE * stream, long int offset, int origin );
举个例子:
#include <stdio.h>
int main()
{
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fseek(pf, 3, SEEK_SET);
int ch = fgetc(pf);
printf("%c", ch);
fclose(pf);
pf = NULL;
return 0;
}
文件中的数据: 读取结果:
函数中的 origin 参数用作偏移量参考的位置。
有如下3种常量用作此函数参数:
常数 | 原始位置 |
---|
SEEK_SET | 从起始位置开始偏移 | SEEK_CUR | 从当前位置开始偏移 | SEEK_END | 从文件结尾向前偏移 |
3.2.2、ftell 返回文件指针相对于起始位置的偏移量
long int ftell ( FILE * stream );
举个例子:
#include <stdio.h>
int main()
{
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fseek(pf, 3, SEEK_SET);
int ch = fgetc(pf);
printf("%c\n", ch);
int pos = ftell(pf);
printf("%d", pos);
fclose(pf);
pf = NULL;
return 0;
}
3.2.3、rewind 让文件指针的位置回到文件的起始位置
void rewind ( FILE * stream );
4、文本文件和二进制文件
根据数据的组织形式,数据文件被称为文本文件和二进制文件
在文本文件中,数据是以字符序列的形式来表示的。 在二进制文件中,数据是以二进制位的形式来表示的。
以10000为例: 由此可见:
文本文件中字符数取决于数值位数。 二进制文件中字符数(字节数)不依赖于数值位数
5、文件读取结束的判定
fgetc:? 如果读取正常,会返回读取到的字符的ASCII值。 ? ? ? ???如果读取失败,返回EOF。 fgets:? 如果读取正常,返回的是存放读取到的数据的地址。 ? ? ? ???如果读取失败,返回NULL。 fscanf: 如果读取正常,返回的是格式串中指定的数据的个数。 ? ? ? ???如果读取失败,返回的是小于格式串中指定的数据的个数。
|