文件
- 程序文件(程序代码)
- 数据文件(电脑中普遍用于存储各种东西的文件)
- “文件”一般是 指储存再外部介质上数据的集合。
- 操作系统是以文件为单位对数据进行管理的。
- 如果想找存放在外部介质上的数据,必须先按文件名找到所指定的文件,然后再从文件中读取数据。
- 要向外部介质上储存数据也必须先建立一个文件(以文件名作为标志),才能向它输出数据。
流
输入输出是数据传送的过程,数据如流水一样从一处留下另一处,因此常将输入输出形象地称为流,即数据流。流表示了信息从源到目的端的流动。
文件名
一个文件有一个唯一的文件标识。它由3部分组成:文件路径,文件名主干,文件后缀。
例如D:\CC\temp\filel.da为一个文件标识
它常被称为文件名。注意要和文件名主干相区别,前面的例子中整体为文件名,而filel是文件名主干,我们通常自己命名的部分就是文件名主干。
文件的分类
- ASCII文件(文本文件)
- 二进制文件(映像文件)
其他与文件操作相关的简单概念名词
文件缓冲区
文件类型指针
文件操作相关函数
打开用fopen函数,关闭用fclose函数。
读取一个字符用fgetc函数,读入一个字符用fputc函数。
读取一个字符串用fgets函数,读入一个字符串用fputs函数。
检测是否读完了文件用feof函数。
格式化方式读写文本文件用fprintf函数和fscanf函数。
二进制方式读写文本文件用fread函数和fwrite函数。
使文件位置标记指向开头用rewind函数,改变文件位置标记用fseek函数。
测定文件当前位置标记用ftell函数。
排查输入输出错误用feeror函数,排查文件出错标志和结束标志用clearerr函数。
带b就是与二进制文件相关。
--------------------------------------------------------------------------------------------------------------------------------
分别介绍
fopen函数
FILE *fopen(const char *filename, const char *mode) 使用给定的文件使用方式 mode 打开 filename 所指向的文件。
FILE *fopen(const char *filename, const char *mode)
- filename -- 这是 C 字符串,包含了要打开的文件名称。
- mode -- 这是 C 字符串,包含了文件使用方式。
fclose函数
int fclose(FILE *stream) 关闭流 stream。刷新所有的缓冲区。
- stream -- 这是指向 FILE 对象的指针(即文件指针),该 FILE 对象指定了要被关闭的流。
如果流成功关闭,则该方法返回零。如果失败,则返回 EOF。
fgetc函数
int fgetc(FILE *stream) 从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动。
该函数以无符号 char 强制转换为 int 的形式返回读取的字符,如果到达文件末尾或发生读错误,则返回 EOF。
fputc函数
int fputc(int char, FILE *stream) 把参数 char 指定的字符(一个无符号字符)写入到指定的流 stream 中,并把位置标识符往前移动。
- char -- 这是要被写入的字符。该字符以其对应的 int 值进行传递。
如果没有发生错误,则返回被写入的字符。如果发生错误,则返回 EOF,并设置错误标识符。
fgets函数
char *fgets(char *str, int n, FILE *stream) 从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。
- str -- 这是指向一个字符数组的指针,该数组存储了要读取的字符串。
- n -- 这是要读取的最大字符数(包括最后的空字符)。通常是使用以 str 传递的数组长度。
如果成功,该函数返回相同的 str 参数。如果到达文件末尾或者没有读取到任何字符,str 的内容保持不变,并返回一个空指针,如果发生错误,返回一个空指针。
fputs函数
int fputs(const char *str, FILE *stream) 把字符串写入到指定的流 stream 中,但不包括空字符。
- str -- 这是一个数组,包含了要写入的以空字符终止的字符序列。
该函数返回一个非负值,如果发生错误则返回 EOF。
feof函数
int feof(FILE *stream) 测试给定流 stream 的文件结束标识符。
当设置了与流关联的文件结束标识符时,该函数返回一个非零值,否则返回零。
fprintf函数
int fprintf(FILE *stream, const char *format, ...) 发送格式化输出到流 stream 中。
- format -- 这是 C 字符串,包含了要被写入到流 stream 中的文本
如果成功,则返回写入的字符总数,否则返回一个负数。
fscanf函数
int fscanf(FILE *stream, const char *format, ...) 从流 stream 读取格式化输入。
如果成功,该函数返回成功匹配和赋值的个数。如果到达文件末尾或发生读错误,则返回 EOF。
fread函数
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
从给定流 stream 读取数据到 ptr 所指向的数组中。
- ptr -- 这是指向带有最小尺寸 size*nmemb 字节的内存块的指针。
- size -- 这是要读取的每个元素的大小,以字节为单位。
- nmemb -- 这是元素的个数,每个元素的大小为 size 字节。
成功读取的元素总数会以 size_t 对象返回,size_t 对象是一个整型数据类型。如果总数与 nmemb 参数不同,则可能发生了一个错误或者到达了文件末尾。
fwrite函数
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
把 ptr 所指向的数组中的数据写入到给定流 stream 中。
- ptr -- 这是指向要被写入的元素数组的指针。
- size -- 这是要被写入的每个元素的大小,以字节为单位。
- nmemb -- 这是元素的个数,每个元素的大小为 size 字节。
如果成功,该函数返回一个 size_t 对象,表示元素的总数,该对象是一个整型数据类型。如果该数字与 nmemb 参数不同,则会显示一个错误。
rewind函数
void rewind(FILE *stream) 设置文件位置标记为给定流 stream 的文件的开头。
该函数不返回任何值。
fseek函数
int fseek(FILE *stream, long int offset, int whence) 设置流 stream 的文件位置标记为给定的偏移 offset,参数 offset 意味着从给定的 whence 位置查找的字节数。
- offset -- 这是相对 whence 的偏移量,以字节为单位。
- whence -- 这是表示开始添加偏移 offset 的位置。它一般指定为下列常量(0,1,2)之一:
起始点 | 名字 | 用数字表示 |
---|---|---|
文件开头位置 | SEEK_SET | 0 |
文件当前位置 | SEEK_CUR | 1 |
文件末尾位置 | SEEK_END | 2 |
如果成功,则该函数返回零,否则返回非零值
ftell函数
long int ftell(FILE *stream) 返回给定流 stream 的当前文件位置
该函数返回位置标识符的当前值。如果发生错误,则返回 -1L,全局变量 errno 被设置为一个正值
feeror函数
int ferror(FILE *stream) 测试给定流 stream 的错误标识符。
如果设置了与流关联的错误标识符,该函数返回一个非零值,否则返回一个零值。
clearerr函数
void clearerr(FILE *stream) 清除给定流 stream 的文件结束和错误标识符。
这不会失败,且不会设置外部变量 errno,但是如果它检测到它的参数不是一个有效的流,则返回 -1,并设置 errno 为 EBADF。
一些实际应用
1.从键盘输入一些字符,并把它们送到磁盘上去,直到用户输入一个“#“为止。
用fgetc函数从键盘逐个输入字符,然后用fgetc函数写到磁盘文件上即可。
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *fp;
char ch,filename[100];
printf("请输入所用的文件名:");
scanf("%s",filename);
getchar();
if((fp=fopen(filename,"w"))==NULL)
{
printf("cannot open file\n");
exit(0);
}
printf("请输入一个准备储存到磁盘发字符串(以#结束):");
ch=getchar();//接收从键盘输入的第一个字符
while(ch != '#')
{
fputc(ch,fp);
putchar(ch);
ch=getchar();//再接受从键盘输入的一个字符
}
fclose(fp);
putchar(10);//向屏幕输出一个换行符
return 0;
}
2.将一个磁盘文件中的信息复制到另一个磁盘文件中。今要求将上例建立的file1.dat文件中的内容复制到另一个磁盘filel.2.dat中。
从file1.dat文件中逐个读入字符,然后逐个输出到filel.2.dat中。
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *in,*out;
char ch,infile[100],outfile[100];
printf("请输入读入文件名字:");
scanf("%s",infile);
printf("请输入读出文件名字:");
scanf("%s",outfile);
if((in=fopen(infile,"r"))==NULL)
{
printf("cannot open file\n");
exit(0);
}
if((out=fopen(outfile,"w"))==NULL)
{
printf("cannot open file\n");
exit(0);
}
ch=fgetc(in);
while(!feof(in))
{
fputc(ch,out);
putchar(ch);
ch=fgetc(in);
}
putchar(10);
fclose(in);
fclose(out);
return 0;
}
3.从键盘读入若干个字符串,对它们按字母大小的顺序排序,然后把排好序的字符串送到磁盘文件中保存。(假设已经建立了文件E:\string.txt)
- 从键盘读入n个字符串,存放在一个二维字符数组中,每个一维数组存放一个字符串
- 对字符数组中的n个字符串按字母顺序排序,排好序的字符串仍放在字符数组中
- 将字符数组中的字符串顺序输出
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
FILE *fp;
int i,j,k,n=3;
char str[n][10],temp[10];
printf("Enter strings:\n");
for(i=0;i<n;i++)
gets(str[i]);
for(i=0;i<n-1;i++)//选择排序法对字符串排序
{
k=i;
for(j=i+1;j<n;j++)
if(strcmp(str[k],str[j])>0)k=j;
if(k!=i)
{
strcpy(temp,str[i]);
strcpy(str[i],str[k]);
strcpy(str[k],temp);
}
}
if((fp=fopen("E:\\string.txt","w"))==NULL)//注意转义字符
{
printf("cannot open file\n");
exit(0);
}
printf("\nThe new sequence:\n");
for(i=0;i<n;i++)
{
fputs(str[i],fp);
fputs("\n",fp);
printf("%s\n",str[i]);
}
return 0;
}
编写以下程序,可以从文件string.txt中读回字符串,并在屏幕上显示
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *fp;
char str[3][10];
int i=0;
if((fp=fopen("E:\\string.txt","r"))==NULL)
{
printf("cannot open file\n");
exit(0);
}
while(fgets(str[i],10,fp)!=NULL)
{
printf("%s",str[i]);
i++;
}
fclose(fp);
return 0;
}
4.从键盘输入10个学生的有关数据,然后把它们转存到磁盘文件上去。
定义一个有10个元素的结构体数组,用来存放10个学生的数据。从main函数输入10个学生的数据,用save函数实现向磁盘输出学生数据。用fwrite函数一次输出一个学生的数据。
假设输入数据为
Zhang 1001 19 room_101
Sun 1002 20 room_102
Tan 1003 21 room_103
Lin 1004 20 room_104
Wu 1005 18 room_105
Huang 1006 19 room_106
Zhan 1007 18 room_107
Luo 1008 19 room_108
Ke 1009 22 room_109
Qin 1010 21 room_110
#include<stdio.h>
#define SIZE 10
struct Student_type
{
char name[10];
int num;
int age;
char addr[15];
}stud[SIZE];
void save()
{
FILE * fp;
int i;
if((fp=fopen("stu.dat","wb"))==NULL)
{
printf("cannot open file\n");
return;
}
for(i=0;i<SIZE;i++)
if(fwrite(&stud[i],sizeof(struct Student_type),1,fp)!=1)
printf("file write error\n");
fclose(fp);
}
int main()
{
int i;
printf("Please enter data of students :\n");
for(i=0;i<SIZE;i++)
scanf("%s%d%d%s",stud[i].name,&stud[i].num,&stud[i].age,stud[i].addr);
save();
return 0;
}
在fopen函数中指定读写方式为wb,即二进制写方式。在向磁盘文件stu.dat写的时候,将内存中存放stud数组元素stud[i]的内存单元中的内容原样复制到磁盘文件中,所建立的stu.dat文件是一个二进制文件。这个文件可以为其他文件所用
在本程序中,用fopen函数打开文件时没有指定路径,只写了文件名stu.dat,系统默认其路径为当前路径为当前用户所使用的子目录(即源文件所在的目录),在此目录下建立一个新文件stu.dat,输出的数据存放在此文件中。
//以下程序可以检查是否读入准确
#include<stdio.h>
#include<stdlib.h>
#define SIZE 10
struct Student_type
{
char name[10];
int num;
int age;
char addr[15];
}stud[SIZE];
int main()
{
int i;
FILE * fp;
if((fp=fopen("stu.dat","rb"))==NULL)
{
printf("cannot open file\n");
exit(0);
}
for(i=0;i<SIZE;i++)
{
fread(&stud[i],sizeof(struct Student_type),1,fp);
printf("%-10s %4d %4d %-15s\n",stud[i].name,stud[i].num,stud[i].age,stud[i].addr);
}
fclose(fp);
return 0;
}
5.有一个磁盘文件,内有一些信息。要求第一次将它的内容显示在屏幕上,第二次把它复制到另一个文件上。
解题思路:两个问题都不难,但是把两者连续做,就会出现问题,因为在第一次读入完文件后,文件位置标记已指到文件的末尾,如果再接着读数据,就遇到文件结束标志EOF,feof函数的值等于1(为真),无法再读入数据。必须在程序中用rewind函数使位置指针返回文件开头。
#include<stdio.h>
int main()
{
FILE *fp1,*fp2;
char ch;
if((fp1=fopen("file1.dat","r"))==NULL)
{
printf("cannot open file\n");
exit(0);
}
if((fp2=fopen("file2.dat","w"))==NULL)
{
printf("cannot open file\n");
exit(0);
}
ch=fgetc(fp1);
while(!feof(fp1))
{
putchar(ch);
ch=fgetc(fp1);
}
putchar(10);
rewind(fp1);
ch=fgetc(fp1);
while(!feof(fp1))
{
fputc(ch,fp2);
ch=fgetc(fp1);
}
fclose(fp1);fclose(fp2);
return 0;
}
6.在磁盘文件上存有10个学生的信息,要求将第1,3,5,7,9个学生数据输入计算机,并在屏幕上显示出来。
思路:
- 按“二进制只读”的方式打开指定的磁盘文件,准备从磁盘文件中读取学生学习
- 将文件位置标记指向文件的开头,然后从磁盘文件读取一个学生的信息,并把它显示在屏幕上
- 再将文件位置标记指向文件中第3,5,7,9个学生数据区的开头,从磁盘文件读入相应学生的信息,并把它显示在屏幕上。
#include<stdio.h>
#include<stdlib.h>
struct Student_type
{
char name[10];
int num;
int age;
char addr[15];
}stud[10];
int main()
{
int i;
FILE *fp;
if((fp=fopen("stu.dat","rb"))==NULL)
{
printf("cannot open file\n");
exit(0);
}
for(i=0;i<10;i=i+2)//题目要求的是第3,5,7,9个学生的数据
{
fseek(fp,i*sizeof(struct Student_type),0);//移动文件为位置标记
fread(&stud[i],sizeof(struct Student_type),1,fp);//读一个数据块到结构体变量
printf("%-10s %4d %4d %-10s\n",stud[i].name,stud[i].num,stud[i].age,stud[i].addr);//在屏幕输出
}
fclose(fp);
return 0;
}
要瞎了要瞎了,溜了溜了