C语言笔记20

229 阅读8分钟

文件

创建写入读取文件

创建或打开 fopen函数

原型FILE* fopen(const char* filename, const char* mode);
前者是文件路径 相对路径(从当前目录开始定位的路径)data.txt 绝对路径(从顶级目录开始定位的路径)D:/projects/data.txt
后者是操作模式
若创建成功则返回一个指针

操作模式

  • 读取"w"写模式 会清空原有文件内容
  • 写入"r"读模式
  • 追加a追加模式 保留原内容 在文件尾部添加新内容
  • 二进制b二进制模式 wb rb
  • 更新+可读可写
    1.w+可读可写 但会清空文件原有内容
    2.r+可读可写

输出到文件 fprintf函数

原型int fprintf(FILE * stream, const char* format, ...);
在printf之前加了个文件结构指针参数 后面参数同printf函数 用w模式

读取文件 fscanf函数

原型int fscanf(FILE * stream, const char* format, ...);
在scanf之前加了个文件结构指针参数 后面参数同scanf函数 用r模式

关闭文件 fclose函数

原型fclose(FILE * stream);

示例

#include<stdio.h>
int main()
{
	//创建名为data.txt的文件
	FILE* pFile = fopen("D:/data.txt", "w");
	if (pFile == NULL)
	{
		return -1;//若创建失败
	}
	//若创建成功
	int n = 123;
	double f = 123.456;
	char ch = 'A';

	fprintf(pFile, "%d\n", n);//第一个参数为文件结构指针
	fprintf(pFile, "%lf\n", f);
	fprintf(pFile, "%c\n", ch);
	fclose(pFile);//关闭文件
	return 0;
}

image.png

#include<stdio.h>
int main()
{
	//打开名为data.txt的文件
	FILE* pFile = fopen("D:/data.txt", "r");
	if (pFile == NULL)
	{
		return -1;//若打开失败
	}
	//若打开成功
	int n;
	double f;
	char ch;

	fscanf(pFile, "%d", &n);//第一个参数为文件结构指针
	fscanf(pFile, "%lf", &f);
	fscanf(pFile, "%c", &ch);//fscanf读取了字符\n赋值给了ch

	printf("%d\n", n);
	printf("%lf\n", f);
	printf("%c\n", ch);
	fclose(pFile);//关闭文件
	return 0;
}

image.png

读取单个字符 fgetc函数

原型int fgetc(FILE * stream);
类似getchar() 若读取成功 返回读取到的字符 若读取失败 返回EOF

#include<stdio.h>
int main()
{
	//打开名为data.txt的文件
	FILE* pFile = fopen("D:/data.txt", "r");
	if (pFile == NULL)
	{
		return -1;//若打开失败
	}
	//若打开成功
	char ch;
	while (1)
	{
		ch = fgetc(pFile);
		if (ch == EOF)
			break;//文件结尾或者其他错误
		putchar(ch);
	}
        fclose(pFile);//关闭文件
	return 0;
}

image.png

读取字符串 fgets函数

原型char* fgets(char* str, int num, FILE * stream);
将读取的字符串储存在以str为首的首地址中
num 读取的最大字符数
stream 文件结构指针

#include<stdio.h>
int main()
{
	//打开名为data.txt的文件
	FILE* pFile = fopen("D:/data.txt", "r");
	if (pFile == NULL)
	{
		return -1;//若打开失败
	}
	//若打开成功
	char str[100];
	while (fgets(str, 100, pFile))
		printf("%s", str);
	fclose(pFile);
	return 0;
}

image.png

文件状态判断

  • feof测试文件是否结尾
  • ferror测试文件是否读写出错 int feof(FILE * stream); 若文件结尾返回值为非0 否则返回值为0
    int ferror(FILE * stream); 若文件读写出错返回值为非0 否则返回值为0
#include<stdio.h>
int main()
{
	//打开名为data.txt的文件
	FILE* pFile = fopen("D:/data.txt", "r");
	if (pFile == NULL)
	{
		return -1;//若打开失败
	}
	//若打开成功
	char ch;
	while (1)
	{
		ch = fgetc(pFile);
		if (ch == EOF)
		{
			if (feof(pFile) != 0)
				printf("end of file\n");
			if (ferror(pFile) != 0)
				printf("file access error\n");
			break;
		}
		putchar(ch);
	}
	fclose(pFile);
	return 0;
}

image.png

写入单个字符 fputc函数

原型int fputc(int character, FILE * stream);
输入写入的字符和文件结构指针
写入成功返回刚刚写入的字符 文件结尾或失败返回EOF

#include<stdio.h>
int main()
{
	FILE* pFile = fopen("D:/data.txt", "w");//写模式
	if (pFile == NULL)
	{
		return -1;
	}

	char str[] = "HelloWorld\n";
	char* p = str;
	while (*p != '\0')
	{
		//向文件中写入一个字符
		fputc(*p, pFile);
		p++;
	}
	fclose(pFile);
	return 0;
}

写入一串字符串 fputs函数

原型fputs(const char* str, FILE * stream);
输入写入的字符串和文件结构指针
写入成功返回非负值 写入失败返回EOF

