C/C++ 文件写入读取

C 文件基本操作

1. 文件基本写入

要想实现一个文件的基本写入需要用到下面三个函数(基本流程):

步骤 函数 说明 备注
第一步 fopen() 打开文件 fopen(“文件路径+文件名”, “模式(wb/ab/rb)”) 返回FILE* fp指针
第二步 fwrite() 写入文件 fwrite(“写入的内容”, 1, “内容的大小(字节)”, “FILE* 指针对象”);
第三步 fclose() 关闭文件 fclose(“FILE* 指针对象”); 对文件进行写入/读取之后都需要关闭文件
  • fopen()函数中常用的三种模式: wb/ab/rb
    • wb模式:写入文件,写入的时候每次都会清空文件原有的内容
    • ab模式:写入文件,每次写入内容都是追加到原文件的内容之后(不会清空原文件内容)
    • rb模式:读取文件

示列:

wb模式写入

#include<stdio.h>
#include<stdlib.h>

// 文件写入:使用wb模式(清空内容之后再写入)
void file_fwrite_wb();

int main() {

	file_fwrite_wb();
	
	return 0;
}

// 文件写入:使用wb模式(清空内容之后再写入)
void file_fwrite_wb() {

	// 文件路径
	const char fileName[] = "F:/C++/file/a.txt";

	// 打开文件
	FILE* fp = fopen(fileName, "wb");

	if (fp == NULL) {
		printf("文件打开失败,文件路径不存在\n ");
		return;
	}
	
	// 要写入的字符
	char buf[] = "hello,world";

	// 返回写入的字节数
	int res = fwrite(buf, 1, 11, fp);

	// 关闭文件
	fclose(fp);
}

ab模式只需要把wb替换成ab就可以了,其它代码一样

// 打开文件
FILE* fp = fopen(fileName, "ab");

以字符串的方式写入int类型数据

#include<stdio.h>
#include<stdlib.h>

// 以字符串的方式写入int类型数据
void file_fwrite_str();

int main() {
	file_fwrite_str();
	return 0;
}

// 以字符串的方式写入int类型数据
void file_fwrite_str() {

	// 文件路径
	const char fileName[] = "F:/C++/file/a.txt";

	// 打开文件
	FILE* fp = fopen(fileName, "wb");

	if (fp == NULL) {
		printf("文件打开失败,文件不存在\n ");
		return;
	}

	// 要写入的字符
	int buf[3] = { 0xA1, 0xB1, 0xC1 };

	for (int i = 0; i < 3; i++)
	{
		char text[12];
		// 加了一个,号分割数组的存储
		sprintf(text, "%d,", buf[i]);
		fwrite(text, 1, strlen(text), fp);
	}

	// 关闭文件
	fclose(fp);
}

2. 文件读取

2.1 读取全部内容

#include<stdio.h>
#include<stdlib.h>

// 读取文件内容(读取全部内容)
void read_all_content();

int main() {

	read_all_content();
	
	return 0;
}

// 读取文件内容(读取全部内容)
void read_all_content() {

	// 文件路径
	const char fileName[] = "F:/C++/file/a.txt";

	// 打开文件
	FILE* fp = fopen(fileName, "rb");

	if (fp == NULL) {
		printf("文件打开失败,文件不存在\n ");
		return;
	}

	// 第一种方式读取(直接读取):
	char buf[12];
	// 我的文件内容为: 161,177,193,
	int n = fread(buf, 1, 12, fp);
	// 可调试+断点,打开局部变量窗口查看buf的值
	fclose(fp);
}

2.2 顺序读取

#include<stdio.h>
#include<stdlib.h>

// 顺序读取
void read_buf_content();

int main() {

	read_buf_content();
	
	return 0;
}

// 顺序读取
void read_buf_content() {
	// 文件路径
	const char fileName[] = "F:/C++/file/a.txt";

	// 打开文件
	FILE* fp = fopen(fileName, "rb");

	if (fp == NULL) {
		printf("文件打开失败,文件路径不存在\n ");
		return;
	}

	// 我的文件内容为: 161,177,193, 一共12个字节
	// 所以我创建一个字节缓冲区(4个字节),下面分3次读取
	char buf[4];

	// 顺序读取(文件流):
	// 如果文件很大,则无法一次性读完,可以采用顺序读取,每次读取一定长度,直到读取完
	while (! feof (fp))
	{
		// 每次读取4个字节
		int n = fread(buf, 1, 4, fp);
		if (n > 0) { // n 读取到字节数
			printf("read %d bytes \n", n);
			// 下一次读取会覆盖上次读取(buf缓冲区)的内容,断点+调试
		}
	}

	// 注意: feof()函数是检测文件是否已经读取到末尾
	fclose(fp);
}

