目录
一、为什么使用文件
二、什么是文件
2.1 程序文件
2.2 数据文件
2.3 文件名
三、文件的打开和关闭
3.1 文件指针
3.2 文件的打开与关闭
3.2.1 fopen函数
3.2.2 fclose函数
四、文件的顺序读写
1 fputc函数
2 fgetc函数
3 fputs函数
4 fgets函数
5 fprintf函数
6 fscanf函数
7 fwrite函数
8 fread函数
五、文件的随机读写
1 fseek函数
2 ftell函数
3 rewind函数
六、文本文件和二进制文件
七、文件读取结束的判定
1 文本文件例子
2 二进制文件例子
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE
FILE* pf;//文件指针变量
FILE* fopen(const char* filename, const char* mode);
文件使用方式 | 作用 | 如果指针不存在 |
“ r ” (只读) | 为了输入数据,打开一个已存在的文本文件 | 报错 |
“ w ” (只写) | 为了输入数据,打开一个文本文件 | 建立一个新文件 |
“ a ” (追加) | 向文本文件末尾添加数据 | 建立一个新文件 |
“ rb” (只读) | 为了输入数据,打开一个二进制文件 | 报错 |
“ wb” (只写) | 为了输出数据,打开一个二进制文件 | 建立一个新文件 |
“ab” (追加) | 向二进制文件末尾添加数据 | 报错 |
“r+” (读写) | 为了读和写,打开一个文本文件 | 报错 |
“w+” (读写) | 为了读和写,新建一个文本文件 | 建立一个新文件 |
”a+” (读写) | 在文本文件末尾读和写 | 建立一个新文件 |
”rb+” (读写) | 为读和写,打开一个二进制文件 | 报错 |
“wb+” (读写) | 为读和写,新建一个二进制文件 | 建立一个新文件 |
”ab+” (读写) | 在二进制文件末尾读和写 | 建立一个新文件 |
int fclose(FILE* stream);
函数说明:关闭与stream关联的文件并解除其关联;
函数输入:要关闭文件的FILE*类型指针;
函数输出:如果流成功关闭,则返回0。失败时,返回EOF。
int fputc ( int character, FILE * stream );
函数说明:将字符写入stream并使位置指示器前进。
函数输入:要写入的字符(会被转化为int类型)。
函数输出:指向输出流的FILE结构体的指针。
代码1演示:
# include
# include
# include
int main()
{
//每次打开文件时系统会自动在文件信息区创建一个FILE结构体
//用来保存文件的相关信息
FILE* fp;
//fopen返回值是FILE*
fp = fopen("test.txt","w");
//判断返回值是否为空指针
if(fp == NULL)
{
printf("%s\n",strerror(errno));
}
else
{
//输入三个字符
fputc('j',fp);
fputc('y',fp);
fputc('y',fp);
}
//关闭文件
fclose(fp);
//把FILE*类型指针赋值为NULL
fp = NULL;
return 0;
}
int fgetc ( FILE * stream );
函数说明:返回指定流(stream)的内部文件位置指示符当前指向的字符。然后,内部文件位置指示器前进到下一个字符。
函数输入:所有流;比如(键盘(标准输入流stdin),FILE*);
函数输出:读取成功后,将返回读取的字符(提升为int型)。读取失败或者读取结束返回EOF;
代码2演示:
# include
# include
# include
int main ()
{
int i;
FILE * pf;
int c;
pf=fopen ("test.txt","r");
if (pf==NULL)
{
printf("%s\n",strerror(errno));
}
else
{
for(i=0;i<3;i++)
{
//获取文件字符,并将文件位置指示器前进一位
c = fgetc(pf);
printf("%c",c);
};
//换行
printf("\n");
//关闭文件
fclose (pf);
//指针赋值为空指针
pf = NULL;
}
}
代码2结果演示:
jyy
请按任意键继续. . .
int fputs ( const char * str, FILE * stream );
函数说明:将str指向的字符串写入stream。函数开始从指定的地址(str)复制,直到到达终止的空字符(‘\0’)。此终止的空字符不会复制到stream中。fputs与puts的不同之处不仅在于可以指定目标流,而且fputs不会写入额外的字符,而puts会自动在末尾添加换行符。
函数输入:str:要输入的字符串;stream:要写入的文件对应的FILE*指针;
函数输出:成功时,返回非负值。出现错误时,函数返回EOF。
代码3演示:
# include
# include
# include
int main()
{
//每次打开文件时系统会自动在文件信息区创建一个FILE结构体
//用来保存文件的相关信息
FILE* fp;
//fopen返回值是FILE*
fp = fopen("test.txt","w");
//判断返回值是否为空指针
if(fp == NULL)
{
printf("%s\n",strerror(errno));
}
else
{
//输入一个字符串
fputs("jyy&whq",fp);
}
//关闭文件
fclose(fp);
//把FILE*类型指针赋值为NULL
fp = NULL;
return 0;
}
代码3结果演示:
char * fgets ( char * str, int num, FILE * stream );
函数说明:从stream中读取字符并将其作为字符串存储到str中,直到读取(num-1)个字符或到达换行符或文件结尾,以先发生的为准。
函数输入:str:输出字符串指针;拷贝到str字符个数(包括‘\0’);stream:要读取文件的指针;
函数输出:读取成功返回str,读取失败或者读取结束返回空指针;
代码4演示:
# include
# include
# include
int main()
{
char str[10];
FILE* fp;
//fopen返回值是FILE*
fp = fopen("test.txt","r");
//判断返回值是否为空指针
if(fp == NULL)
{
printf("%s\n",strerror(errno));
}
else
{
//输出字符串
fgets(str, 7, fp);
//输出jyy&wh表示最后一位是'\0'
printf("%s\n",str);
}
//关闭文件
fclose(fp);
//把FILE*类型指针赋值为NULL
fp = NULL;
return 0;
}
代码4结果演示:
jyy&wh
请按任意键继续. . .
int fprintf ( FILE * stream, const char * format, ... );
函数说明:将按格式输入的字符串写入stream。如果format包含格式说明符(以%开头的子序列),则格式化格式后面的其他参数并将其插入到结果字符串中,替换它们各自的说明符。
函数输入:stream:要输入的文件指针(流);format:要输入的格式;
函数输出:如果写入成功返回字符个数;如果失败返回负数;
代码5演示:
# include
# include
# include
typedef struct S
{
char name[10];
int age;
};
int main()
{
//初始化结构体
struct S s = {"jyy",23};
char n;
FILE* fp;
//fopen返回值是FILE*
fp = fopen("test.txt","w");
//判断返回值是否为空指针
if(fp == NULL)
{
printf("%s\n",strerror(errno));
}
else
{
//输出字符串
n = fprintf(fp,"%s %d",s.name,s.age);
//输出应该是6个字符
printf("%d\n",n);
}
//关闭文件
fclose(fp);
//把FILE*类型指针赋值为NULL
fp = NULL;
return 0;
}
代码5结果演示:
6
请按任意键继续. . .
int fscanf ( FILE * stream, const char * format, ... );
函数说明:从stream中读取数据,并根据参数格式将其存储到附加参数所指向的位置(地址)。
函数输入:stream:读取文件的指针;format:读取内容的格式(参考scanf);
函数输出:读取成功返回读取的项数,比如“%s %s”是两项,读取失败返回负数;
代码5演示:
# include
# include
# include
typedef struct S
{
char name[10];
int age;
};
int main()
{
//初始化结构体
struct S s;
char n;
FILE* fp;
//fopen返回值是FILE*
fp = fopen("test.txt","r");
//判断返回值是否为空指针
if(fp == NULL)
{
printf("%s\n",strerror(errno));
}
else
{
//读取格式数据
n = fscanf(fp,"%s %d",s.name,&(s.age));
//输出应该是2个字符
printf("%d\n",n);
printf("%s %d",s.name,s.age);
}
//关闭文件
fclose(fp);
//把FILE*类型指针赋值为NULL
fp = NULL;
return 0;
}
代码5结果演示:
2
jyy 23请按任意键继续. . .
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
函数说明:将一个计数元素数组(每个元素的大小为字节)从ptr指向的内存块写入stream中的当前位置。
函数输入:ptr:要输入信息地址;size:每个元素大小;count:元素个数;stream要写入文件对应的FILE*类型指针;
函数输出:返回成功写入的元素总数。如果此数字与计数参数(count)不同,则写入错误会导致函数无法完成。在这种情况下,将为流设置错误指示器(ferror)。
代码演示:
# include
int main ()
{
size_t n;
FILE * pFile;
char buffer[] = { 'x' , 'y' , 'z' };
pFile = fopen ("myfile.bin", "wb");
if (pFile != NULL)
n = fwrite (buffer , sizeof(char), sizeof(buffer), pFile);
printf("%d\n%d\n",n,sizeof(buffer));
fclose (pFile);
pFile = NULL;
return 0;
}
代码结果演示:
3
3
请按任意键继续. . .
结果以16进制保存。
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
函数说明:从stream中读取数据块;读取到ptr地址;
函数输入:ptr:指向大小至少为(size*count)字节的内存块的指针,转换为void*。;size:元素大小;count:元素个数;stream被读取的文件对应的FILE*类型指针;
函数输出:返回成功读取的元素总数。如果此数字与计数参数不同,则表示读取时发生读取错误或到达文件结尾。在这两种情况下,都设置了适当的指示器,可分别使用ferror和feof进行检查。
代码演示:
#include
#include
int main () {
FILE * pFile;
long lSize;
char * buffer;
size_t result;
pFile = fopen ( "myfile.bin" , "rb" );
if (pFile==NULL)
{
//stdrr表示标准错误
fputs ("File error",stderr);
return 0;
}
// 获取文件大小:
fseek (pFile , 0 , SEEK_END);
lSize = ftell (pFile);
rewind (pFile);
// 申请内存:
buffer = (char*) malloc (sizeof(char)*lSize);
if (buffer == NULL)
{
fputs ("Memory error",stderr);
return 0;
}
// 读取数据到buffer:
result = fread (buffer,1,lSize,pFile);
if (result != lSize)
{
fputs ("Reading error",stderr);
return 0;
}
// 结束
fclose (pFile);
free (buffer);
return 0;
}
代码结果演示:
我们在读写文件时,从哪里开始读,从哪里开始写都是由文件指针位置决定的,第四节介绍的函数都是从文件开始位置读写的。然而有时候我们会根据自己需求来读写文件,这就要求要改变文件指针的位置,下面我们介绍三个改变文件指针位置的函数。
int fseek ( FILE * stream, long int offset, int origin );
函数说明:根据文件指针的位置和偏移量来定位文件指针
函数输入:ofset:对于二进制文件:从起始偏移的字节数。对于文本文件:零或ftell返回的值。
origin:用作偏移参考的位置。它由
函数输出:如果成功,函数返回零。否则,返回非零值(相对于origin位置)。
代码7演示:
# include
int main ()
{
FILE * pFile;
pFile = fopen ( "test.txt" , "w" );
if(pFile != NULL)
{
//输入字符串,并把文件指针转移到apple后面
fputs ( "This is an apple." , pFile );
//把文件指针转移到a和n之间(从文件开始前进九个字符)
fseek ( pFile , 9 , SEEK_SET );
//把 app四个字符替换为 sam
fputs ( " sam" , pFile );
//关闭文件
fclose ( pFile );
pFile = NULL;
}
return 0;
}
代码7结果演示:
long int ftell ( FILE * stream );
函数说明:返回文件指针位置相对于初始位置的偏移量(文件指针当前位置);
函数输入:指向读写文件的FILE*类型指针;
函数输出:成功后返回文件指针当前位置,失败返回-1;
代码8演示:
# include
int main ()
{
long end;
FILE * pFile;
long size;
pFile = fopen ("test.txt","rb");
if (pFile==NULL)
perror ("Error opening file");
else
{
//文件结尾位置相对于文件末尾指针位置
end = fseek (pFile, 0, SEEK_END);
printf("test.txt文件指针位置:%d 字节\n",end);
//文件结尾位置相对于初始位置指针位置
size=ftell (pFile);
fclose (pFile);
printf ("test.txt文件大小: %ld 字节.\n",size);
}
return 0;
}
代码8结果演示:
test.txt文件指针值:0 字节
test.txt文件大小: 17 字节.
请按任意键继续. . .
void rewind ( FILE * stream );
函数说明:让文件指针位置回到初始值
函数输入:指向读写文件的FILE*类型指针;
代码9演示:
#include
int main (){
int n;
FILE * pFile;
char buffer [30];
//打开一个读写文件
pFile = fopen ("test.txt","w+");
for ( n='A'; n<='Z' ; n++)
fputc(n, pFile);
//让文件指针回到初始位置
rewind(pFile);
//读取文件
fgets(buffer,27,pFile);
fclose (pFile);
pFile = NULL;
printf("%s\n",buffer);
return 0;
}
代码9结果演示:
ABCDEFGHIJKLMNOPQRSTUVWXYZ
请按任意键继续. . .
#include
int main()
{
int a = 10000;
FILE* pf = fopen("test.txt", "wb");
fwrite(&a, 4, 1, pf);//二进制的形式写到文件中
fclose(pf);
pf = NULL;
return 0;
}
代码10结果演示:
代码11演示:
#include
int main()
{
char a[] = "10000";
FILE* pf = fopen("test.txt", "w");
fputs(a,pf);//ASCII的形式写到文件中
fclose(pf);
pf = NULL;
return 0;
}
代码11结果演示:
代码12演示:
#include
#include
int main(void)
{
int c; // 注意:int,非char,要求处理EOF
FILE* fp = fopen("test.txt", "r");
if(!fp) {
perror("File opening failed");
return EXIT_FAILURE;
}
//fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
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);
}
代码12结果演示:
10000
End of file reached successfully
请按任意键继续. .
代码13演示:
#define _CRT_SECURE_NO_WARNINGS
#include
enum {SIZE = 5};
int main(void)
{
int n;
double a[SIZE] = {1.,2.,3.,4.,5.};
double b[SIZE];
unsigned int ret_code;
FILE *fp = fopen("test.bin", "wb"); // 必须用二进制模式
fwrite(a, sizeof(double), SIZE, fp); // 写 double 的数组
fclose(fp);
fp = NULL;
fp = fopen("test.bin","rb");
ret_code = fread(b, sizeof(double), SIZE, fp); // 读 double 的数组
if(ret_code == SIZE)
{
puts("Array read successfully, contents: ");
for(n = 0; n < SIZE; ++n)
printf("%f ", b[n]);
putchar('\n');
}
else
{ // error handling
if (feof(fp))
printf("Error reading test.bin: unexpected end of file\n");
else if (ferror(fp))
{
perror("Error reading test.bin");
}
}
fclose(fp);
fp =NULL;
}
代码13结果演示:
Array read successfully, contents:
1.000000 2.000000 3.000000 4.000000 5.000000
请按任意键继续. . .
八、文件缓冲区
代码14演示:
#include
#include
int main()
{
FILE*pf = fopen("test.txt", "w");
fputs("abcdef", pf);//先将代码放在输出缓冲区
printf("睡眠10秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");
Sleep(10000);
printf("刷新缓冲区\n");
fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
//注:fflush 在高版本的VS上不能使用了
printf("再睡眠10秒-此时,再次打开test.txt文件,文件有内容了\n");
Sleep(10000);
fclose(pf);
//注:fclose在关闭文件的时候,也会刷新缓冲区
pf = NULL;
return 0;
}
代码14结果演示:
睡眠10秒-已经写数据了,打开test.txt文件,发现文件没有内容
刷新缓冲区
再睡眠10秒-此时,再次打开test.txt文件,文件有内容了
请按任意键继续. . .