#include<stdio.h>
int main()
{
	FILE* pFile = fopen("D:/data.txt", "w");//写模式
	if (pFile == NULL)
	{
		return -1;
	}

	char str[] = "HelloWorld\n";
	for (int i = 0; i < 5; i++)
	{
		//写入五行
		fputs(str, pFile);
	}
	fclose(pFile);
	return 0;
}

刷新缓存 fflush函数

原型fflush(FILE * stream);
文件操作函数数据会先写到缓存里 然后一起写入文件 清空缓存区数据 称为刷新缓存 即使没有运行到fclose或关闭程序 也可以保存到文件中
成功刷新返回0 否则返回EOF

文件偏移

文件指针移动 fseek函数

原型int fseek(FILE * stream,long offset, int origin);
输入 文件结构指针 文件指针偏移量 从什么位置开始偏移
如果成功 返回0 否则返回一个非零值

fseek(pFile, 5,SEEK_SET);从文件开头偏移5个字节
fseek(pFile, 0,SEEK_SET);文件指针回到文件最开始
fseek(pFile,-5,SEEK_END);从文件结尾偏移-5个字节

#include<stdio.h>
int main()
{
	FILE* pFile = fopen("D:/data.txt", "r");//读模式
	if (pFile == NULL)
	{
		return -1;
	}

	char ch;

	//从文件开头偏移5个字节
	fseek(pFile, 5,SEEK_SET); 
	ch = fgetc(pFile);
	putchar(ch);

	//从文件结尾偏移-5个字节
	fseek(pFile, -5,SEEK_END);
	ch = fgetc(pFile);
	putchar(ch);

	fclose(pFile);
	return 0;
}

image.png

获取当前文件指针位置 ftell函数

原型long ftell (FILE * stream);
输入文件结构指针
成功返回当前指针位置 失败返回-1
如果将文件指针先偏移到末尾 再获取文件指针当前的位置 就能知道该文件内有多少个字节 即该文件的大小

#include<stdio.h>
int main()
{
	FILE* pFile = fopen("D:/data.txt", "r");//读模式
	if (pFile == NULL)
	{
		return -1;
	}

	char ch;

	//偏移到文件结尾  
	fseek(pFile, 0,SEEK_END); 
	//获取当前文件指针位置
	long length = ftell(pFile);
	printf("size of file %ld\n", length);

	fclose(pFile);
	return 0;
}

image.png

指针归零 rewind函数

void rewind (FILE * stream); 输入指针 无输出
文件指针回到文件最开始

更新文件

  • 文件从写操作转换为读操作前 必须使用fflush fseek rewind其中一个函数
  • 文件从读操作转换为写操作前 必须使用fseek rewind其中一个函数
  • 在代码中读写操作转换的地方加入必要函数 如果仅需要读写操作转换 但无需变动文件指针 可以在当前位置偏移0字节fseek(pFile,0,SEEK_CUR);

eg 把Helloworld的H改成h
代码中使用fgetc 读取文件中的每个字符 若读到字符H 则把这个字符使用fputc修改为h 注意fgetc读取到字符H后 文件指针已经指向了下一个字符 所以 若读取到字符H 需要将文件指针向前移动一个字节 再进行修改

#include<stdio.h>
void fileEofOrError(FILE* pFile)
{
	if (feof(pFile) != 0)//测试文件是否结尾
		printf("end of file\n");
	else if (ferror(pFile) != 0)//测试文件是否读写出错
		printf("file access error\n");
}
int main()
{
	FILE* pFile = fopen("D:/data.txt", "r+");
	if (pFile == NULL)
	{
		return -1;
	}

	char ch;

	while (1)
	{
		ch = fgetc(pFile); 
		if (ch == EOF)
		{
			fileEofOrError(pFile); 
			break;
		}
		if (ch == 'H')//文件指针向前移动一个字节
		{
			//读转写
			fseek(pFile, -1, SEEK_CUR);
			ch = fputc('h', pFile);
			if (ch == EOF)
			{
				fileEofOrError(pFile);
				break;
			}
			//写转读
			fflush(pFile);
		}
	}

	fclose(pFile);
	return 0;
}

image.png

数值与字符串

将数值转化成字符串保存

数值位数越多 占用空间越大

int numbers[8] = { 1,12,123,1234,12345,10,123456,1234567 };转换成字符串保存到文件中

#include<stdio.h>
int main()
{
	FILE* pFile = fopen("D:/data.txt", "w");
	if (pFile == NULL)
		return -1;
	int numbers[8] = { 1,12,123,1234,12345,10,123456,1234567 };
	for (int i = 0; i < 8; i++)
		fprintf(pFile, "%d\n", numbers[i]);
	fclose(pFile);
	return 0;
}

image.png

读取字符串转成数值

#include<stdio.h>
int main()
{
	FILE* pFile = fopen("D:/data.txt", "r");
	if (pFile == NULL)
		return -1;
	int numbers[8] = { 0 };
	for (int i = 0; i < 8; i++)
		fscanf(pFile, "%d" , & numbers[i]);
	for (int i = 0; i < 8; i++)
		printf("%d\n", numbers[i]);
	fclose(pFile);
	return 0;
}

