分享
 
 
 

[原创] 自己写着玩之一: ini 解析器

王朝other·作者佚名  2006-01-10
窄屏简体版  字體: |||超大  

Windows 提供了一套读写 ini 文件的 API,使我们可以方便地通过文件对自己的程序进行配置。Linux 下的用户好像没那么幸运,据我所知,如果要用到配置文件,要么去网上找源码,要么就自己写一个。此外,Windows API 读写 ini 文件效率也很低,每调用一次 API 都要对文件做一次解析。而且写 ini 的 API 只有 WritePrivateProfileString 和 WritePrivateProfileStruct 两个,想写个整数进去还得自己先把它转换成字符串,很不方便。

其实 ini 文件解析起来也不难,毕竟就只有那么点东西。所以抽点时间自己写了一个,觉得还挺好用的。

这个解析器只需要在载入文件之前解析一次,解析时把所有的信息保存在了一个分为两层的 map 中。第一层以段(section)名为键,以该段下的 key map 作为值。第二层,即前面说的 key map,以键(key)名作为 map 的键,其值是这个键所对应的字符串值。读写时通过 section 和 key 名从 map 中找到对应的配置条目,进行适当操作即可。解析器析构时会自动保存,也可以在其析构之前显示调用 save 或者 saveAs 来保存。

解析器的关键实现是 loadString 函数。它从传入的字符串头部开始,逐个字符地分析,根据字符的类型来改变当前的解析状态,每当读完一行时,根据当前状态来修改当前 section 和 key,以及确定是否读入了一个键值。

如果某一行是一个段名,则它应该是这样的格式 [ section_name ],方括号与 section_name 之间及方括号两端可以有空格,解析时应该将之忽略。

如果某一行是键与值,则它是这样的格式 key=value,key 左右两端可以有空格,在解析时应该忽略。但 = 后面的内容直至行尾应该完整保留。

解析过程中只有有限的几种状态,读起来应该不难,时间太晚我就不多解释了。直接把代码给贴出来。

IniParser 使用的都是 STL 里的东西,在 Linux 下编译应该是没有问题的。

/************************************************************************/

/* ini parser designed by leiXure */

/* free to use, modify and redistribute, just don't remove these header.*/

/************************************************************************/

#ifndef __LXRIniParser_h__

#define __LXRIniParser_h__

#ifdef _AFX

#pragma warning(disable : 4786)

#endif

#include <map>

#include <string>

namespace leiXure

{

struct IgnoreCaseLT

{

bool operator()(const std::string& lhs, const std::string& rhs) const

{

return stricmp(lhs.c_str(), rhs.c_str()) < 0;

}

};

class IniParser

{

public:

typedef std::map<std::string, std::string, IgnoreCaseLT> KeyMap;

typedef std::map<std::string, KeyMap, IgnoreCaseLT> SectionMap;

typedef KeyMap::iterator KeyIterator;

typedef SectionMap::iterator SectionIterator;

public:

// default constructor, destructor.

IniParser();

~IniParser();

// construct and load from the file.

IniParser(const char* file_name);

// load an ini file, if the previous ini file is modified,

// the modification would be saved.

bool load(const char* file_name);

// load a C string as ini.

bool loadString(const char* str, unsigned length);

// save the ini to the file from which it was loaded.

bool save();

// save the ini in a file with a name that is different

// from which it was loaded.

bool saveAs(const char* file_name);

// return whether the ini is modified since it's loaded

// or its last saving.

bool isModified() const { return m_modified; }

public: // high level member function.

// the following member functions get the value from

// the section-key entry, interpret it as the specific

// type. def_val, if any, will be used if the

// section-key entry is absent, or if error occurs,

// such as using getInteger from an entry while the

// value could not be converted to integer.

// return as integer value.

long getInteger(const char* section, const char* key, long def_val);

// return as floating-point value.

float getFloat(const char* section, const char* key, float def_val);

// return how many bytes is written into buffer.

long getStruct(const char* section, const char* key, void* buffer, long size);

// save the section-key value to buffer and return length

// of the string. after the call, buffer is null-terminated.

long getString(const char* section, const char* key, const char* def_val, char* buffer, long size);

// save it in dst_str.

long getString(const char* section, const char* key, const char* def_val, std::string& dst_str);

#ifdef _AFX

// save it in a MFC::CString object dst_str.

long getString(const char* section, const char* key, const char* def_val, CString& dst_str);

#endif

// the following member functions set the value of the sepcific

// section-key as the 'value'. if the section-key is absent,

// a new section-key entry is created, and the ini is marked as

// modified.

void setInteger(const char* section, const char* key, long value);

void setFloat(const char* section, const char* key, float value);

void setStruct(const char* section, const char* key, const void* buffer, long size);

void setString(const char* section, const char* key, const char* value);

public: // low level member function.

// return the whole section with name 'section'

const KeyMap& getSection(const char* section) const;

// return all sections in the ini.

const SectionMap& getIni() const { return m_map; }

private:

void saveBeforeLoad();

const char* key_value(const char* section, const char* key);

private:

// disable copy ctor and assignment operator.

IniParser(const IniParser& copy);

IniParser& operator=(const IniParser& rhs);

private:

static const KeyMap ms_emptySection;

SectionMap m_map;

std::string m_file_name;

bool m_modified;

};

}// end of namespace leiXure

