工作中时常需要给软件添加配置文件功能,INI文件简单又高效,但是微软的那套API使用太不方便,尤其是INI文件的遍历,所有花了一下午时间造了个轮子,自己解析INI文件。
目前只能读取Unicode小端编码,就是用windows记事本另存为时编码选择Unicode。
INI文件中元素分为节名、键和值,比如
[section]
key=value
键值必须属于某个节,节名必须放在中括号内,键和值中用等号隔开,一行只能有一对键值和一个节(就是必须分行)。本轮子内的键名可以有多个 ‘[’ 但不能有 ‘]’和 ‘=’,键中可以有 ‘[’ 但不能有 ‘]’和 ‘=’(’=’会被截断),值中可以有任何字符。空格和注释会被忽略,注释是以 ‘;’ 开头的行 。
下面是代码:
Ini.h
#pragma once
#include <afxwin.h>
#include <iostream>
#include <map>
#include <vector>
class CIni
{
public:
// Ini文件中的键值对表
typedef std::map<std::wstring, std::wstring> KValueTable;
// Ini文件中的节
typedef struct Section
{
std::wstring sectionName;
KValueTable kValueTable;
}*PSection;
// Ini文件中的节表
typedef std::vector<Section> SectionTable;
CIni();
~CIni();
// 加载Ini文件,返回值 -2:文件读取错误; -1:不支持的编码; 0:正确; > 0:解析错误的行数(第几号)
int Load(TCHAR *fileName);
// 保存Ini文件
bool Save(TCHAR *fileName);
// 清空节表
void Clear() { m_sectionTable.clear(); }
// 添加节
bool AddSection(TCHAR *sectionName);
// 添加键值
bool AddKValue(TCHAR *sectionName, TCHAR *key, TCHAR *value);
// 通过节名和键名获取值
const TCHAR *GetValue(TCHAR *sectionName, TCHAR *key);
// 通过节名和键名修改值
bool UpdateValue(TCHAR *sectionName, TCHAR *key, TCHAR *value);
// 获取节表
const SectionTable &GetSectionTable() const { return m_sectionTable; }
// 解析正常返回 0,不能处理的编码返回-1, 否则返回失败的行数(目前只能解析小端unicode编码)
int Parse(TCHAR *text, int size, SectionTable §ionTable);
// ini文件内容(树形式)
std::wstring ToString();
private:
SectionTable m_sectionTable; // 节表
};
Ini.cpp
#include "Ini.h"
#include <fstream>
CIni::CIni()
{
}
CIni::~CIni()
{
}
int CIni::Load(TCHAR * fileName)
{
// 清空节表
m_sectionTable.clear();
// 打开文件
CFile file;
if (!file.Open(fileName, CFile::modeRead)) { return -1; }
// 获取文件大小
CFileStatus fileStatus;
file.GetStatus(fileStatus);
int fileSize = fileStatus.m_size;
// 读取文件
TCHAR *text = new TCHAR[fileSize / 2 + 10];
int readLength = file.Read(text, fileSize);
if (readLength != fileSize)
{
file.Close();
return -1;
}
// 解析文件
int errorRow = Parse(text, fileSize / 2, m_sectionTable);
delete[] text;
file.Close();
return errorRow;
}
bool CIni::Save(TCHAR * fileName)
{
CFile file;
if (!file.Open(fileName, CFile::modeCreate | CFile::modeWrite)) { return false; }
const TCHAR left = _T('['); // 节名左中括号
const TCHAR right = _T(']'); // 节名右中括号
const TCHAR equal = _T('='); // 等号
const TCHAR newLine[2] = {_T('\r'), _T('\n')}; // 回车换行
// 遍历节表
for (auto it = m_sectionTable.begin(); it != m_sectionTable.end(); ++it)
{
file.Write(&left, 2);
file.Write(it->sectionName.c_str(), it->sectionName.length() * 2);
file.Write(&right, 2);
file.Write(newLine, 4);