6.文件的操作

FILE指针

C语言,每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及 文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE. 例如,VS2008编译环境提供的stdio.h 头文件中有以下的文件类型申明:

struct _iobuf{
    char*_ptr; 
    int _cnt; 
    char *_base; 
    int _flag;  
    int _file; 
    int _charbuf; 
    int _bufsiz; 
    char*_tmpfname;
    }; 
typedef struct _iobuf FILE;

不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。 每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关 心细节。 一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。


打开关闭文件

打开文件

fopen函数原型:
FILE *fopen(const char *path, const char *mode);

参数:第一个参数是文件路径,第二个参数是打开文件的方式,类型均为字符串

使用示例
pFile = fopen("myfile.txt", "w");
#include <stdio.h>
int main(){
	FILE* pFile;
	pFile = fopen("myfile.txt", "w");
	if (pFile != NULL)	{
		fputs("fopen example", pFile);
		fclose(pFile);
	}
	return 0;
}

若是没有这个文件,便会创建一个文件

第二个参数选择
文件使用方式 含义 如果指定文件不存在
“r”(读) 打开文件用于读 出错
“w”(写) 将文件截断为零长度或创建文本文件以进行写入 建立一个新的文件
“rt”(只读) 1 打开一个文本文件,只允许读数据 出错
“wt”(只写) 只写打开或建立一个文本文件,只允许写数据 建立一个新文件
“a”(追加) 向文本文件尾追加数据 出错
“rb”(追加) 打开一个二进制文件读 出错
“wb”(只写) 打开一个二进制文件写 建立一个新的文件
“ab”(追加) 向一个二进制文件尾添加数据 出错
“r+”(读写) 为了读和写,打开一个文本文件 出错
“w+”(读写) 为了读和写,建立一个新的文件 建立一个新文件
“a+”(读写) 打开一个文件,在文件尾进行读写 建立一个新的文件
“rb+”(读写) 2 为了读和写打开一个二进制文件 出错
“wb+”(读写) 为了读和写,新建一个新的二进制文件 建立一个新的文件
“ab+”(读写) 打开一个二进制文件,在文件尾进行读和写 建立一个新的文件

关闭文件

fclose函数原型:
int fclose(FILE *stream);

参数:唯一的一个参数是需要关闭的文件指针


文件读写

顺序读写

功能 函数名 适用于
字符输出函数 fputc 所有输出流
字符输入函数 fgetc 所有输入流
文本行输出函数 fputs 所有输出流
文本行输入函数 fgets 所有输入流
格式化输出函数 fprintf 所有输出流
格式化输入函数 fscanf 所有输入流
二进制输出 fwrite 文件
二进制输入 fread 文件
fputc函数原型:
int fputc( int c, FILE *stream );

参数:第一个参数是要写入的字符,第二个参数是要写入的文件流

使用示例
    FILE* pFile = fopen("fputc.txt", "w");
	char* str = "This is a test of fputc!!\n";
	char* p_str = str;
	while ((*p_str != '\0') && fputc(*(p_str++), stdout) != EOF);
	p_str = str;
	while ((*p_str != '\0') && fputc(*(p_str++), pFile) != EOF);

FILE指针其实就是文件流,也是流的一种,与标准输入输出流使用起来差不多

fgetc函数原型:
int fgetc( FILE *stream );

参数:stream是要读取的目的流

使用示例
    //先使用fputs写一个文本文件
	FILE* pFile = fopen("fgetc.txt", "w");
	char* str = "This is a test of fgetc!!\n";
	fputs("Hello world from fgetc.\n", pFile);
	fclose(pFile);
	//使用fgetc读取这个文本文件的内容
	pFile = fopen("fgetc.txt", "r");
	while(feof(pFile) == 0){
		char ch = fgetc(pFile);
		fputc((char)ch, stdout);
	}
	fclose(pFile);
    

fputs函数原型:
int fputs( const char *string, FILE *stream );