#endif//__LXRIniParser_h__

//////////////////////////////////////////////////////////////////////////

// IniParser.cpp

#include "StdAfx.h" // remove this line, if you're not using msvc.

#include "LXRIniParser.h"

#ifdef _AFX

#pragma warning(disable : 4786 4503)

#endif

#include <fstream>

#include <strstream>

using namespace std;

#ifdef _AFX

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

#endif

#define BUFFER_LEN 255

namespace leiXure

{

static const char left_tag = '[';

static const char right_tag = ']';

static const char equal = '=';

static const char cr = '\r';

static const char new_line = '\n';

static const char* empty_str = "";

const IniParser::KeyMap IniParser::ms_emptySection;

IniParser::IniParser() : m_modified(false)

{

}

IniParser::IniParser(const char* file_name) : m_modified(false)

{

load(file_name);

}

IniParser::~IniParser()

{

if(m_modified)

save();

}

void IniParser::saveBeforeLoad()

{

if(m_modified)

save();

m_file_name.resize(0);

m_map.clear();

m_modified = false;

}

const char* IniParser::key_value(const char* section, const char* key)

{

SectionIterator itSection = m_map.find(section);

if(m_map.end() != itSection)

{

KeyIterator itKey = itSection->second.find(key);

if(itKey != itSection->second.end())

return itKey->second.c_str();

}

return 0;

}

bool IniParser::load(const char* file_name)

{

saveBeforeLoad();

ifstream file(file_name);

if(!file)

return false;

file.seekg(0, ios::end);

long len = file.tellg();

if(len < 0)

return false;

char* buffer = new char[len + 1];

if(0 == buffer)

return false;

file.seekg(0, ios::beg);

file.read(buffer, len);

buffer[len = file.gcount()] = 0;

loadString(buffer, len);

m_file_name = file_name;

delete[] buffer;

return true;

}

bool IniParser::loadString(const char* str, unsigned length)

{

saveBeforeLoad();

if(length && 0==str)

return false;

enum status

{

after_left_tag,

after_section_name,

after_section_name_ws,

after_key_name,

after_key_name_ws,

after_equal,

start

};

string section; // current section.

string key; // current key.

status sta = start; // parsing state.

const char* p = str; // current parsing position.

const char* beg = p; // beginning of current element.

const char* last_ws = p; // last white space character.

for(; length; ++p, --length)

{

if(new_line == *p)

{

if(after_equal == sta)

{

if(cr == *(p - 1))

--p;

m_map[section][key] = string(beg, p - beg);

if(cr == *p)

++p;

}

sta = start;

}

else

{

switch(sta)

{

case after_left_tag:

if(right_tag == *p)

{

sta = start;

section = empty_str; // empty section name.

}

else if(!isspace((unsigned char)*p))

{

sta = after_section_name;

beg = p;

}

break;

case after_section_name:

if(right_tag == *p)

{

sta = start;

section = string(beg, p - beg);

}

else if(isspace((unsigned char)*p))

{

sta = after_section_name_ws;

last_ws = p;

}

break;

case after_section_name_ws:

if(right_tag == *p)

{

sta = start;

section = string(beg, last_ws - beg);

}

else if(!isspace((unsigned char)*p))

{

sta = after_section_name;

}

break;

case after_key_name:

if(equal == *p)

{

sta = after_equal;

key = string(beg, p - beg);

beg = p + 1;

}

else if(isspace((unsigned char)*p))

{

sta = after_key_name_ws;

last_ws = p;

}

break;

case after_key_name_ws:

if(equal == *p)

{

sta = after_equal;

key = string(beg, last_ws - beg);

beg = p + 1;

}

else if(!isspace((unsigned char)*p))

{

sta = after_key_name;

}

break;

case start:

if(left_tag == *p)

{

sta = after_left_tag;

}

else if(equal == *p)

{

key = empty_str; // an empty key.

sta = after_equal;

beg = p + 1;

}

else if(!isspace((unsigned char)*p))

{

sta = after_key_name;

beg = p;

}

break;

}

}

}

if(after_equal == sta)

m_map[section][key] = string(beg, p - beg);

return true;

}

bool IniParser::save()

{

if(0==m_file_name.c_str() || 0==m_file_name[0])

return false; // file name invalid

ofstream file(m_file_name.c_str());

if(!file)

return false;

for(SectionMap::iterator itApp=m_map.begin(); itApp!=m_map.end(); ++itApp)

{

file << left_tag << itApp->first << right_tag << endl;

for(KeyMap::iterator itKey=itApp->second.begin(); itKey!=itApp->second.end(); ++itKey)

file << itKey->first << equal << itKey->second << endl;

file << endl;

}

m_modified = false;

return true;

}

bool IniParser::saveAs(const char* file_name)

{

string old_file_name = m_file_name;

m_file_name = file_name;

if(save())

return true;

m_file_name = old_file_name;

return false;

}

long IniParser::getInteger(const char* section, const char* key, long def_val)

{

istrstream(key_value(section, key)) >> def_val;

return def_val;

}

float IniParser::getFloat(const char* section, const char* key, float def_val)

{

istrstream(key_value(section, key)) >> def_val;

return def_val;

}

long IniParser::getStruct(const char* section, const char* key, void* buffer, long size)

{

key = key_value(section, key);

if(0==key || 0==*key)

return 0;

const char* p = key;

char* dst = (char*)buffer;

long read_len = 0;

char value;

while(*p && read_len<size)

{

switch(*p)

{

case '0': case '1': case '2': case '3': case '4':

case '5': case '6': case '7': case '8': case '9':

value = *p - '0';

break;

case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':

value = *p - 'a' + 10;

break;

case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':

value = *p - 'A' + 10;

break;

default:

return read_len;

}

if(0 == (p - key)%2)

*(dst + read_len) = value << 4;

else

*(dst + read_len) = (*(dst + read_len) & 0xf0) + value;

if(0 == (++p - key)%2)

++read_len;

}

return read_len;

}

long IniParser::getString(const char* section, const char* key, const char* def_val, char* buffer, long size)

{

key = key_value(section, key);

if(0 == key)

key = def_val;

strncpy(buffer, key, size - 1);

buffer[size - 1] = 0;

return strlen(buffer);

}

long IniParser::getString(const char* section, const char* key, const char* def_val, std::string& dst_str)

{

key = key_value(section, key);

dst_str = key ? key : def_val;

return dst_str.length();

}

#ifdef _AFX

long IniParser::getString(const char* section, const char* key, const char* def_val, CString& dst_str)

{

key = key_value(section, key);

dst_str = key ? key : def_val;

return dst_str.GetLength();

}

#endif//_AFX

void IniParser::setInteger(const char* section, const char* key, long value)

{

char buffer[BUFFER_LEN + 1];

ostrstream ostr(buffer, BUFFER_LEN);

ostr << value;

buffer[ostr.pcount()] = 0;

setString(section, key, buffer);

}

void IniParser::setFloat(const char* section, const char* key, float value)

{

char buffer[BUFFER_LEN + 1];

ostrstream ostr(buffer, BUFFER_LEN);

ostr << value;

buffer[ostr.pcount()] = 0;

setString(section, key, buffer);

}

inline char bin2hex(char bin)

{

return bin<10 ? bin+'0' : bin-10+'A';

}

void IniParser::setStruct(const char* section, const char* key, const void* buffer, long size)

{

char* dst = new char[size*2 + 1];

if(dst)

{

const char* src = (const char*)buffer;

for(long i=0; i<size; ++i)

{

dst[i << 1] = bin2hex((src[i] >> 4) & 0x0f );

dst[(i << 1) + 1] = bin2hex(src[i] & 0x0f);

}

dst[i << 1] = 0;

setString(section, key, dst);

delete[] dst;

}

}

void IniParser::setString(const char* section, const char* key, const char* value)

{

m_map[section][key] = value;

m_modified = true;

}

const IniParser::KeyMap& IniParser::getSection(const char* section) const

{

SectionMap::const_iterator itApp = m_map.find(section);

return m_map.end()==itApp ? ms_emptySection : itApp->second;

}

}

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有