因为经常使用配置文件,为了方便之后的使用,决定自己写一个功能比较齐全的操作ini类型的配置文件,因为是在linux 环境使用,所以是针对linux系统的文件。写代码加上测试,花了几天时间,博客记录一波。水平有限,可能有不少问题,希望大佬可以指正。如果有人想使用,一定要自己好好测试。。。
代码分为两个文件,头文件iniparser.h代码如下:
/*
[section] //section 段
key = value //item
程序说明:
1、本程序提供的返回值为int类型的接口,成功都返回0,失败返回负的错误码
2、支持按照string、int、double、boolean类型读写value
3、支持一个section包含重复key,读取所有的value,但不支持重复key的写
4、支持不包含section的配置文件
5、支持添加item、section,删除item或整个section
6、支持不同的注释符
7、支持读取注释以及修改或添加注释(虽然可能没用)
*/
#ifndef __INIPARSER_H__
#define __INIPARSER_H__
#include
#include
#include
源文件iniparser.cpp如下:
#include "iniparser.h"
#include
#include
#include
#include
#include
#include
#define LINE_MAX_SIZE 1024
//#define __DEBUG__
#ifdef __DEBUG__
#define print_info(format, ...) do {\
fprintf(stdout, "[" __FILE__ "][%s:%d] " format "\n", __FUNCTION__ , __LINE__, ##__VA_ARGS__); \
} while(0)
#else
#define print_info(format, ...)
#endif
namespace iniparser
{
CIniParser::CIniParser()
: m_CommentDelim(";#")
{
}
CIniParser::~CIniParser()
{
Release();
}
int CIniParser::Load(const std::string& path)
{
if (path.empty())
return ERR_PARAMETER_INVALID;
//会不会是重新导入新的文件,先将之前的清空
Release();
/* std::string real_path;
if (path[0] == '/' || path[0] == '.') //如果是绝对路径或者以点开头的路径,直接使用该路径
{
real_path = path;
}
else //否则使用$HOME/conf/作为前缀
{
char *conf_path = getenv("HOME");
real_path = std::string(conf_path) + "/" + path;
}
m_FilePath = real_path;
*/
m_FilePath = path;
FILE *fp = fopen(m_FilePath.c_str(), "r");
if (!fp)
{
print_info("fopen %s fail", m_FilePath.c_str());
return ERR_FILE_OPERATION_FAIL;
}
char line[LINE_MAX_SIZE];
//创建一个空段,用来保存文件开始可能存在的没有section的部分
IniSection *pis = new IniSection();
m_Sections.push_back(pis);
std::string comment;
while (fgets(line, sizeof(line), fp))
{
if (line[strlen(line) - 1] != '\n')
{
fclose(fp);
return ERR_LINE_TOO_LONG;
}
std::string clean_line = Trim(line);
//如果是空行或者开头是注释的行,都放到comment中
if (clean_line.empty() ||
find(m_CommentDelim.begin(), m_CommentDelim.end(), clean_line[0]) != m_CommentDelim.end())
{
comment += line;
continue;
}
if (clean_line[0] == '[')
{
pis = ParseSectionLine(line, comment);
if (!pis)
{
print_info("ParseSectionLine fail");
fclose(fp);
return ERR_PARSE_FAIL;
}
m_Sections.push_back(pis);
comment.clear();
}
else
{
IniItem *pii = ParseItemLine(line, comment);
if (!pii)
{
print_info("ParseItemLine fail");
fclose(fp);
return ERR_PARSE_FAIL;
}
pis->items.push_back(pii);
comment.clear();
}
}
fclose(fp);
return 0;
}
int CIniParser::SetCommentDelim(const std::string &delim)
{
if (delim.empty())
return ERR_PARAMETER_INVALID;
m_CommentDelim = delim;
return 0;
}
int CIniParser::Save(const std::string &path)
{
std::string real_path;
if (path.empty())
{
real_path = m_FilePath;
}
else
{
real_path = path;
}
std::string data;
for (std::vector::const_iterator sec_citer = m_Sections.begin();
sec_citer != m_Sections.end(); ++sec_citer)
{
if (!(*sec_citer)->upper_comment.empty())
{
data += (*sec_citer)->upper_comment;
}
if (!(*sec_citer)->real_line.empty())
{
data += (*sec_citer)->real_line;
}
for (std::vector::const_iterator item_citer = (*sec_citer)->items.begin();
item_citer != (*sec_citer)->items.end(); ++item_citer)
{
if (!(*item_citer)->upper_comment.empty())
{
data += (*item_citer)->upper_comment;
}
if (!(*item_citer)->real_line.empty())
{
data += (*item_citer)->real_line;
}
}
}
FILE *fp = fopen(real_path.c_str(), "w");
if (!fp)
{
print_info("fopen %s fail", real_path.c_str());
return ERR_FILE_OPERATION_FAIL;
}
size_t n = fwrite(data.c_str(), 1, data.size(), fp);
if (n != data.size())
{
print_info("fwrite %s fail", real_path.c_str());
fclose(fp);
return ERR_FILE_OPERATION_FAIL;
}
fclose(fp);
return 0;
}
int CIniParser::GetStringValue(const std::string §ion, const std::string &key, std::string *value)
{
IniSection *pis = GetSection(section);
if (pis == NULL)
return ERR_NOT_FOUND_SECTION;
for (std::vector::const_iterator item_citer = pis->items.begin();
item_citer != pis->items.end(); ++item_citer)
{
if ((*item_citer)->key == key)
{
*value = (*item_citer)->value;
return 0;
}
}
return ERR_NOT_FOUND_KEY;
}
int CIniParser::GetIntValue(const std::string §ion, const std::string &key, int *value)
{
std::string v;
int ret = GetStringValue(section, key, &v);
if (ret != 0)
{
return ret;
}
else
{
*value = atoi(v.c_str());
return 0;
}
}
int CIniParser::GetDoubleValue(const std::string §ion, const std::string &key, double *value)
{
std::string v;
int ret = GetStringValue(section, key, &v);
if (ret != 0)
{
return ret;
}
else
{
*value = atof(v.c_str());
return 0;
}
}
int CIniParser::GetBooleanValue(const std::string §ion, const std::string &key, bool *value)
{
std::string v;
int ret = GetStringValue(section, key, &v);
if (ret != 0)
{
return ret;
}
else
{
if (!strcasecmp(v.c_str(), "true") || !strcasecmp(v.c_str(), "1"))
{
*value = true;
return 0;
}
else if (!strcasecmp(v.c_str(), "false") || !strcasecmp(v.c_str(), "0"))
{
*value = false;
return 0;
}
else
{
return ERR_PARSE_FAIL;
}
}
}
int CIniParser::GetStringValues(const std::string §ion, const std::string &key, std::vector *values)
{
IniSection *pis = GetSection(section);
if (pis == NULL)
return ERR_NOT_FOUND_SECTION;
std::vector vs;
for (std::vector::const_iterator item_citer = pis->items.begin();
item_citer != pis->items.end(); ++item_citer)
{
if ((*item_citer)->key == key)
{
vs.push_back((*item_citer)->value);
}
}
if (vs.empty())
{
return ERR_NOT_FOUND_KEY;
}
else
{
*values = vs;
return 0;
}
}
int CIniParser::GetIntValues(const std::string §ion, const std::string &key, std::vector *values)
{
std::vector vs;
int ret = GetStringValues(section, key, &vs);
if (ret != 0)
{
return ret;
}
else
{
std::vector vi;
for (std::vector::const_iterator citer = vs.begin();
citer != vs.end(); ++citer)
{
vi.push_back(atoi(citer->c_str()));
}
*values = vi;
return 0;
}
}
int CIniParser::GetDoubleValues(const std::string §ion, const std::string &key, std::vector *values)
{
std::vector vs;
int ret = GetStringValues(section, key, &vs);
if (ret != 0)
{
return ret;
}
else
{
std::vector vd;
for (std::vector::const_iterator citer = vs.begin();
citer != vs.end(); ++citer)
{
vd.push_back(atof(citer->c_str()));
}
*values = vd;
return 0;
}
}
int CIniParser::GetBooleanValues(const std::string §ion, const std::string &key, std::vector *values)
{
std::vector vs;
int ret = GetStringValues(section, key, &vs);
if (ret != 0)
{
return ret;
}
else
{
std::vector vb;
for (std::vector::const_iterator citer = vs.begin();
citer != vs.end(); ++citer)
{
if (!strcasecmp(citer->c_str(), "true") || !strcasecmp(citer->c_str(), "1"))
{
vb.push_back(true);
}
else if (!strcasecmp(citer->c_str(), "false") || !strcasecmp(citer->c_str(), "0"))
{
vb.push_back(false);
}
else
{
return ERR_PARSE_FAIL;
}
}
*values = vb;
return 0;
}
}
int CIniParser::GetSections(std::vector *sections)
{
std::vector secs;
for (std::vector::const_iterator sec_citer = m_Sections.begin();
sec_citer != m_Sections.end(); ++sec_citer)
{
//过滤掉第一个名为空的section
if (!(*sec_citer)->name.empty())
{
secs.push_back((*sec_citer)->name);
}
}
*sections = secs;
return 0;
}
int CIniParser::GetKeys(const std::string §ion, std::vector *keys)
{
IniSection *pis = GetSection(section);
if (pis == NULL)
return ERR_NOT_FOUND_SECTION;
std::vector ks;
for (std::vector::const_iterator item_citer = pis->items.begin();
item_citer != pis->items.end(); ++item_citer)
{
ks.push_back((*item_citer)->key);
}
*keys = ks;
return 0;
}
int CIniParser::GetItems(const std::string §ion, std::map *items)
{
IniSection *pis = GetSection(section);
if (pis == NULL)
return ERR_NOT_FOUND_SECTION;
std::map os;
for (std::vector::const_iterator item_citer = pis->items.begin();
item_citer != pis->items.end(); ++item_citer)
{
os[(*item_citer)->key] = (*item_citer)->value;
}
*items = os;
return 0;
}
int CIniParser::SetStringValue(const std::string §ion, const std::string &key, const std::string &value)
{
if (key.empty() || value.empty())
return ERR_PARAMETER_INVALID;
IniSection *pis = GetSection(section);
if (pis == NULL)
return ERR_NOT_FOUND_SECTION;
for (std::vector::const_iterator item_citer = pis->items.begin();
item_citer != pis->items.end(); ++item_citer)
{
if ((*item_citer)->key == key)
{
(*item_citer)->value = value;
return 0;
}
}
//如果不存在,创建
IniItem *pii = new IniItem();
pii->key = key;
pii->value = value;
pii->real_line = key + " = " + value + '\n';
pis->items.push_back(pii);
return 0;
}
int CIniParser::SetIntValue(const std::string §ion, const std::string &key, int value)
{
char buf[64];
snprintf(buf, sizeof(buf), "%d", value);
return SetStringValue(section, key, buf);
}
int CIniParser::SetDoubleValue(const std::string §ion, const std::string &key, double value)
{
char buf[64];
snprintf(buf, sizeof(buf), "%f", value);
return SetStringValue(section, key, buf);
}
int CIniParser::SetBooleanValue(const std::string §ion, const std::string &key, bool value)
{
if (value)
{
return SetStringValue(section, key, "true");
}
else
{
return SetStringValue(section, key, "false");
}
}
int CIniParser::AddSection(const std::string §ion)
{
IniSection *pis = GetSection(section);
if (pis != NULL)
return ERR_ALREADY_EXIST;
pis = new IniSection();
pis->name = section;
pis->real_line = std::string("[") + section + "]";
m_Sections.push_back(pis);
return 0;
}
int CIniParser::DeleteSection(const std::string §ion)
{
for (std::vector::iterator sec_citer = m_Sections.begin();
sec_citer != m_Sections.end(); ++sec_citer)
{
if ((*sec_citer)->name == section)
{
for (std::vector::iterator item_citer = (*sec_citer)->items.begin();
item_citer != (*sec_citer)->items.end(); )
{
delete *item_citer;
item_citer = (*sec_citer)->items.erase(item_citer);
}
delete *sec_citer;
m_Sections.erase(sec_citer);
return 0;
}
}
return ERR_NOT_FOUND_SECTION;
}
int CIniParser::DeleteItem(const std::string §ion, const std::string &key)
{
IniSection *pis = GetSection(section);
if (pis == NULL)
return ERR_NOT_FOUND_SECTION;
for (std::vector::iterator item_citer = pis->items.begin();
item_citer != pis->items.end(); )
{
if ((*item_citer)->key == key)
{
delete *item_citer;
item_citer = pis->items.erase(item_citer);
return 0;
}
}
return ERR_NOT_FOUND_KEY;
}
bool CIniParser::HasSection(const std::string §ion)
{
if (GetSection(section) == NULL)
{
return false;
}
return true;
}
bool CIniParser::HasKey(const std::string §ion, const std::string &key)
{
IniSection *pis = GetSection(section);
if (pis == NULL)
return false;
for (std::vector::const_iterator item_citer = pis->items.begin();
item_citer != pis->items.end(); ++item_citer)
{
if ((*item_citer)->key == key)
{
return true;
}
}
return false;
}
int CIniParser::GetSectionUpperComment(const std::string §ion, std::string *comment)
{
IniSection *pis = GetSection(section);
if (pis == NULL)
return ERR_NOT_FOUND_SECTION;
if (!pis->upper_comment.empty() &&
pis->upper_comment[pis->upper_comment.size() - 1] == '\n')
{
*comment = pis->upper_comment.substr(0, pis->upper_comment.size() - 1);
}
else
{
*comment = pis->upper_comment;
}
return 0;
}
int CIniParser::GetSectionRightComment(const std::string §ion, std::string *comment)
{
IniSection *pis = GetSection(section);
if (pis == NULL)
return ERR_NOT_FOUND_SECTION;
*comment = pis->right_comment;
return 0;
}
int CIniParser::GetItemUpperComment(const std::string §ion, const std::string &key, std::string *comment)
{
IniSection *pis = GetSection(section);
if (pis == NULL)
return ERR_NOT_FOUND_SECTION;
for (std::vector::const_iterator item_citer = pis->items.begin();
item_citer != pis->items.end(); ++item_citer)
{
if ((*item_citer)->key == key)
{
if (!(*item_citer)->upper_comment.empty() &&
(*item_citer)->upper_comment[(*item_citer)->upper_comment.size() - 1] == '\n')
{
*comment = (*item_citer)->upper_comment.substr(0, (*item_citer)->upper_comment.size() - 1);
}
else
{
*comment = pis->upper_comment;
}
return 0;
}
}
return ERR_NOT_FOUND_KEY;
}
int CIniParser::GetItemRightComment(const std::string §ion, const std::string &key, std::string *comment)
{
IniSection *pis = GetSection(section);
if (pis == NULL)
return ERR_NOT_FOUND_SECTION;
for (std::vector::const_iterator item_citer = pis->items.begin();
item_citer != pis->items.end(); ++item_citer)
{
if ((*item_citer)->key == key)
{
*comment = (*item_citer)->right_comment;
return 0;
}
}
return ERR_NOT_FOUND_KEY;
}
int CIniParser::SetSectionUpperComment(const std::string §ion, const std::string &comment)
{
IniSection *pis = GetSection(section);
if (pis == NULL)
return ERR_NOT_FOUND_SECTION;
pis->upper_comment = m_CommentDelim[0] + comment + '\n';
return 0;
}
int CIniParser::SetSectionRightComment(const std::string §ion, const std::string &comment)
{
IniSection *pis = GetSection(section);
if (pis == NULL)
return ERR_NOT_FOUND_SECTION;
pis->right_comment = m_CommentDelim[0] + comment;
std::string::size_type index = pis->real_line.find_first_of(m_CommentDelim);
if (index == std::string::npos)
{
//默认使用第一个注释符号
pis->real_line = pis->real_line + " " + m_CommentDelim[0] + comment + '\n';
}
else
{
pis->real_line = pis->real_line.substr(0, index+1) + comment = '\n';
}
return 0;
}
int CIniParser::SetItemUpperComment(const std::string §ion, const std::string &key, const std::string &comment)
{
IniSection *pis = GetSection(section);
if (pis == NULL)
return ERR_NOT_FOUND_SECTION;
for (std::vector::const_iterator item_citer = pis->items.begin();
item_citer != pis->items.end(); ++item_citer)
{
if ((*item_citer)->key == key)
{
(*item_citer)->upper_comment = m_CommentDelim[0] + comment + '\n';
return 0;
}
}
return ERR_NOT_FOUND_KEY;
}
int CIniParser::SetItemRightComment(const std::string §ion, const std::string &key, const std::string &comment)
{
IniSection *pis = GetSection(section);
if (pis == NULL)
return ERR_NOT_FOUND_SECTION;
for (std::vector::const_iterator item_citer = pis->items.begin();
item_citer != pis->items.end(); ++item_citer)
{
if ((*item_citer)->key == key)
{
(*item_citer)->right_comment = m_CommentDelim[0] + comment;
std::string::size_type index = (*item_citer)->real_line.find_first_of(m_CommentDelim);
if (index == std::string::npos)
{
(*item_citer)->real_line = (*item_citer)->real_line + " " + m_CommentDelim[0] + comment;
}
else
{
(*item_citer)->real_line = (*item_citer)->real_line.substr(0, index+1) + comment;
}
return 0;
}
}
return ERR_NOT_FOUND_KEY;
}
void CIniParser::Print()
{
for (std::vector::const_iterator sec_citer = m_Sections.begin();
sec_citer != m_Sections.end(); ++sec_citer)
{
printf("section[%s]\n", (*sec_citer)->name.c_str());
printf("upper_comment[%s]\n", (*sec_citer)->upper_comment.c_str());
printf("right_comment[%s]\n", (*sec_citer)->right_comment.c_str());
printf("items:\n");
for (std::vector::const_iterator item_citer = (*sec_citer)->items.begin();
item_citer != (*sec_citer)->items.end(); ++item_citer)
{
printf("[%s=%s]\n", (*item_citer)->key.c_str(), (*item_citer)->value.c_str());
printf("upper_comment[%s]\n", (*item_citer)->upper_comment.c_str());
printf("right_comment[%s]\n", (*item_citer)->right_comment.c_str());
}
printf("\n");
}
}
//完全按照文件格式打印
void CIniParser::PrintRaw()
{
for (std::vector::const_iterator sec_citer = m_Sections.begin();
sec_citer != m_Sections.end(); ++sec_citer)
{
if (!(*sec_citer)->upper_comment.empty())
{
printf("%s\n", (*sec_citer)->upper_comment.c_str());
}
if (!(*sec_citer)->real_line.empty())
{
printf("%s\n", (*sec_citer)->real_line.c_str());
}
for (std::vector::const_iterator item_citer = (*sec_citer)->items.begin();
item_citer != (*sec_citer)->items.end(); ++item_citer)
{
if (!(*item_citer)->upper_comment.empty())
{
printf("%s\n", (*item_citer)->upper_comment.c_str());
}
if (!(*item_citer)->real_line.empty())
{
printf("%s\n", (*item_citer)->real_line.c_str());
}
}
}
}
void CIniParser::Release()
{
for (std::vector::iterator sec_citer = m_Sections.begin();
sec_citer != m_Sections.end();)
{
for (std::vector::iterator item_citer = (*sec_citer)->items.begin();
item_citer != (*sec_citer)->items.end();)
{
delete *item_citer;
item_citer = (*sec_citer)->items.erase(item_citer);
}
delete *sec_citer;
sec_citer = m_Sections.erase(sec_citer);
}
}
IniItem *CIniParser::ParseItemLine(const std::string &line, const std::string &comment)
{
if (line.empty())
{
print_info("line is empty");
return NULL;
}
std::string left_str;
std::string right_comment;
//按照注释符号,分解成内容和注释两部分
SplitByComment(line, m_CommentDelim, left_str, right_comment);
char key[LINE_MAX_SIZE];
char value[LINE_MAX_SIZE];
int n = sscanf(left_str.c_str(), "%[^= \t] = %s", key, value);
if (n != 2)
{
print_info("sscanf get key=value [%s] fail", left_str.c_str());
return NULL;
}
IniItem *pii = new IniItem();
pii->key = std::string(key);
pii->value = std::string(value);
pii->right_comment = right_comment;
pii->upper_comment = comment;
pii->real_line = line;
return pii;
}
IniSection *CIniParser::GetSection(const std::string §ion)
{
for (std::vector::iterator sec_citer = m_Sections.begin();
sec_citer != m_Sections.end(); ++sec_citer)
{
if ((*sec_citer)->name == section)
{
return *sec_citer;
}
}
return NULL;
}
IniSection *CIniParser::ParseSectionLine(const std::string &line, const std::string &comment)
{
if (line.empty())
{
print_info("line is empty");
return NULL;
}
std::string left_str;
std::string right_comment;
//按照注释符号,分解成内容和注释两部分
SplitByComment(line, m_CommentDelim, left_str, right_comment);
char buf[LINE_MAX_SIZE];
int n = sscanf(left_str.c_str(), "[ %[^] \t]", buf);
if (n != 1)
{
print_info("sscanf get section fail");
return NULL;
}
std::string::size_type index = left_str.find_first_of("]");
if (index == std::string::npos)
{
print_info("can not find ], line[%s] left_str[%s]", line.c_str(), left_str.c_str());
return NULL;
}
if (GetSection(std::string(buf)) != NULL)
{
print_info("section %s already exists", buf);
return NULL;
}
IniSection *pis = new IniSection();
pis->name = std::string(buf);
pis->right_comment = right_comment;
pis->upper_comment = comment;
pis->real_line = line;
return pis;
}
std::string CIniParser::Trim(const std::string& s)
{
if (s.empty())
return s;
std::string s1 = s;
std::string::iterator siter = s1.begin();
while (siter != s1.end())
{
if (isspace(*siter))
siter = s1.erase(siter);
else
break;
}
if (s1.empty())
return std::string();
siter = s1.end();
while (siter != s1.begin())
{
if (isspace(*--siter))
siter = s1.erase(siter);
else
break;
}
return s1;
}
bool CIniParser::SplitByComment(
const std::string &line, const std::string &delim, std::string &left_str, std::string &right_comment)
{
std::string::size_type index = line.find_first_of(delim);
//存在注释
if (index != std::string::npos)
{
//right_comment里包含注释符号
right_comment = Trim(line.substr(index));
left_str = Trim(line.substr(0, index));
return true;
}
else
{
//不存在注释
left_str = Trim(line);
right_comment.clear();
return false;
}
}
}