image.png

除了使用固定长度的循环 还可以通过函数fscanf 的返回值判断是否已经读完文件
函数fscanf的返回值的意义为 参数列表中成功填充的参数个数 若文件读取失败或文件结尾则返回EOF
若返回EOF 可以通过feof以及ferror函数查询具体的原因 我们可以使用之前定义的函数fileEofOrError 来判断究竟是哪种情况

#include<stdio.h>
void fileEofOrError(FILE* pFile)
{
	if (feof(pFile) != 0)//测试文件是否结尾
		printf("end of file\n");
	else if (ferror(pFile) != 0)//测试文件是否读写出错
		printf("file access error\n");
}
int main()
{
	FILE* pFile = fopen("D:/data.txt", "r");
	if (pFile == NULL)
		return -1;

	int numbers[8] = { 0 };
	int count = 0;

	while (1)
	{
		if (count >= 8)//为防止越界 若已经填满8个元素 则不继续读取
		{
			printf("numbers is full\n");
			break;
		}
		//fscanf从文件中获取字符串并转换为数值
		int get = fscanf(pFile, "%d", &numbers[count]);
		printf("%d,", get);//成功填充了几个参数
		if (get == EOF)
		{
			fileEofOrError(pFile);
			break;
		}
		count++;
	}
	putchar('\n');

	for (int i = 0; i < 8; i++)
		printf("%d\n", numbers[i]);
	fclose(pFile);
	return 0;
}

image.png

数值与二进制

除了可以把数值转为字符串保存 还能把数值直接以二进制形式保存成文件

二进制输出 fwrite函数

原型size_t fwrite(const void *buffer, size_t size,size_t count,FILE*stream);
输入 待写入文件数据的首地址 每一块数据的大小 一共有多少块数据 文件结构指针
返回值 成功写入多少块数据 失败返回0

#include<stdio.h>
void fileEofOrError(FILE* pFile)
{
	if (feof(pFile) != 0)//测试文件是否结尾
		printf("end of file\n");
	else if (ferror(pFile) != 0)//测试文件是否读写出错
		printf("file access error\n");
}
#include<stdio.h>
int main()
{
	FILE* pFile = fopen("D:/data.txt", "w");
	if (pFile == NULL)
		return -1;
	//装有数值的数组
	int numbers[8] = { 1,12,123,1234,12345,10,123456,1234567 };
        //numbers转化为首元素指针 同时丢失指针类型信息
        
	//将数组numbers分为1块 每一块sizeof(numbers)大小
	fwrite(numbers, sizeof(numbers), 1, pFile);
        //或者将数组numbers分成8块 每一块sizeof(int)大小
        fwrite(numbers, sizeof(int), 8, pFile);
        
	fclose(pFile);
	return 0;
}

二进制读取 fread函数

原型size_t fread(void *buffer,size_t size, size_t count,FILE *stream );
输入 收数据的首地址 每一块数据的大小 一共有多少块数据 文件结构指针
返回值成功读取多少块数据 失败返回0

#include<stdio.h>
void fileEofOrError(FILE* pFile)
{
	if (feof(pFile) != 0)//测试文件是否结尾
		printf("end of file\n");
	else if (ferror(pFile) != 0)//测试文件是否读写出错
		printf("file access error\n");
}
#include<stdio.h>
int main()
{
	FILE* pFile = fopen("D:/data.txt", "r");
	if (pFile == NULL)
		return -1;
	//接受数据的数组
	int numbers[8] = { 0 };
	//每块读取sizeof(numbers)字节 一共读取1块
	fread(numbers, sizeof(numbers), 1,pFile);
        
	for(int i=0;i<8;i++)
		printf("%d\n", numbers[i]);
	fclose(pFile);
	return 0;
}

image.png

除了读取固定大小的数据 我们也能让fread每次读取一字节数据 直到文件结尾或接收的空间存满为止

#include<stdio.h>
void fileEofOrError(FILE* pFile)
{
	if (feof(pFile) != 0)//测试文件是否结尾
		printf("end of file\n");
	else if (ferror(pFile) != 0)//测试文件是否读写出错
		printf("file access error\n");
}
#include<stdio.h>
int main()
{
	FILE* pFile = fopen("D:/data.txt", "r");
	if (pFile == NULL)
		return -1;
	
	int numbers[8] = { 0 };//接受数据的数组
	char* p = (char*)(numbers);//接受数据的首地址
	int count = 0;//已读取的字节
	while (1)
	{
		//如果数组已经读满8个元素 则不继续读取
		if (count >= sizeof(numbers))
		{
			printf("numbers is full\n");
			break;
		}
		//每块读取1字节 一共读取1块
		int get = fread(p, 1, 1, pFile);//fread函数将读取到的1字节数据 存放到指针p中保存的地址当中
		if (get == EOF)
		{
			fileEofOrError(pFile);
			break;
		}
		p++;//将接收数据的地址向后移动一字节
		count++;
	}
	for(int i=0;i<8;i++)
		printf("%d\n", numbers[i]);
	fclose(pFile);
	return 0;
}

image.png