顺序读写数据文件
- 在顺序写时,先写入的数据存放在文件中前面,后写入的数据存放在文件中后面。
- 在顺序读时,先读文件中前面的数据,后读文件中后面的数据。
- 对顺序读写来说,对文件读写数据的顺序和数据文件中的物理顺序是一致的。
读写一个字符的函数
fgetc()函数
- 原型:
int fgetc( FILE * fp );
- 调用形式:
fgetc(fp)
- 作用:从 fp 所指向的输入文件中读取一个字符。
- 返回值:成功则返回的是读取的字符,发生错误则返回 EOF(即-1)。
- 原型:
fputc()函数
- 原型:
int fputc( int c, FILE *fp );
- 调用形式:
fputc(c,fp)
- 功能:把参数 c 的字符值写入到 fp 所指向的输出流(文件)中。
- 返回值:写入成功,它会返回它会返回写入的字符(一个非负值),发生错误,则会返回 EOF(即-1)。
- 原型:
- 例如:
从一个磁盘文本文件顺序读入字符并在屏幕上显示出来。
#include <stdio.h>
#include <string.h>
void main() {
char c;
FILE* fp = NULL;
fopen_s(&fp, "a.txt", "r");
c = fgetc(fp);
while (c != EOF) {
putchar(c);
c = fgetc(fp);
}
fclose(fp);
}
feof()函数
- 调用方式:
feof(fp)
- 功能:对于二进制文件读取时判断是否结束。
- 返回值:如果是文件结束,函数feof()值为1(真);否则为0(假)。
- 调用方式:
- 例如:
从一个磁盘二进制文件顺序读入字符并在屏幕上显示出来。
#include <stdio.h>
#include <string.h>
void main() {
char c;
FILE* fp = NULL;
fopen_s(&fp, "a", "r");
c = fgetc(fp);
while (!feof(fp)) {
putchar(c);
c = fgetc(fp);
}
fclose(fp);
}
例子
从键盘输入一些字符,逐个把它们送到磁盘上去直到用户输入一个“#”为止。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#pragma warning (disable:4996)
void main() {
char c,filename[10];
FILE *fp = NULL;
printf("请输入所用的文件名:");
scanf("%s", filename);
if ((fp = fopen(filename, "w")) == NULL) {
printf("无法打开此文件\n");
exit(0);
}
c = getchar(); // 接收执行scanf时最后输入的回车符
// 如果注释掉,文件中会首先换行,然后再输入的字符串
printf("请输入一个字符串(以#结束)");
c = getchar(); // 第一个输入的字符被赋给变量c
while (c!='#') {
fputc(c, fp);
putchar(c); // 字符被输出到显示器
c = getchar();
}
putchar(10); // 向屏幕输出一个换行符
fclose(fp);
}
将一个磁盘文件中的信息复制到另一个磁盘文件中。今要求建立的a.txt文件中的内容复制到另一个磁盘文件b.txt中。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#pragma warning (disable:4996)
void main() {
char c=NULL,infile[10],outfile[10];
FILE *in = NULL;
FILE *out = NULL;
printf("请输入读取的文件名:");
scanf("%s", infile);
printf("请输入输出的文件名:");
scanf("%s", outfile);
if ((in = fopen(infile, "r")) == NULL) {
printf("无法打开此文件\n");
exit(0);
}
if ((out = fopen(outfile, "w")) == NULL) {
printf("无法打开此文件\n");
exit(0);
}
while (c!=EOF) {
c= fgetc(in);
fputc(c, out);
putchar(c); // 字符被输出到显示器
}
putchar(10); // 向屏幕输出一个换行符
fclose(in);
fclose(out);
}
读写一个字符串的函数
fgets()函数
- 原型:
char *fgets( char *buf, int n, FILE *fp );
- 调用格式:
char *fgets( str, n, fp );
- 功能:从 fp 所指向的输入流(文件)中读取长度为(n - 1)的字符串存放到字符数组str中,并在最后追加一个 null 字符(即'\0')来终止字符串。
- 返回值:读成功返回地址str ,失败则返回NULL
- 原型:
fputs()函数
- 原型:
int fputs( const char *s, FILE *fp );
- 调用格式:
int fputs( str, fp );
- 功能:str所指向的字符串写到文件指针变量fp所指向的文件中。
- 返回值:如果写入成功,则返回写出的字符个数(一个非负值),如果发生错误,则返回EOF(即-1)。
- 原型:
说明:
fgets(str,n,fp);
中 n 是要求得到的字符个数,但实际上只读 n-1 个字符,然后在最后加一个\0
字符,
这样得到的字符串共有n个字符,把它们放到字符数组str中。- fgets()函数,如果在读完 n-1 个字符之前就遇到一个换行符
\n
或文件的末尾EOF
,
则读入结束,则只会返回读取到的字符,包括换行符。 - fputs函数中第一个参数可以是字符串常量、字符数组名或字符型指针。
- fputs()函数字符串末尾的
\0
不输出。
例子
从键盘读入若千个字符串,然后把字符串送到磁盘文件中保存。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#pragma warning (disable:4996)
void main() {
char str[3][10];
int i;
FILE *fp = NULL;
printf("请输入要写入的字符串:\n");
for (i = 0; i < 3; i++) {
gets(str[i]);
}
if ((fp = fopen("a.txt", "w")) == NULL) {
printf("无法打开此文件\n");
exit(0);
}
for (i = 0; i < 3; i++) {
fputs(str[i], fp);
fputs("\n", fp);
printf("写入成功:");
printf("%s\n", str[i]);
}
fclose(fp);
}
读取文件中的字符串,打印到控制台
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#pragma warning (disable:4996)
void main() {
char str[3][10];
int i;
FILE *fp = NULL;
if ((fp = fopen("a.txt", "r")) == NULL) {
printf("无法打开此文件\n");
exit(0);
}
for (i = 0; i < 3; i++) {
fgets(str[i],10,fp);
printf("%s", str[i]);
}
fclose(fp);
}
用格式化的方式读写文件
一般调用方式为:
fprintf(文件指针, 格式字符串, 输出表列);
- 例如:
fprintf(fp,"%d,%f",i,f);
fscanf(文件指针, 格式字符串, 输入表列);
- 例如:
fscanf(fp,"%d,%f",&i,&f);
用fprintf和fscanf对磁盘文件读写,使用方便,容易理解,
但是由于在输入时要将ASCII码转换为二进制形式,在输出时又要将二进制形式转换符,花费时间比较多。- 因此在内存与磁盘频繁交换的情况下,最好不要用 fprintf 和 fscanf 函数,可以用fread 和 fwrite 函数。
二进制 I/O 函数
- fread函数
- 原型:
size_t fread(void *buffer, size_t size, size_t count, FILE *a_file);
- 返回值:如果读取成功,则返回读的块数,失败,则返回0 。
- fwrite函数
- 原型:
size_t fwrite(const void *buffer, size_t size, size_t count, FILE *a_file);
- 返回值:如果写入成功,则返回写的块数,失败,则返回0 。
- 这两个函数都是用于存储块的读写 - 通常是数组或结构体。
例子:
从键盘输入5个学生的有关数据,然后把它们转存到磁盘文件上去,读取并打印到控制台。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#pragma warning (disable:4996)
#define SIZE 5
struct Student {
int num;
char name[10];
int age;
char addr[15];
}stu[SIZE];
void main() {
void save();
void reads();
int i;
printf("请输入学生数据:\n");
for (i = 0; i < SIZE; i++) {
scanf("%d %s %d %s", &stu[i].num, stu[i].name, &stu[i].age, stu[i].addr);
}
save();
printf("学生信息:\n");
reads();
}
void save() {
int i;
FILE *fp = NULL;
if ((fp = fopen("stu.dat", "wb")) == NULL) {
printf("无法打开此文件\n");
return;
}
for (i = 0; i < SIZE; i++) {
if (fwrite(&stu[i], sizeof(struct Student), 1, fp) != 1) {
printf("写入文件错误\n");
}
}
fclose(fp);
}
void reads() {
int i;
FILE *fp = NULL;
if ((fp = fopen("stu.dat", "rb")) == NULL) {
printf("无法打开此文件\n");
return;
}
for (i = 0; i < SIZE; i++) {
fread(&stu[i], sizeof(struct Student), 1, fp);
printf("%4d %-10s %4d %-15s\n", stu[i].num, stu[i].name, stu[i].age, stu[i].addr);
}
fclose(fp);
}
从已有的二进制文件“stu2.dat”中,读入数据并输出到“stu.dat”文件中
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#pragma warning (disable:4996)
#define SIZE 7
struct Student {
int num;
char name[10];
int age;
char addr[15];
}stu[SIZE];
void main() {
void load();
void save();
void reads();
load();
save();
printf("学生信息:\n");
reads();
}
void save() {
int i;
FILE *fp = NULL;
if ((fp = fopen("stu.dat", "wb")) == NULL) {
printf("无法打开此文件\n");
return;
}
for (i = 0; i < SIZE; i++) {
if (fwrite(&stu[i], sizeof(struct Student), 1, fp) != 1) {
printf("写入文件错误\n");
}
}
fclose(fp);
}
void reads() {
int i;
FILE *fp = NULL;
if ((fp = fopen("stu.dat", "rb")) == NULL) {
printf("无法打开此文件\n");
return;
}
for (i = 0; i < SIZE; i++) {
fread(&stu[i], sizeof(struct Student), 1, fp);
printf("%4d %-10s %4d %-15s\n", stu[i].num, stu[i].name, stu[i].age, stu[i].addr);
}
fclose(fp);
}
void load() {
int i;
FILE *fp = NULL;
if ((fp = fopen("stu2.dat", "rb")) == NULL) {
printf("无法打开此文件\n");
return;
}
for (i = 0; i < SIZE; i++) {
if (fread(&stu[i], sizeof(struct Student), 1, fp) != 1) {
if (feof(fp)) {
fclose(fp);
return;
}
printf("无法打开此文件\n");
}
}
fclose(fp);
}
其它读写函数
- putw 和 getw 函数
- 作用:以二进制形式,对磁盘文件读写一个int型的整数,2个字节。
- 返回值:成功,则返回所写的整数值;失败,则返回EOF
- 例如:
putw(10, fp);
i=getw(fp);
随机读写数据文件
- 对文件进行顺序读写比较容易理解,也容易操作,但有时效率不高。
文件位置标记
- 为了对读写进行控制,系统为每个文件设置了一个文件读写位置标记(简称文件标记),
用来指示“接下来要读写的下一个字符的位置”。 - 一般情况下,在对字符文件进行顺序读写时,文件标记指向文件开头,进行读的操作时,就读第一个字符,然后文件标记向后移动一个位置,在下一次读操作时,将位置标记指向第二个字符读入,以此类推直到遇文件尾结束。
- 随机读写可以在任何位置读取和写入数据
文件位置标记的定位
- 将文件的指针指向文件的开头,进行文件操作
- rewind函数
- 函数原型:
void rewind(FILE *fp);
- 功能:重置文件位置指针到文件开头。
- 返回值:无
-例如:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#pragma warning (disable:4996)
void main(){
FILE *fp1, *fp2;
fp1 = fopen("file1.dat", "r");
fp2 = fopen("file2.dat", "w");
while (!feof(fp1)) {
putchar(getc(fp1));
}
putchar(10);
rewind(fp1);
while (!feof(fp1)) {
putc(getc(fp1), fp2);
}
fclose(fp1);
fclose(fp2);
}
- 可以强制使文件标记指向指定的位置
- fseek函数
- 调用形式:
fseek(文件类型指针,位移量,起始点)
- 起始点:0代表“文件开始位置(SEEK_SET)”,1为“文件当前位置(SEEK_CUR)”,2代表“文件的末尾(SEEK_END)”。
- 位移量指以起始点为基点,向前移动的字节数。位移量应是long型数据(在数字的末尾加一个字母L)。
- fseek函数一般用于二进制文件。
- 例如:
fseek(fp,100L,0); // 将位置指针移动到离文件头100个字节处
fseek(fp,50L,1); // 离当前位置50个字节处
fseek(fp,-10L,2); // 从文件末尾处向后退10个字节
- ftell函数
- 由于文件中的文件位置标记经常移动,人们往往不容易知道其当前位置,所以常用ftell函数得到当前位置(相对于文件开头的位移量来表示)。
如果调用函数时出错(如不存在fp指向的文件),ftell函数返回值为-1L。 例如:
i=ftell(fp);
if(i==-1L){printf("error\n");}
例子
在磁盘文件上存有10个学生的数据要求将第1,3,5,7,9个学生数据输入计算机,并在控制台显示出来。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#pragma warning (disable:4996)
struct Student {
int num;
char name[10];
int age;
}stu[10];
void main() {
FILE *fp;
int i;
if ((fp = fopen("stu.dat", "rb")) == NULL) {
printf("打开文件失败");
exit(0);
}
for (i = 0; i < 10; i+=2) {
fseek(fp, i * sizeof(struct Student), 0);
fread(&stu[i], sizeof(struct Student), 1, fp);
printf("%4d %-10s %4d\n", stu[i].num, stu[i].name, stu[i].age);
}
fclose(fp);
}
文件读写的出错检测
ferror函数
- 一般调用形式:
ferror(fp);
- 返回值:如果为0,表示未出错,否则表示出错。
- 一般调用形式:
- 每次调用输入输出函数,都产生新的ferror函数值,因此调用输入输出函数后立即检查。
- 调用fopen时,ferror的初始值自动置为0。
clearerr函数
- 作用是使文件错误标志和文件结束标志置为0。
- 调用一个输入输出函数时出现错误(ferror值为非零值),立即调用clearerr(fp),
使ferror(fp)值变0,以便再进行下一次检测 - 只要出现文件读写错误标志,它就一直保留,直到对同一文件调用 clearerr 函数或 rewind 函数,或任何其他一个输入输出函数
总结
- 在使用文件时,首先要定义一个文件指针:
FILE *fp;
然后通过该指针来操作相应的文件; - 通过fopen这个函数,使文件指针fp和相应的文件建立了联系,通过fclose函数将切断fp和文件的联系;
- 如果以一次一个字符的方式处理文件,需要用 fgetc 或者 fputc 函数;
- 如果以一次一行的方式处理文件,可以用 fgets 或者 fputs 函数;
- 如果以一次一个结构体的方式处理文件,可以用 fread 和 fwrite 函数(多为二进制文件);
评论