文章目录
一、头文件 cfg.h二、核心业务 cfg.c三、主函数四、执行结果一、头文件 cfg.h
// 防止多次导入#ifndef __CFG_H__#define __CFG_H__// 兼容 C++#ifdef __cplusplusextern "C" {#endif // __cplusplus// 定义接口时 , 如果函数形参用作输入数据时 , 可以在形参名很后面添加 /*in*/ 注释//// 获取配置项int read_config_file(char *filename /*in*/, char *key /*in*/, char * value/*in out*/, int * value_len /*out*/);// 写出 / 更新配置项int write_or_update_config_file(char *filename /*in*/, char *key /*in*/, char *value/*in*/, int value_len /*in*/);#ifdef __cplusplus}#endif // __cplusplus#endif // __CFG_H__
二、核心业务 cfg.c
#define _CRT_SECURE_NO_WARNINGS#include <stdlib.h>#include <string.h>#include <stdio.h>// 每行最长的大小#define MAX_LINE 256/*** @brief read_config_file 读取配置文件* @param filename 文件名* @param key键* @param value 值* @param value_len 值字符串长度* @return*/int read_config_file(char *filename /*in*/, char *key /*in*/, char *value/*in out*/, int *value_len /*out*/){// 返回值int ret = 0;// 文件指针FILE *fp = NULL;// 灵活使用的临时指针char *p = NULL;// 指向 Value 开始位置的指针char *start = NULL;// 指向 Value 结束位置的指针char *end = NULL;// 存储配置文件中的一行数据char line_buffer[MAX_LINE];// 以只读的方式打开文件fp = fopen(filename, "r");// 如果文件打开失败 , 说明文件不存在 , 直接退出if (fp == NULL){ret = -1;return ret;}// 逐行遍历 配置文件 中的文本数据while (!feof(fp)){// 清空 line_buffer 中的遗留数据 , 避免被上一次写入的数据干扰memset(line_buffer, 0, sizeof(line_buffer));// 获取一行数据fgets(line_buffer, MAX_LINE, fp);// 查找 '=' 字符p = strchr(line_buffer, '=');// 如果没有找到 '=' 字符 , 则退出 , 继续执行下一次循环if (p == NULL){continue;}// 查找 Key 值// 如果找到了 Key 关键字 , 则返回的指针 p 指向 Key 关键字出现的首地址中p = strstr(line_buffer, key);// 如果没有找到 Key 关键字 , 退出执行下一次循环换if (p == NULL){continue;}// 越过 Key 关键字 , 从 Key 关键字后面的内容遍历p = p + strlen(key);// 查找 '=' 字符p = strchr(p, '=');// 如果没有找到 '=' 字符 , 则退出 , 继续执行下一次循环if (p == NULL){continue;}// 越过 '=' 字符 , 从 '=' 字符 后面的内容遍历p = p + 1;// 获取 Value 起始位置for(;;){// 去掉开始位置的空格if (*p == ' '){p ++ ;}else{start = p;if (*start == '\n'){// 进入到该分支 , 说明 Value 值是空的// 直接退出即可goto End;}break;}}// 获取 Value 结束位置// 从 Value 的不为空格的位置开始遍历for(;;){// 遇到空格或回车 , 说明读取到了最后的位置, 或者换行位置if ((*p == ' ' || *p == '\n')){break;}else{p ++;}}end = p;// 通过 间接赋值 设置 Value 值长度*value_len = end - start;// 通过 间接赋值 设置 Value 值数据内容memcpy(value, start, end - start);}End:// 关闭文件if (fp == NULL){fclose(fp);}return 0;}/*** @brief write_or_update_config_file 写出或更新配置项* 遍历每行数据 , 检查 key 键 是否存在* 如果存在 , 就更新对应的 value 值* 如果不存在 , 在文件末尾添加该键值对信息* 格式为 :* key = value** @param filename 文件名称* @param key 键* @param value 值* @param value_len 值的长度* @return*/int write_or_update_config_file(char *filename /*in*/, char *key /*in*/, char * value/*in*/, int value_len /*in*/){// 返回值int ret = 0;// 查询配置文件中 Key 是否存在int key_exist = 0;// 统计的配置文件大小int file_length = 0;// 文件指针FILE *fp = NULL;// 存储读取的每一行配置文件信息char line_buffer[MAX_LINE];// 临时指针char *p = NULL;// 文件数据缓存区char file_buffer[1024 * 4] = {0};// 验证指针有效性if (filename == NULL || key == NULL || value == NULL){ret = -1;printf("error : filename == NULL || key == NULL || value == NULL \n");goto End;}// 使用读写方式打开 filename 文件fp = fopen(filename, "r+");// 如果打开失败 提示失败信息if (fp == NULL){ret = -2;printf("error : fopen \n");}// 如果文件打开失败 , 说明没有文件if (fp == NULL){// 以写的方式 , 打开文本文件 , 如果文件不存在 , 则创建文件fp = fopen(filename, "w+t");// 打开失败 , 直接退出if (fp == NULL){ret = -3;printf("error : fopen \n");goto End;}}// 统计文件大小// 将文件指针移动到末尾fseek(fp, 0L, SEEK_END);// 获取当前指针位置 , 当前指针位置就是文件大小file_length = ftell(fp);// 将文件指针指向开始位置fseek(fp, 0L, SEEK_SET);// 文件大小不能超过 4Kif (file_length > 1024 * 4){ret = -3;printf("File Size More Than 4K\n");goto End;}// 逐行遍历配置文件while (!feof(fp)){// 将 line_buffer 数据清空memset(line_buffer, 0, sizeof(line_buffer));// 获取 fp 文件的一行数据 , 保存到 line_buffer 数组中 , 最多获取 MAX_LINE 字节p = fgets(line_buffer, MAX_LINE, fp);// 如果获取失败 , 则返回 NULL// 获取成功 , 返回的是 line_buffer 地址if (p == NULL){break;}// 查询 本行字符数组中是否包含 键 Keyp = strstr(line_buffer, key);// 本行不包含 Key , 将数据行 line_buffer// 追加拷贝到 file_buffer 数组中if (p == NULL){strcat(file_buffer, line_buffer);continue;}else{// 如果 Key 关键字 在本行 , 则使用新的数据替换原来的数据 , 最后拷贝到 file_buffer 中// 替换本行数据sprintf(line_buffer, "%s = %s\n", key, value);// 将替换的数据 , 追加拷贝到 file_buffer 数组中strcat(file_buffer, line_buffer);// 设置 Key 存在标志位key_exist = 1;}}// 如果 Key 关键字不存在 , 直接将数据追加到文件末尾即可if (key_exist == 0){fprintf(fp, "%s = %s\n", key, value);}else // 如果 Key 关键字存在 , 则需要重新写出该文件的数据 , 原来的数据直接删除覆盖{// 先关闭之前的 文件指针if (fp != NULL){fclose(fp);fp = NULL;}// 重新打开文件fp = fopen(filename, "w+t");if (fp == NULL){ret = -4;printf("fopen() err. \n");goto End;}// 将文件的完整数据 , 写出到 fp 中// 注意此处的文件数据 , 没有原来的 键值对数据// 写入了要更新的键值对数据fputs(file_buffer, fp);// 也可以使用 fwrite 函数 , 向文件中写出数据//fwrite(filebuf, sizeof(char), strlen(filebuf), fp);}End:// 关闭文件if (fp != NULL){fclose(fp);}return ret;}
三、主函数
#include <stdio.h>#include <stdlib.h>#include <string.h>#include "cfg.h"// 配置文件名称#define CONFIG_FILE_NAME "D:/File/config.ini"/*** @brief show_menu* 显示应用操作菜单*/void show_menu(){printf("=============================\n");printf("1 Write Config File\n");printf("2 Read Config File\n");printf("0 Quit\n");printf("=============================\n");}/*** @brief read_config* 从配置文件中 , 读取配置文件 键值对 信息* @return*/int read_config(){// 局部变量 返回值 , 用于表示程序状态int ret = 0;// 读取 的配置项// 数组声明会后 , 注意先进行初始化为 0 操作 , 否则其中的数据可能是随机的char key[256] = {0};// Key 键char value[256] = {0}; // Value 值// 值 Value 的长度int value_len = 0;// 提示输入 Key 键printf("\nPlease Input Key : ");// 将 Key 存储到 name 字符串数组中scanf("%s", key);// 从 D:/File/config.ini 读取 键值对 信息ret = read_config_file(CONFIG_FILE_NAME /*in*/, key /*in*/, value/*in*/, &value_len /*out*/);if (ret != 0){printf("error : read_config : %d \n", ret);return ret;}// 打印查询结果printf("Get Value Success , Value = %s \n", value);}/*** @brief write_update_config* 启动 写出 / 更新 配置项 模块 , 执行 写出 / 更新 配置项操作* @return*/int write_update_config(){// 局部变量 返回值 , 用于表示程序状态int ret = 0;// 写出 或 更新 的配置项// 数组声明会后 , 注意先进行初始化为 0 操作 , 否则其中的数据可能是随机的char key[256] = {0};// Key 键char value[256] = {0}; // Value 值// 提示输入 Key 键printf("\nPlease Input Key : ");// 将 Key 存储到 name 字符串数组中scanf("%s", key);// 提示输入 Value 值printf("\nPlease Input Value : ");// 将 Value 值 存储到 value 字符串数组中scanf("%s", value);// 向 D:/File/config.ini 写出或更新 键值对 信息ret = write_or_update_config_file(CONFIG_FILE_NAME /*in*/, key /*in*/, value/*in*/,strlen(value) /*in*/);// 判定执行中是否出错if (ret != 0){printf("error : write_or_update_config : %d \n", ret);return ret;}// 打印成功插入的键值对printf("Input %s = %s Success !\n", key , value);return ret;}/*** @brief main 主函数入口*/void main(){// 用户输入的选项 , 0 退出 , 1 写出配置 , 2 读取配置int user_input;// 启动无限循环for (;;){//显示一个菜单show_menu();// 从命令行接收 int 值 , 作为菜单选择scanf("%d", &user_input);// 根据用户的不同输入 , 进行不同的操作switch (user_input){case 1:// 写出 或 更新配置项write_update_config();break;case 2:// 读取配置项read_config();break;case 0:// 退出程序exit(0);default:// 无法识别的操作 , 提示错误操作 , 继续printf("Input Error !\n");break;}}// 执行完毕会后暂停system("pause");return ;}
四、执行结果
插入数据 :
查询数据 :
如果觉得《【C 语言】文件操作 ( 配置文件读写 | 完整代码示例 ) ★》对你有帮助,请点赞、收藏,并留下你的观点哦!