C语言基础(九)
系列合集 初窥C语言
十、文件
10.1 C文件概述
文件:存储在外部介质上数据的集合,是操作系统数据管理的单位。
使用数据文件的目的:
1)程序和数据分离:数据文件的改动不引起程序的改动
2)数据共享:不同程序可以访问同一数据文件中的数据
3)能长期保存程序运行的中间数据或结果数据
文件分类:
(1)按文件的逻辑结构
记录文件:由具有一定结构的记录组成(定长或不定长)
流式文件:由一个个字符(字节)数据顺序组成
(2)按存储介质
普通文件:存储介质文件(磁盘,磁带等)
设备文件:非存储介质(键盘,显示器,打印机等)
(3)按数据的组织形式
文本文件:ASCII文件,每个字节存放一个字符的ASCII码。
文本文件特点:存储量大,速度慢,便于对字符操作
二进制文件:数据按其在内存中的存储形式原样存放
二进制文件特点:存储量小,速度快,便于存放中间结果
文件处理方式:
缓冲文件系统:高级文件系统,系统自动为正在使用的文件开辟内存缓冲区
非缓冲文件系统:低级文件系统,由用户在程序中为每个文件设定缓冲区
10.2 文件类型指针
文件结构体 FILE:
缓冲文件系统为每个正使用的文件在内存中开辟了文件信息区,文件信息用系统定义的名为FILE的结构体描述
FILE定义在studio.h中
typedef struct{
int _fd; // 文件号
int _cleft; //缓冲区剩下的字符数
int _mode; //文件操作方式
char *_next; //文件当前读写位置
char *_buff; //文件缓冲区位置
}FILE;
文件类型指针:
指针变量说明: FILE *fp;
用法:文件打开时,系统自动建立文件结构体,并把指向它的指针返回来,程序通过这个指针获得文件信息,访问文件;文件关闭后,它的文件结构体被释放。
10.3 文件的打开与关闭
10.3.1 文件的打开
C文件操作用调用库函数实现,包括在studio.h
文件使用方式:打开文件 --文件读、写 --关闭文件
系统自动打开和关闭三个标准文件:
标准输入: 键盘 stdin
标准输出: 显示器 stdout
标准出错输出: 显示器 stderr
打开文件fopen:
函数原型:
FILE *fopen (char *name, char *mode)
功能:按指定方式打开文件
返回值:正常打开,为指向文件结构体的指针;打开失败,返回NULL
例如:文件的打开与测试
FILE *fp;
fp = fopen("a.c","w");
if(fp == NULL){
prentf("file open error\n");
exit(0);
}
文件的打开方式:
三个基本模式:
“r”(read)模式总是打开一个已经存在的文件,如果文件不存在则出错。
“w”(write)模式建立一个新文件,如果文件已经存在,那么先删除存在的文件,再建立新文件。
“a”(append)打开一个存在的文件,在文件的尾部追加数据。
三个追加符:
“b”(binary)表示二进制文件。
“t”(或默认)表示文本文件。
“+”表示将模式扩展为可读、可写方式。
文件的打开方式 | 含义 |
---|---|
’ r ’ (只读) | 为输入打开一个文本文件 |
’ w '(只写) | 为输出打开一个文本文件 |
’ a '(追加) | 向文本文件尾追加数据 |
’ rb '(只读) | 为输入打开一个二进制文件 |
’ wb '(只写) | 为输出打开一个二进制文件 |
’ ab '(追加) | 向二进制文件尾追加数据 |
’ r+ '(读写) | 为读/写打开一个文本文件 |
’ w+ '(读写) | 为读/写建立一个新的文本文件 |
’ a+ '(读写) | 为读/写打开一个文本文件 |
’ rb+ '(读写) | 为读/写打开一个二进制文件 |
’ wb+ '(读写) | 为读/写建立一个新的二进制文件 |
’ ab+ '(读写) | 为读/写打开一个二进制文件 |
10.3.2 文件的关闭
作用:使文件指针变量与文件“脱离”,释放文件结构体和文件指针
函数原型: int fclose(FILE *fp);
*fp :文件打开时返回的文件类型指针
功能:关闭fp指向的文件
返回值:正常关闭为0,出错为非0
10.4 文件的读写
10.4.1 fputc函数和fgetc函数
字符I/O:fputc与fgetc
1)fputc
函数原型:
int fputc(int c,FILE *fp)
功能:把一字节代码c写入fp指向的文件中
返回值:成功,返回c;出错,返回EOF
2)fgetc
函数原型: int fgetc(FILE *fp)
功能:从fp指向的文件中读取一字节代码
返回值:正常,返回读到的代码值;读到文件尾或出错为止
终端I/O | 文件I/O | |
---|---|---|
#define | putc ( ch,fp ) | fputc ( ch,fp ) |
#define | getc ( fp ) | fgetc ( fp ) |
#define | putchar ( c ) | fputc ( c,stdout ) |
#define | getchar ( ) | fgetc ( stdin ) |
示例:
1.从键盘输入字符,逐个存到磁盘文件中,直到输入‘#’为止
#include <stdio.h>
#include <stdlib.h>
int main(){
//FILE *fp 是声明,声明fp是指针,用来指向FILE类型的对象。
FILE *fp;
char ch,filename[10];
printf("请输入文件名称:\n");
scanf("%s",filename);
if((fp=fopen(filename,"w"))==NULL){
printf("open file is false,exit...\n");
exit(0);
}
printf("open file is true,Please input word\n");
//前面的scanf()在读取输入时会在缓冲区中留下一个字符'\n'
//所以如果不在此加一个getchar()把这个回车符取走的话,
//而是会直接取走这个“无用的”回车符,从而导致读取有误。
ch=getchar();
//首次获取键盘输入
ch=getchar();
while(ch!='#'){
//fputc 将字符ch写到文件指针fp所指向的文件的当前写指针的位置
fputc(ch,fp);
ch=getchar();
}
fclose(fp);
return 0;
}
输入:
file1.c
computer and c#
输出:
computer and c
2.将一个磁盘文件中的信息复制到另一个磁盘文件
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *in,*out;
char ch,infile[10],outfile[10];
printf("enter the infile name:");
scanf("%s",infile);
printf("enter the outfile name:");
scanf("%s",outfile);
if((in=fopen(infile,"r"))==NULL){
printf("cannot open this file\n");
exit(0);
}
if((out=fopen(outfile,"w"))==NULL){
printf("cannot open this file\n");
exit(0);
}
ch=fgetc(in);
// 对feof()来说,站在光标所在位置,向后看看还有没有字符。如果有,返回0;如果没有,返回非0。
//它并不会读取相关信息,只是查看光标后是否还有内容。
//
while(!feof(in)){
fputc(ch,out);
ch=fgetc(in);
}
fclose(in);
fclose(out);
return 0;
}
输入:
file1.c
file2.c
10.4.2 fread函数和fwrite函数
数据块I/O:fread与fwrite
函数原型:
size_t fread( void *buffer, size_t size, size_t count, FILE *fp)
size_t fwrite( void *buffer, size_t size, size_t count, FILE *fp)
*buffer,size_t :指向要输入/输出数据块的首地址
size,size_t :每个要读/写的数据块的大小(字节数)
count,FILE:要读/写的数据块的个数
*fp :要读/写的文件指针
功能:读写数据块
返回值:成功,返回读写的块数;出错或文件尾,返回0
示例:从键盘输入4个学生的数据,转存到磁盘文件中,并显示在屏幕上
#include <stdio.h>
#define SIZE 4
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_data", "wb")) == NULL)
{
printf("cant 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);
}
void display()
{
int i;
FILE *fp;
if ((fp = fopen("stu_data", "rb")) == NULL)
{
printf("cannot open file\n");
return;
}
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);
}
int main()
{
int i;
printf("please enter date of studnet:\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();
display();
}
10.4.3 fprintf函数和fscanf函数
格式化I/O:fprintf与fscanf
函数原型:
int fprintf ( FILE *fp, const char *format [argument...])
int fscanf ( FILE *fp,const char *format [address...] )
功能:按格式对文件进行I/O操作
返回值:成功,返回I/O的个数;失败,出错或文件尾,返回EOF
fprintf (fp, "%d,%6.2f",i,t);
//将i和t按%d,%6.2f格式输出到fp文件
fscanf(fp, "%d,%f",&i,&t);
//将文件中的整型数据输入i,浮点型数据输入t
10.4.4 其他读写函数
字符串I/O:fgets和fputs
char *fgets( char *s, int n,FILE *fp)
int fputs(char *s,FILE *fp)
功能:从fp指向的文件读写一个字符串
返回值:
fgets正常时返回读取字符串的首地址;出错或文件尾,返回NULL
fputs正常时返回写入的最后一个字符;出错为EOF
fgets从fp所指文件读n-1个字符送入s指向的内存区,并在最后加上’\0’(若读入n-1个字符前遇到换行符或文件尾(EOF)即结束)
fputs把s指向的字符串写入fp指向的文件
10.5 文件的定位
10.5.1 rewind函数
文件位置指针-----指向当前读写位置的指针
顺序读写:位置指针按字节位置顺序移动
随机读写:位置指针按需要移动到任意位置
函数原型:
void rewind(FILE *fp)
功能:重置文件位置指针到文件开头
返回值:无
示例:从文件中第一次将它内容显示在屏幕上并且复制到另一个文件上
#include<stdio.h>
int main()
{
FILE *fp1,*fp2;
fp1 = fopen("file1.c","r");
fp2 = fopen("file2.c","w");
while(!feof(fp1)) putchar(getc(fp1));
rewind(fp1);
while(!feof(fp1)) putc(getc(fp1),fp2);
fclose(fp1);
fclose(fp2);
}
10.5.2 fseek函数和随机读写
fseek函数
函数原型:
int fseek(FILE *fp, long offset, int from)
功能:改变文件位置指针的位置
返回值:成功,返回0;失败,返回非0;
offset:位移量,类型为long型,表示以from为起点移动的量相对值(字节数)。
from:移动的起始位置。
from取值含义:
from value | 宏名 | 含义 |
---|---|---|
0 | SEEK_SET | 文件头 |
1 | SEEK_CUR | 当前位置 |
2 | SEEK_END | 文件尾 |
示例1:磁盘文件上有4个学生数据,要求读入第1,3学生数据并显示
#include <stdio.h>
#include<stdlib.h>
struct student_type
{
char name[10];
int num;
int age;
char addr[15];
}stud[10];
int main()
{
FILE * fp;
int i;
if ((fp = fopen( "stu_data" , "rb" )) == NULL)
{
printf( "cannot open file\n " );
exit(0);
}
for (i = 0 ;i < 4;i ++ )
{
fseek(fp,i*sizeof(struct student_type),0);
fread(&stud[i],sizeof(struct student_type),1,fp);
printf("%s %d %d %s \n",stud[i].name,stud[i].num,stud[i].age,stud[i].addr);
}
fclose(fp);
}
示例2:
fseek(fp,10L,SEEK_SET); //将读写指针移动到离文件头10个字节处
fseek(fp,20L,1); //将读写指针移动到离当前位置20个字节处
fseek(fp,-10L,2); //将读写指针移动到离文件末尾处10个字节处。
10.5.3 ftell函数
函数原型:
long ftell(FILE *fp)
功能:返回位置指针当前的位置(用相对文件开头的位移量表示)
返回值:成功,返回当前位置指针位置;失败,返回 -1L
10.6 出错的检测
10.6.1 ferror函数
函数原型:
int ferror(FILE *fp)
功能:测试文件是否出现错误
返回值:未出错,0;出错,非0
说明:
1)每次调用文件输入输出函数,均产生一个新的ferror函数值,所以应及时测试
2)fopen打开文件时,ferror函数初值自动置为0
10.6.2 clearerr函数
函数原型:
void clearerr(FILE *fp)
功能:使文件错误标志置为0
返回值:无
说明:出错后,错误标志一直保留,直到对同一文件调clearerr(fp)或rewind或任何其它一个输入输出函数
10.6.3 feof函数
作用:
feof()是检测流上的文件结束符的函数,如果文件结束,则返回非0值,否则返回0
EOF:
1)EOF是一个计算机术语,为End Of File的缩写,在操作系统中表示资料源无更多的资料可读取。资料源通常称为档案或串流。通常在文本的最后存在此字符表示资料结束。
2)文档的结尾都有一个隐藏字符”EOF”,当程序读取它的时候,就会知道文件已经到达结尾。
3)EOF 的值通常为 -1,但它依系统有所不同。
feof()的原理:
站在光标所在位置,向后看看还有没有字符。如果有,返回0;如果没有,返回非0。它并不会读取相关信息,只是查看光标后是否还有内容。
注意:
1)对于文件来说,无论是空文件,还是存有信息的文件,当文件被打开,光标处于默认的开头时,光标后都有信息,这时候调用feof()来查看光标后是否还有内容,就没意义。
2)我们需要从相同中找不同,先使用getc(),从文件中读取一个字符,让光标向后移动一个字符。这时空文件的光标就已经移动到EOF的后面,这时使用feof()就会返回1了。这才是feof()的正确用法。
3)但要注意,一定要将光标回到文件的开头,因为之前判断文件是否为空时,将光标向前移动了一位,必须要将光标恢复到开头,这样才能保证文件的正常读取。
rewind(p);//将光标跳回到文件开头
分类 | 函数名 |
---|---|
打开文件 | fopen() |
关闭文件 | fclose() |
文件定位 | fseek() ; rewind() ; ftell() |
文件读写 | fgetc() , getc() , fputc() , putc() , fgets() , fputs() , getw() , putw() , fread() , fwrite() , fscan() , fprintf() |
文件状态 | feof() , ferror() , clearerr() |