参数:第一个是要写入文件的字符串,第二个参数是输出流

使用示例
    FILE* pFile = fopen("fputs.txt", "w");
	char* str = "This is a test of fputs!!\n";
    fputs(str, pFile);

fgets函数原型:
char *fgets(char *restrict s, int n, FILE *restrict stream);

参数:从stream流中读取最大n个字符量的字符串保存到s字符中

使用示例
//先使用fputs写入一个文本文件
	FILE* pFile = fopen("fgets.txt", "w");
	char* str = "This is a test of fgets!!\n";
	fputs(str, pFile);
	fclose(pFile);
	//再使用fgets读
	char line[1000];
	if ((pFile = fopen("fgets.txt", "r")) != NULL)
	{
		if (fgets(line, 1000, pFile) == NULL)
			printf("fgets error\n");
		else
			printf("%s", line);
		fclose(pFile);
	}

fprintf函数原型:
int fprintf( FILE *stream, const char *format [, argument ]...);

参数:stream要输出的流,format是格式控制字符串,argument是多个变量

使用示例
    int    i = 10;
	double fp = 1.5;
	char   s[] = "this is a string";
	char   c = '\n';

	FILE* stream = fopen("fprintf.out", "w");
	fprintf(stream, "%s%c", s, c);
	fprintf(stream, "%d\n", i);
	fprintf(stream, "%f\n", fp);
	fclose(stream);
	system("type fprintf.out");

与标准输出的printf区别在于多了第一个指定输出的流参数,out后缀文件内容直接可以使用system打印到标准输出流


fscanf函数原型:
int fscanf( FILE *stream, const char *format [, argument ]... );

参数:从stream流按format格式解析数据依次存储到arguments中

使用示例
    //先使用fprintf向文件输入
    int    i = 10;
	double fp = 1.5;
	char   s[] = "string";
	char   c = 'U';

	FILE* stream = fopen("fscanf.out", "w");
	fprintf(stream, "%s %c", s, c);
	//s字符串需要scanf读取的话,没有空格和换行的字符串请用空格与后面的变量隔开,如果字符串含空格,建议将字符串作为一整行用fgets读取
	fprintf(stream, "%d\n", i);
	fprintf(stream, "%f\n", fp);
	fclose(stream);
	
	//使用fscanf读取文件数据
	stream = fopen("fscanf.out", "r");
	fscanf(stream, "%s", &s);
	fscanf(stream, "%c", &c); //捕捉空格
	fscanf(stream, "%c", &c);
	printf("%s %c\n", s, c);
	fscanf(stream, "%d", &i);
	fscanf(stream, "%f", &fp);
	printf("%d\n%f\n", i, fp);
	fclose(stream);

fwrite函数原型:
size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );

参数:buffer是将要被写的数据,size是元素字节大小,count是元素个数,stream是要写的目的流

使用示例
    FILE* stream = fopen("fwrite.out", "w+t");
	char list[] = "abcdefghijklmnopqrstuvwxyz";
	if (stream  != NULL)	{
		char numwritten = fwrite(list, sizeof(char), strlen(list), stream);
		printf("Wrote %d items\n", numwritten);
		fclose(stream);
	}
	system("type fread.out");

fwrite可以写入count个字符、整形或者size大小的字符串


fread函数原型:
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );

参数:buffer是读取的数据存储区,size是单个元素大小,count是

使用示例
    //读取上面fwrite保存的fwrite.out文件
    if ((stream = fopen("fwrite.out", "r+t")) != NULL)
	{
		char numread = fread(list, sizeof(char), 26, stream);
		printf("Number of items read = %d\n", numread);
		printf("Contents of buffer = %.26s\n", list);
		fclose(stream);
	}
	else
		printf("File could not be opened\n");

随机读写

文件指针跳转任意位置

fseek函数原型
int fseek(FILE * stream, long int offset, int origin);

参数:stream是所操作的流,offset是偏移量,origin是基准偏移起始位置

