尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
纯C++实现ini文件操作
纯C实现ini文件操作(可以自从此处下载 https://download.csdn.net/download/huangjiazhi_/93079858头文件#ifndef INIRW_H #define INIRW_H #include string #include map #include vector /** * brief INI配置文件读写类 * 特性 * 1. 行首 # 为整行注释空行、注释保存时原样保留 * 2. 支持读取 string / int / double / bool * 3. 支持修改、新增分段与键值保存不破坏原有注释排版 * 4. 提供格式合法性校验函数输出详细错误行号与原因 * 5. 纯标准C仅依赖STL无第三方库跨平台 */ /// 存储文件原始单行数据区分注释行与配置行 struct IniLine { bool isComment; /// true空行/#注释行false[Section]或keyvalue配置行 std::string rawText; /// 该行原始完整文本 }; class IniRW { public: /** * brief 构造函数自动加载指定INI文件 * param filePath INI文件路径 */ explicit IniRW(const std::string filePath); /// 析构默认 ~IniRW() default; /** * brief 从磁盘重新加载INI文件清空原有缓存 * return 加载成功返回true文件不存在/打开失败返回false */ bool load(); /** * brief 将内存中修改后的配置写回原文件保留全部原始注释与空行 * return 写入成功true文件无法打开返回false */ bool save(); /** * brief 校验当前加载的INI文件格式是否合法 * 校验规则 * 1. [分段]必须成对括号段名不可为空 * 2. 配置行必须包含分隔符键名不可为空 * 3. 所有keyvalue必须归属在某个[Section]下禁止裸键值 * 4. 纯注释/空文件视为合法 * return 格式合法true存在语法错误false */ bool checkValid() const; /** * brief 获取checkValid校验失败的错误描述 * return 错误字符串包含出错行号原因校验通过返回空字符串 */ std::string getErrorMsg() const; /** * brief 判断指定分段是否存在 * param sec 分段名称 * return 存在true不存在false */ bool hasSection(const std::string sec) const; /** * brief 判断指定分段下某个键是否存在 * param sec 分段名 * param key 键名 * return 存在true不存在false */ bool hasKey(const std::string sec, const std::string key) const; // 读取接口 /** * brief 读取字符串类型配置 * param sec 分段名 * param key 键名 * param def 不存在分段/键时返回的默认值 * return 配置值字符串 */ std::string getString(const std::string sec, const std::string key, const std::string def ); /** * brief 读取整型配置 * param sec 分段名 * param key 键名 * param def 默认值 * return 解析后整数解析失败/不存在返回def */ int getInt(const std::string sec, const std::string key, int def 0); /** * brief 读取浮点型配置 * param sec 分段名 * param key 键名 * param def 默认值 * return 解析后浮点数解析失败/不存在返回def */ double getDouble(const std::string sec, const std::string key, double def 0.0); /** * brief 读取布尔配置 * 识别真值1 / true / yes假值0 / false / no * param sec 分段名 * param key 键名 * param def 默认值 * return 布尔结果无法识别返回def */ bool getBool(const std::string sec, const std::string key, bool def false); // 修改/写入接口 /** * brief 设置字符串键值分段不存在自动创建仅更新已有key行不会追加新行 * param sec 目标分段 * param key 键名 * param val 待写入字符串值 */ void setString(const std::string sec, const std::string key, const std::string val); /** * brief 设置整型键值 */ void setInt(const std::string sec, const std::string key, int val); /** * brief 设置浮点键值 */ void setDouble(const std::string sec, const std::string key, double val); /** * brief 设置布尔键值文件中存储true/false文本 */ void setBool(const std::string sec, const std::string key, bool val); /** * brief 【新增接口】在已有分段末尾追加新键值对 * param sec 目标分段名 * param key 新增键名 * param val 键对应字符串值 * 说明setString仅更新原有行该接口会在对应分段尾部插入全新keyvalue行 * 若分段不存在则自动新建分段存放该键值 */ void appendKeyInSection(const std::string sec, const std::string key, const std::string val); private: /// 单分段内键值对存储 using SectionKV std::mapstd::string, std::string; std::mapstd::string, SectionKV m_data; /// 解析后的键值缓存 std::vectorIniLine m_lines; /// 文件原始全部行用于保存还原注释 std::string m_filePath; /// INI文件路径 mutable std::string m_errMsg; /// 校验错误信息mutable允许const函数修改 /** * brief 去除字符串首尾空白字符空格/制表符/换行 * param str 原始字符串 * return 修剪后字符串 */ static std::string trim(const std::string str); /** * brief 清空所有缓存数据、错误信息 */ void clearAll(); /** * brief 查找指定分段在原始行数组中的起始下标与结束下标 * param sec 分段名 * param outStart 输出分段[xxx]所在行下标 * return 分段下一个其他分段的下标无后续分段返回m_lines.size() */ size_t findSectionLineRange(const std::string sec, size_t outStart) const; }; #endif源文件#include IniRW.h #include fstream #include sstream #include algorithm #include cctype #include iomanip // 去除字符串首尾空白 std::string IniRW::trim(const std::string str) { if (str.empty()) return ; // 跳过开头空白 auto start str.begin(); while (start ! str.end() std::isspace(static_castunsigned char(*start))) start; // 跳过末尾空白 auto end str.end(); do --end; while (std::distance(start, end) 0 std::isspace(static_castunsigned char(*end))); return std::string(start, end 1); } // 清空所有缓存与错误信息 void IniRW::clearAll() { m_data.clear(); m_lines.clear(); m_errMsg.clear(); } // 构造自动加载文件 IniRW::IniRW(const std::string filePath) : m_filePath(filePath) { load(); } // 从磁盘加载并解析INI bool IniRW::load() { clearAll(); std::ifstream fin(m_filePath); if (!fin.is_open()) return false; std::string curSec; std::string lineBuf; while (std::getline(fin, lineBuf)) { IniLine lineItem; lineItem.rawText lineBuf; std::string trimLine trim(lineBuf); // 空行 / 行首#注释 标记为注释行 if (trimLine.empty() || trimLine.front() #) { lineItem.isComment true; m_lines.push_back(lineItem); continue; } // 配置/分段行 lineItem.isComment false; m_lines.push_back(lineItem); // 解析 [Section] if (trimLine.front() [ trimLine.back() ]) { curSec trim(trimLine.substr(1, trimLine.size() - 2)); continue; } // 解析 keyvalue size_t eqPos trimLine.find(); if (eqPos std::string::npos) continue; std::string key trim(trimLine.substr(0, eqPos)); std::string val trim(trimLine.substr(eqPos 1)); // 存在当前分段才存入缓存 if (!curSec.empty()) { m_data[curSec][key] val; } } fin.close(); return true; } // 查找分段起始与结束行下标 size_t IniRW::findSectionLineRange(const std::string sec, size_t outStart) const { outStart m_lines.size(); size_t secEndIdx m_lines.size(); bool targetSecFound false; std::string tempSecName; for (size_t i 0; i m_lines.size(); i) { const IniLine item m_lines[i]; if (item.isComment) continue; std::string s trim(item.rawText); if (s.front() [ s.back() ]) { tempSecName trim(s.substr(1, s.size() - 2)); // 已经找到目标分段遇到下一个分段则终止 if (targetSecFound) { secEndIdx i; break; } // 匹配目标分段记录起始行 if (tempSecName sec) { targetSecFound true; outStart i; } } } return secEndIdx; } // 保存内存配置回文件保留注释 bool IniRW::save() { std::ofstream fout(m_filePath, std::ios::trunc); if (!fout.is_open()) return false; std::vectorIniLine newLineList m_lines; // 遍历所有分段更新原有key、新增缺失分段 for (const auto secPair : m_data) { const std::string secName secPair.first; const SectionKV kvMap secPair.second; size_t secStart newLineList.size(); size_t secEnd findSectionLineRange(secName, secStart); // 场景1分段已存在更新分段内所有键值行 if (secStart ! newLineList.size()) { for (size_t i secStart 1; i secEnd; i) { IniLine lineItem newLineList[i]; if (lineItem.isComment) continue; std::string s trim(lineItem.rawText); size_t eq s.find(); if (eq std::string::npos) continue; std::string k trim(s.substr(0, eq)); // 缓存中存在该key则覆盖文本 if (kvMap.find(k) ! kvMap.end()) { lineItem.rawText k kvMap.at(k); } } // 新增代码追加分段内不存在的key std::mapstd::string, bool existKeys; // 收集当前分段原有所有key for (size_t i secStart 1; i secEnd; i) { const IniLine lineItem newLineList[i]; if (lineItem.isComment) continue; std::string s trim(lineItem.rawText); size_t eq s.find(); if (eq std::string::npos) continue; std::string k trim(s.substr(0, eq)); existKeys[k] true; } // 将缓存中不存在的key插入到分段末尾 for (const auto kv : kvMap) { const std::string k kv.first; const std::string v kv.second; if (!existKeys.count(k)) { IniLine newKvLine; newKvLine.isComment false; newKvLine.rawText k v; newLineList.insert(newLineList.begin() secEnd, newKvLine); secEnd 1; } } // } // 场景2不存在该分段追加全新[Section]与所有键值 else { IniLine secLine; secLine.isComment false; secLine.rawText [ secName ]; newLineList.push_back(secLine); for (const auto kv : kvMap) { IniLine kvLine; kvLine.isComment false; kvLine.rawText kv.first kv.second; newLineList.push_back(kvLine); } } } // 逐行写入文件 for (const auto item : newLineList) { fout item.rawText \n; } fout.close(); return true; } // 校验INI格式合法性 bool IniRW::checkValid() const { m_errMsg.clear(); std::string currentSection; for (size_t lineIdx 0; lineIdx m_lines.size(); lineIdx) { const IniLine item m_lines[lineIdx]; std::string trimLine trim(item.rawText); // 注释、空行无需校验 if (item.isComment) continue; // 处理分段行 [xxx] if (trimLine.front() [) { // 缺少右中括号 if (trimLine.back() ! ]) { m_errMsg 第 std::to_string(lineIdx 1) 行错误分段标记[无对应]; return false; } // 段名不能为空 std::string secName trim(trimLine.substr(1, trimLine.size() - 2)); if (secName.empty()) { m_errMsg 第 std::to_string(lineIdx 1) 行错误分段名称不能为空 []; return false; } currentSection secName; continue; } // 普通配置行必须存在等号 size_t eqPos trimLine.find(); if (eqPos std::string::npos) { m_errMsg 第 std::to_string(lineIdx 1) 行错误无效配置行缺少分隔符; return false; } // 键名不能为空 std::string key trim(trimLine.substr(0, eqPos)); if (key.empty()) { m_errMsg 第 std::to_string(lineIdx 1) 行错误键名不能为空; return false; } // 键值行不能在任何分段之外 if (currentSection.empty()) { m_errMsg 第 std::to_string(lineIdx 1) 行错误键值无归属分段必须写在[Section]下方; return false; } } // 全部校验通过 return true; } // 获取校验错误信息 std::string IniRW::getErrorMsg() const { return m_errMsg; } // 判断分段是否存在 bool IniRW::hasSection(const std::string sec) const { return m_data.find(sec) ! m_data.end(); } // 判断分段下键是否存在 bool IniRW::hasKey(const std::string sec, const std::string key) const { auto secIt m_data.find(sec); if (secIt m_data.end()) return false; return secIt-second.find(key) ! secIt-second.end(); } // 读取字符串 std::string IniRW::getString(const std::string sec, const std::string key, const std::string def) { auto secIt m_data.find(sec); if (secIt m_data.end()) return def; auto keyIt secIt-second.find(key); if (keyIt secIt-second.end()) return def; return keyIt-second; } // 读取int int IniRW::getInt(const std::string sec, const std::string key, int def) { std::string valStr getString(sec, key, ); if (valStr.empty()) return def; std::istringstream iss(valStr); int res; if (iss res) return res; return def; } // 读取double double IniRW::getDouble(const std::string sec, const std::string key, double def) { std::string valStr getString(sec, key, ); if (valStr.empty()) return def; std::istringstream iss(valStr); double res; if (iss res) return res; return def; } // 读取bool bool IniRW::getBool(const std::string sec, const std::string key, bool def) { std::string valStr getString(sec, key, ); std::string lowerStr; lowerStr.resize(valStr.size()); std::transform(valStr.begin(), valStr.end(), lowerStr.begin(), ::tolower); if (lowerStr 1 || lowerStr true || lowerStr yes) return true; if (lowerStr 0 || lowerStr false || lowerStr no) return false; return def; } // 设置字符串 void IniRW::setString(const std::string sec, const std::string key, const std::string val) { m_data[sec][key] val; } // 设置int void IniRW::setInt(const std::string sec, const std::string key, int val) { std::ostringstream oss; oss val; m_data[sec][key] oss.str(); } // 设置double void IniRW::setDouble(const std::string sec, const std::string key, double val) { std::ostringstream oss; oss std::fixed val; m_data[sec][key] oss.str(); } // 设置bool void IniRW::setBool(const std::string sec, const std::string key, bool val) { m_data[sec][key] val ? true : false; } // 新增接口实现分段尾部追加键值 void IniRW::appendKeyInSection(const std::string sec, const std::string key, const std::string val) { m_data[sec][key] val; } // 主函数#include mainwindow.h #include QApplication #include inirw.h #include iostream #include QTextCodec // Qt编码头文件 #include QTextCodec #include QMessageBox #include QScreen #include QRect #include QDebug #include windows.h int main(int argc, char *argv[]) { // 1. 获取 UTF-8 编码器 QTextCodec *utf8Codec QTextCodec::codecForName(UTF-8); if (utf8Codec) { // ① 关键设置 locale 编码为 UTF-8影响文件 I/O、系统接口、QString 与 const char* 转换等 QTextCodec::setCodecForLocale(utf8Codec); // ② 兼容旧代码设置 C 字符串const char*默认编码Qt5 已废弃但部分场景仍需 //QTextCodec::setCodecForCStrings(utf8Codec); // ③ 兼容 tr() 翻译设置 tr() 函数的字符串编码Qt5 已废弃若用 tr 需设置 //QTextCodec::setCodecForTr(utf8Codec); } // 2. 可选设置 Windows 控制台编码为 UTF-8避免控制台输出乱码 SetConsoleOutputCP(CP_UTF8); // 控制台输出用 UTF-8 SetConsoleCP(CP_UTF8); // 控制台输入用 UTF-8 // // 解决MinGW控制台cout中文乱码核心代码 QTextCodec *codec QTextCodec::codecForName(UTF-8); QTextCodec::setCodecForLocale(codec); QApplication a(argc, argv); MainWindow w; w.show(); // 初始化INI对象自动加载config.ini IniRW ini(config.ini); // 1. 合法性校验 if (!ini.checkValid()) { qDebug() (QString)INI文件格式校验失败 ini.getErrorMsg().c_str(); return -1; } qDebug() INI文件格式校验通过 \n\n; // 2. 读取各类配置 std::string appName ini.getString(Global, AppName, DefaultApp); int port ini.getInt(Net, Port, 8080); double ratio ini.getDouble(Logic, Rate, 1.0); bool logSwitch ini.getBool(Log, Enable, false); qDebug() 【读取原始配置】\n; qDebug() (QString)AppName: appName.c_str() ; qDebug() (QString)Port: port \n; qDebug() (QString)Rate: ratio \n; qDebug() (QString)LogEnable: (logSwitch ? true : false) \n\n; // 3. 修改已有键值 ini.setString(Global, AppName, IniDemoClient); ini.setInt(Net, Port, 9999); ini.setDouble(Logic, Rate, 3.1415926); ini.setBool(Log, Enable, true); // 4. 【新增功能测试】在已有分段末尾追加全新键值 ini.appendKeyInSection(Global, new, 1024); ini.setString(Global, Describe, 主要修改了xxx); // 4. 新增分段与键 ini.setString(NewSection, Msg, Hello INI RW); ini.setInt(NewSection, NumTest, 666); ini.setString(Global, Describe, 主要修改了xxx); // 5. 保存修改到文件原有#注释全部保留 if (ini.save()) { qDebug() 配置修改完成保存文件成功\n\n; } else { qDebug() 保存INI文件失败; return -2; } // 6. 重新加载验证修改结果 ini.load(); if (ini.checkValid()) { qDebug() 【重新加载后读取修改值】\n; qDebug() 新AppName: ini.getString(Global, AppName).c_str() \n; qDebug() 新端口: ini.getInt(Net, Port) \n; qDebug() 新增字段Msg: ini.getString(NewSection, Msg).c_str() \n; } return a.exec(); }
RELATED

相关推荐

Docker 容器化本地部署 Claude Code 完整硬核教程|隔离沙盒 + 国产模型直连 + 持久化 Skill

Docker 容器化本地部署 Claude Code 完整硬核教程|隔离沙盒 + 国产模型直连 + 持久化 Skill

国内开发者使用 Claude Code 长期面临时区风控、账号封禁、环境污染、会话丢失四大痛点。本文完整落地 Docker 本地镜像部署方案,分官方镜像一键运行、自定义 Dockerfile 构建、docker-compose 企业持久化三套方案;支持 DeepSeek/GLM/ 千问国产模型直连&…

📅 2026/10/10 19:14:53
5大创新功能:TEdit如何彻底改变你的泰拉瑞亚创作体验

5大创新功能:TEdit如何彻底改变你的泰拉瑞亚创作体验

5大创新功能:TEdit如何彻底改变你的泰拉瑞亚创作体验 【免费下载链接】Terraria-Map-Editor TEdit - Terraria Map Editor - TEdit is a stand alone, open source map editor for Terraria. It lets you edit maps just like (almost) paint! It also lets you cha…

📅 2026/10/2 0:13:40
恒生电子×牛客AI面试:每轮省300小时,评估标准反而更统一了

恒生电子×牛客AI面试:每轮省300小时,评估标准反而更统一了

作为国内金融科技领域的代表企业,恒生电子长期吸引大量计算机相关专业人才加入。近年来,随着AI技术快速进入企业经营场景,恒生电子不仅将AI深度应用于招聘流程,也开始重新定义未来的人才标准。 目前,恒生电子通过牛客A…

📅 2026/10/26 13:28:18
MORE NEWS

更多资讯

📰

前缀和与前后缀分解算法详解及应用

1. 题目背景与核心问题解析 这道题目来自USACO 2024年公开赛的铜组B题,题目编号P10274。作为一道面向算法初学者的普及难度题目,它考察的是基础数据结构与算法思想的应用能力。题目名称中的"Logical Moos"暗示了题目内容与逻辑判断和数据处理相…

📰

雷池WAF离线部署与金融行业安全防护实践

1. 项目概述 雷池WAF(Web Application Firewall)是一款专注于保护Web应用安全的防护系统。它通过分析HTTP/HTTPS流量,能够有效防御SQL注入、XSS跨站脚本、CSRF等常见Web攻击。在实际企业环境中,由于安全合规要求或网络环境限制&am…

📰

PowerBuilder数据库开发实战:从案例源码到事务管理与数据窗口

简介:这份资源面向希望系统掌握 PowerBuilder 数据库开发的中初级开发者与维护老系统的技术人员,以经典案例解析为主线,帮助读者从数据窗口设计、事件处理、事务管理到报表图表、数据源连接等环节建立完整的实践认知。压缩包共 172 个文件&am…

📰

2025-2026网络与信息安全毕设选题:易通过、可落地、能答辩

每年十月到十二月,我这边收到的私信里,问得最多的一类就是:"网络与信息安全专业毕设题目到底怎么选,才能稳稳过?"问的人背景差得挺远,有的是科班出身但实践少,有的是跨专业刚补完基础…

📰

网络与信息安全毕业设计易通过选题:边界、闭环与十周推进

1. “易通过”的题到底长什么样:先把评判标准摆上桌 每年到了这个时候,我都会被问到同一个问题:网络与信息安全专业的毕业设计选题,到底怎么挑才不会做到一半崩掉。问的人里有的是自己心里没底,有的是被导师打回来两次…

📰

移动端Web测试代理工具Fiddler与Charles深度解析

1. 移动端Web测试中的代理工具核心价值 在移动应用开发与测试领域,网络通信的可见性始终是调试和优化的关键瓶颈。不同于桌面浏览器提供的开发者工具,移动端环境(特别是原生应用内嵌的WebView)往往缺乏原生的网络监控界面。这正是…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