INI檔案解析、遍歷
阿新 • • 發佈:2019-02-19
工作中時常需要給軟體新增配置檔案功能,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);
// 遍歷鍵值表
for (auto i = it->kValueTable.begin(); i != it->kValueTable.end(); ++i)
{
file.Write(i->first.c_str(), i->first.length() * 2);
file.Write(&equal, 2);
file.Write(i->second.c_str(), i->second.length() * 2);
file.Write(newLine, 4);
}
}
return true;
}
bool CIni::AddSection(TCHAR *sectionName)
{
std::wstring wSectionName(sectionName);
for (auto it = m_sectionTable.begin(); it != m_sectionTable.end(); ++it)
{
if (it->sectionName == wSectionName)
{
return false;
}
}
Section section;
section.sectionName = wSectionName;
m_sectionTable.push_back(section);
return true;
}
bool CIni::AddKValue(TCHAR *sectionName, TCHAR *key, TCHAR *value)
{
std::wstring wSectionName(sectionName);
std::wstring wKey(key);
std::wstring wValue(value);
for (auto it = m_sectionTable.begin(); it != m_sectionTable.end(); ++it)
{
if (it->sectionName == wSectionName)
{
it->kValueTable[wKey] = wValue;
return true;
}
}
return false;
}
const TCHAR *CIni::GetValue(TCHAR * sectionName, TCHAR * key)
{
std::wstring wSectionName(sectionName);
std::wstring wKey(key);
for (auto it = m_sectionTable.begin(); it != m_sectionTable.end(); ++it)
{
if (it->sectionName == wSectionName)
{
auto temp = it->kValueTable.find(wKey);
if (temp == it->kValueTable.end())
{
return nullptr;
}
else
{
return temp->second.c_str();
}
}
}
return nullptr;
}
bool CIni::UpdateValue(TCHAR * sectionName, TCHAR * key, TCHAR * value)
{
std::wstring wSectionName(sectionName);
std::wstring wKey(key);
std::wstring wValue(value);
for (auto it = m_sectionTable.begin(); it != m_sectionTable.end(); ++it)
{
if (it->sectionName == wSectionName)
{
auto temp = it->kValueTable.find(wKey);
if (temp == it->kValueTable.end())
{
return false;
}
else
{
temp->second = wValue;
return true;
}
}
}
return false;
}
int CIni::Parse(TCHAR * text, int size, SectionTable & sectionTable)
{
TCHAR sectionName[100] = {0}; // 節名
int sectionIndex = 0; // 節名下標
TCHAR key[100] = { 0 }; // 鍵
int keyIndex = 0; // 鍵下標
TCHAR value[2000] = {0}; // 值
int valueIndex = 0; // 值下標
bool isSection = false; // 當前字元是節名
bool isKey = false; // 當前字元是鍵
bool isValue = false; // 當前字元是值
bool isComment = false; // 當前字元是註釋
// 失敗行數
int errorRow = 0;
// 小端
if (*text == 0xFEFF)
{
++text;
--size;
}
// 大端不能解析
if (*text == 0xFFFE)
{
return -1;
}
for (int i = 0; i < size; ++i)
{
if (text[i] == _T('['))
{
if (!isComment)
{
if (isKey)
{
key[keyIndex] = text[i];
++keyIndex;
}
else if (isValue)
{
value[valueIndex] = text[i];
++valueIndex;
}
else if (isSection)
{
++errorRow;
return errorRow;
}
else
{
TCHAR *pTemp = text + i + 1;
while (!(*pTemp == _T('\r') && *(pTemp + 1) == _T('\n')) && *pTemp != 0)
{
if (*pTemp == _T('='))
{
isKey = true;
memset(key, sizeof(key), 0);
key[0] = text[i];
keyIndex = 1;
break;
}
else if (*pTemp == _T(']'))
{
TCHAR *pTemp2 = pTemp + 1;
while (!(*pTemp2 == _T('\r') && *(pTemp2 + 1) == _T('\n')) && *pTemp2 != 0)
{
if (*pTemp2 != _T(' '))
{
break;
}
++pTemp2;
}
if (*pTemp2 == 0)
{
++errorRow;
return errorRow;
}
if ((*pTemp2 == _T('\r') && *(pTemp2 + 1) == _T('\n')))
{
isSection = true;
memset(sectionName, sizeof(sectionName), 0);
sectionIndex = 0;
break;
}
}
++pTemp;
}
if ((*pTemp == _T('\r') && *(pTemp + 1) == _T('\n')) || *pTemp == 0)
{
++errorRow;
return errorRow;
}
}
}
}
else if (text[i] == _T(']'))
{
if (!isComment)
{
if (isSection)
{
sectionName[sectionIndex] = 0;
Section section;
section.sectionName = sectionName;
sectionTable.push_back(section);
isSection = false;
memset(sectionName, sizeof(sectionName), 0);
sectionIndex = 0;
TCHAR *pTemp = text + i + 1;
while (!(*pTemp == _T('\r') && *(pTemp + 1) == _T('\n')) && *pTemp != 0)
{
if (*pTemp != _T(' '))
{
break;
}
++pTemp;
}
if (!(*pTemp == _T('\r') && *(pTemp + 1) == _T('\n')) && *pTemp != 0)
{
++errorRow;
return errorRow;
}
}
else if (isKey)
{
key[keyIndex] = text[i];
++keyIndex;
}
else if (isValue)
{
value[valueIndex] = text[i];
++valueIndex;
}
else
{
++errorRow;
return errorRow;
}
}
}
else if (text[i] == _T('='))
{
if (!isComment)
{
if (isKey)
{
key[keyIndex] = 0;
isKey = false;
if (isValue)
{
++errorRow;
return errorRow;
}
isValue = true;
}
else if (isSection)
{
sectionName[sectionIndex] = text[i];
++sectionIndex;
}
else if (isValue)
{
value[valueIndex] = text[i];
++valueIndex;
}
else
{
++errorRow;
return errorRow;
}
}
}
else if ((text[i] == _T('\r') && text[i + 1] == _T('\n')))
{
isComment = false;
if (isValue)
{
if (isSection || isKey)
{
++errorRow;
return errorRow;
}
value[valueIndex] = 0;
std::wstring wKey(key);
std::wstring wValue(value);
if (sectionTable.size() <= 0)
{
++errorRow;
return errorRow;
}
sectionTable[sectionTable.size() - 1].kValueTable[wKey] = wValue;
memset(key, sizeof(key), 0);
keyIndex = 0;
isValue = false;
memset(value, sizeof(value), 0);
valueIndex = 0;
}
else if (isKey || isSection)
{
++errorRow;
return errorRow;
}
++errorRow;
++i;
}
else if (text[i] == 0)
{
if (isValue && !isKey && !isSection)
{
value[valueIndex] = 0;
std::wstring wKey(key);
std::wstring wValue(value);
if (sectionTable.size() <= 0)
{
++errorRow;
return errorRow;
}
sectionTable[sectionTable.size() - 1].kValueTable[wKey] = wValue;
}
else if (isKey)
{
++errorRow;
return errorRow;
}
}
else if (isSection)
{
if (text[i] != _T(' '))
{
if (isKey || isValue)
{
++errorRow;
return errorRow;
}
else
{
sectionName[sectionIndex] = text[i];
++sectionIndex;
}
}
}
else if (isKey)
{
if (text[i] != _T(' '))
{
if (isSection || isValue)
{
++errorRow;
return errorRow;
}
else
{
key[keyIndex] = text[i];
++keyIndex;
}
}
}
else if (isValue)
{
if (text[i] != _T(' '))
{
if (isSection || isKey)
{
++errorRow;
return errorRow;
}
else
{
value[valueIndex] = text[i];
++valueIndex;
}
}
}
else
{
if (text[i] == _T(';'))
{
isComment = true;
}
if (text[i] != _T(' ') && text[i] != 0 && !isComment)
{
isKey = true;
key[keyIndex] = text[i];
++keyIndex;
}
}
}
if (isValue)
{
value[valueIndex] = 0;
std::wstring wKey(key);
std::wstring wValue(value);
if (sectionTable.size() <= 0)
{
++errorRow;
return errorRow;
}
sectionTable[sectionTable.size() - 1].kValueTable[wKey] = wValue;
}
if (sectionTable.size() <= 0)
{
return -1;
}
else
{
return 0;
}
}
std::wstring CIni::ToString()
{
std::wstring context;
// 遍歷節表
for (auto it = m_sectionTable.begin(); it != m_sectionTable.end(); ++it)
{
context += _T(" [");
context += it->sectionName;
context += _T("]\n");
// 遍歷鍵值表
for (auto i = it->kValueTable.begin(); i != it->kValueTable.end(); ++i)
{
context += _T(" |-- ");
context += i->first;
context += _T(" = ");
context += i->second;
context += _T("\n");
}
context += _T("\n");
}
return context;
}
test.cpp
#include <Ini.h>
#include <iostream>
#include <string>
#include <locale>
int main()
{
CIni ini;
if (0 == ini.Load(_T("test.ini")))
{
std::locale loc("chs");
std::wcout.imbue(loc);
std::wcout << ini.ToString();
}
return 0;
}
test.ini
[General]
AutoRun=0
AutoDownload=0
DiskCache=4
AutoShutDown=0
ShowPortal=0
[Category]
count=5
Default=2
Category0=2|4|0|已下載|C:\QQDownload|0
Category1=4|0|7|軟體|C:\QQDownload\Software|0
Category2=7|0|8|音樂|C:\QQDownload\Music|0
Category3=8|0|9|遊戲|C:\QQDownload\Game|0
Category4=9|0|0|電影|C:\QQDownload\Movies|0
[TaskInfo]
ShowReferPage=http://www.qq.com
ReferPage=http://www.qq.com
DefaultCategory=2
UseLastCategory=1
LastCategory=2
執行結果