一、文件的打开和关闭
1、文件指针
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE。
一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。
下面可以创建一个FILE*的指针变量。
FILE* fp; //文件指针变量
定义fp是一个指向FILE类型数据的指针变量。可以使fp指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够找到与它关联的文件。
2、文件的打开和关闭
文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
规定使用fopen函数来打开文件,fclose函数来关闭文件
//打开文件
FILE *fopen( const char *filename, const char *mode );
//关闭文件
int fclose( FILE *stream );
打开方式如下:
文件使用方式 | 含义 | 如果指定文件不存在 |
"r"(只读) | 为了输入数据,打开一个已经存在 的文本文件 | 出错 |
"w"(只写) | 为了输出数据,打开一个文本文件 | 建立一个新的文件 |
"a"(追加) | 向文本文件尾添加数据 | 出错 |
"rb"(只读) | 为了输入数据,打开一个二进制文件 | 出错 |
"wb"(只写) | 为了输出数据,打开一个二进制文件 | 建立一个新的文件 |
"ab"(追加) | 向一个二进制文件尾添加数据 | 出错 |
"r+"(读写) | 为了读和写,打开一个文本文件 | 出错 |
"w+"(读写) | 为了读和写,打开一个文本文件 | 建立一个新的文件 |
"a+"(读写) | 打开一个文件,在文件尾进行读写 | 建立一个新的文件 |
"rb+"(读写) | 为了读和写,打开一个二进制文件 | 出错 |
"wb+"(读写) | 为了读和写,新建一个新的二进制 文件 | 建立一个新的文件 |
"ab+"(读写) | 打开一个二进制文件,在文件尾进行 读和写 | 建立一个新的文件 |
示例:
#include<stdio.h>
int main()
{
FILE* fp = fopen("1.txt", "w");
if (fp == NULL)
{
printf("文件打开失败\n");
return 0;
}
fclose(fp);
return 0;
}
二、文件的顺序读写
功能 | 函数名 | 适用于 |
字符输入函数 | fgetc | 所有输入流 |
字符输出函数 | fputc | 所有输出流 |
文本行输入函数 | fgets | 所有输入流 |
文本行输出函数 | fputs | 所有输出流 |
格式化输入函数 | fscanf | 所有输入流 |
格式化输出函数 | fprintf | 所有输出流 |
二进制输入 | fread | 文件 |
二进制输出 | fwrite | 文件 |
一下为函数原型
int fgetc( FILE *stream );
int fputc( int c, FILE *stream );
char *fgets( char *string, int n, FILE *stream );
int fputs( const char *string, FILE *stream );
int fscanf( FILE *stream, const char *format [, argument ]... );
int fprintf( FILE *stream, const char *format [, argument ]...);
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );
(1)fgetc与fputc的使用
(2) fgets与fputs的使用
(3)fscanf与fprintf的使用
(4)fread与fwrite的使用
三、文件的随机读写
//根据文件指针的位置和偏移量来定位文件指针
int fseek( FILE *stream, long offset, int origin );
//返回文件指针相对于起始位置的偏移量
long ftell( FILE *stream );
//让文件指针的位置回到文件的起始位置
void rewind( FILE *stream );
fseek的相对位置有三个。
SEEK_SET,从起始开始;SEEK_CUR,从当前位置开始;SEEK_END,从末尾开始
#include<stdio.h>
int main()
{
//1.txt内容为"hello world"
FILE* fp = fopen("1.txt", "r");
if (fp == NULL)
{
printf("文件打开失败\n");
return 0;
}
char buf1[20] = { 0 };
char buf2[20] = { 0 };
fgets(buf1, 20, fp);
printf("buf1 = %s\n", buf1);
//指针指向
printf("%ld\n", ftell(fp));
//将文件指针指向起始位置
rewind(fp);
//指针指向
printf("%ld\n", ftell(fp));
//改变指针指向
fseek(fp, 6, SEEK_SET);
fgets(buf2, 20, fp);
printf("buf2 = %s\n", buf2);
fclose(fp);
return 0;
}
运行结果:
四、文件读取结束的判定
(1)被错误使用的feof
int feof( FILE *stream );
牢记:在文件读取过程中,不能用feof函数的返回值直接用来判断文件是否结束。而是应用于当文 件读取结束的时候,判断是读取失败结束,还是遇到文件结束。
1、文本文件读取是否结束,判断返回值是否为EOF(fgetc),或者NULL(fgets)
fgetc判断是否为EOF(end of file)
fgets判断返回值是否为NULL
2、二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
文本文件例子:
#include <stdio.h>
#include <stdlib.h>
int main()
{
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) // 标准C I/O读取文件循环
{
putchar(c);
}
//判断是什么原因结束的
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
}
二进制文件例子:
#include <stdio.h>
enum { SIZE = 5 };
int main(void)
{
double a[SIZE] = { 1.,2.,3.,4.,5. };
FILE* fp = fopen("test.bin", "wb"); // 必须用二进制模式
fwrite(a, sizeof * a, SIZE, fp); // 写 double 的数组
fclose(fp);
double b[SIZE];
fp = fopen("test.bin", "rb");
size_t ret_code = fread(b, sizeof * b, SIZE, fp); // 读 double 的数组
if (ret_code == SIZE) {
puts("Array read successfully, contents: ");
for (int 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);
}