3. 随机访问

随机访问:比如MP3、MP4等文件拖动快进时就是随机读取( fseek()函数))
功能 示列
调到第100个字节位置 fseek(fp, 100, SEEK_SET);
调到倒数100个字节的位置 fseek(fp, 100, SEEK_END);
调到当前位置往前100个字节 fseek(fp, -100, SEEK_CUR);
调到当前位置的往后100个字节 fseek(fp, 100, SEEK_CUR);
#include<stdio.h>
#include<stdlib.h>

// 随机访问读取
void random_read_content();

int main() {

	random_read_content();
	
	return 0;
}

// 随机访问读取
void random_read_content() {
	
	// 文件路径+文件名
	const char fileName[] = "F:/C++/file/a.txt";

	FILE* fp = fopen(fileName, "rb");

	if (fp == NULL) {
		printf("文件打开失败,文件路径不存在\n ");
		return;
	}

	// 我的文件内容为: 161,177,193, 一共12个字节
	// 读取文件内容的buf缓冲区
	unsigned char buf[4];

	// 从第8个字节开始读取,读取四个
	fseek(fp, 8, SEEK_SET);
	// n 返回的字节数
	int n = fread(buf, 1, 4, fp);

	fclose(fp);
}

4. 文本形式存储与读写

4.1 以文本形式存储(按行存储,就是在结尾加一个\n)

#include<stdio.h>
#include<stdlib.h>

// 以文本形式存储(按行存储,就是在结尾加一个\n)
void file_fwrite_text();

int main() {

	file_fwrite_text();
	
	return 0;
}

// 以文本形式存储(按行存储,就是在结尾加一个\n)
void file_fwrite_text() {

	// 文件路径+文件名
	const char fileName[] = "F:/C++/file/text.txt";

	FILE* fp = fopen(fileName, "wb");

	if (fp == NULL) {
		printf("文件打开失败,文件路径不存在\n ");
		return;
	}

	// 比如我需要保存服务器的IP和端口号到配置文件(按行存储)
	char ip[] = "192.169.0.1";
	int port = 8080;

	// 定义一行的缓冲区
	char line[256];

	// 保存ip
	sprintf(line, "ip=%s\n", ip);
	fwrite(line, 1, strlen(line), fp);
	// 保存端口号
	sprintf(line, "port=%d\n", port);
	fwrite(line, 1, strlen(line), fp);

	fclose(fp);
}

4.2 按行读取、解析数据

使用fgets()函数,内部会检查,如果都到\n时,停止读取。返回实际读取的字节

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

// 按行读取、解析数据(fgets()函数,内部会检查,如果都到\n时,停止读取。返回实际读取的字节)
void read_line_content();

int main() {

	read_line_content();
	
	return 0;
}

// 按行读取、解析数据
void read_line_content() {

	// 文件路径+文件名
	const char fileName[] = "F:/C++/file/text.txt";

	FILE* fp = fopen(fileName, "rb");

	if (fp == NULL) {
		printf("文件打开失败,文件不存在\n ");
		return;
	}
	// 我的文本数据
	//ip=192.169.0.1
	//port=8080


	// 按行读取数据的缓冲区(一行最多512个字节)
	char buf[512];

	// feof()函数是检测文件是否已经读取到末尾
	while (! feof(fp))
	{
		char* line = fgets(buf, 512, fp);
		if (line) {

			//printf("got: %s \n", line);
			// 解析字符串 (sscanf()只适提取数字,不适合字符串), 需要自己解析(比较麻烦)

			// 字符串为指定char的下标
			int eq = 0;
			
			// 获取字符串长度
			int len = strlen(line);

			// 动态创建新的字符串对象
			char* copy = (char*)malloc(len + 1);
			// copy字符创的下标
			int count = 0;
			
			for (int i = 0; i < len; i++)
			{
				char ch = line[i];
				// 等于换行符的时候跳过
				if (ch == '\n') continue;
				// 如果当下边的char等于'='是。获取'='的下标,赋值给eq
				if (ch == '=')
					eq = i;
				if (eq && i > eq) {
					copy[count] = line[i];
					count++;
				}
			}
			// 字符串结束(为什么len - (eq + 1) -1)
			// 因为len是原本字符串的长度,我们截取之后长度不够; 
			// eq + 1 = 下标是从0开始的; 为什么还要 -1呢, '\n'也算一个字节;
			copy[len - (eq + 1) - 1] = 0;
			// 解析之后赋值给当前的line
			strcpy(line, copy);
			// 释放
			free(copy);

			printf("read: %s \n", line);
		}
	}
	
	fclose(fp);
}

写入到文本的数据:
写入到文本的数据
解析文本数据:
解析文本数据

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章