基准 偏移基准位置
SEEK_SET 文件起始
SEEK_CUR 文件指针所指向的当前位置
SEEK_END 文件结尾
使用示例
    FILE* pFile = fopen("example.txt", "wb");
	fputs("This is an apple.", pFile);
	fseek(pFile, 9, SEEK_SET);
	fputs(" sam", pFile);
	fclose(pFile);
	system("type example.txt");

返回文件指针相对于起始位置的偏移量

ftell函数原型
long ftell( FILE *stream );

参数:stream是得到偏移量的目标流

使用示例
    FILE* stream = fopen("ftell.c", "w+");
	srand(time(NULL));
	int charNumber = 150;
	while (charNumber--)
		fputc(rand() % 57 + 65, stream);

	//得到当前的偏移量
	long position;
	position = ftell(stream);
	printf("Position after trying to read 100 bytes: %ld\n",
		position);
	fclose(stream);


文件指针的位置回到文件的起始位置

rewind函数原型
void rewind ( FILE * stream );

参数:stream是将文件指针指向文件起始的目标流

使用示例
    //写一个文件用于测试
    char list[100];
	FILE* stream = fopen("rewind.c", "w");
	srand(time(NULL));
	int charNumber = 150;
	while (charNumber--)
		fputc(rand() % 57 + 65, stream);
	fclose(stream);
	stream = fopen("rewind.c", "r");
	
	//读
	fread(list, sizeof(char), 100, stream);
	char position = ftell(stream);
	printf("Position after read: %ld\n",
		position);
		
	//文件指针回到文件首
	rewind(stream);
	position = ftell(stream);
	printf("Position after back to start: %ld\n",
		position);
	fclose(stream);

刚写完不关闭文件流读取文件指纹偏移量是负数

FILE* stream = fopen("rewind.c", "w");
	srand(time(NULL));
	int charNumber = 150;
	while (charNumber--)
		fputc(rand() % 57 + 65, stream);
	char position = ftell(stream);
	printf("Position after write: %ld\n", position);

文件结束判定

feof函数原型
int feof( FILE *stream );

参数:判断文件指针是否到达文件结尾的目标流

fgetc、fgetc、fread、fprintf当读到文件结尾时就会返回EOF

使用示例
    //创建feof.c
	FILE* stream = fopen("feof.c", "w");
	srand(time(NULL));
	int charNumber = 80;
	while (charNumber--)
		fputc(rand() % 57 + 65, stream);
	fclose(stream);

	//读feof.c
	int  count, total = 0;
	char buffer[100];
	if ((stream = fopen("feof.c", "r")) == NULL)
		exit(1);


	//结合feof就可以循环读到文件结尾
	/* Cycle until end of file reached: */
	while (!feof(stream))
	{
		/* Attempt to read in 10 bytes: */
		count = fread(buffer, sizeof(char), 100, stream);
		if (ferror(stream)) {
			perror("Read error");
			break;
		}

		/* Total up actual bytes read */
		total += count;
	}
	printf("Number of bytes read = %d\n", total);
	fclose(stream);

读二进制文件示例

#include <stdio.h>
enum { SIZE = 5 };
int main(void)
{
	double a[SIZE] = { 1.0,2.0,3.0,4.0,5.0 };
	double b = 0.0;
	size_t ret_code = 0;
	FILE* fp = fopen("test.bin", "wb"); // 必须用二进制模式
	fwrite(a, sizeof(*a), SIZE, fp); // 写 double 的数组
	fclose(fp);
	fp = fopen("test.bin", "rb");
	// 读 double 的数组
	while ((ret_code = fread(&b, sizeof(double), 1, fp)) >= 1)
	{
		printf("%lf\n", b);
	}
	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;
}


  1. 等价于r+t ↩︎

  2. 等价于r+b ↩︎

猜你喜欢

转载自blog.csdn.net/qq_43808700/article/details/114106618