跳至主要内容

【转】用STL快速编写ini配置文件识别类

作者:Winter

ini文件是技术人员经常用到的一种系统配置方法,如何读取和快速识别ini文件中的内容实现起来比较繁琐。STL强大的功能在于能快速的实现排序、查找、 识别等功能。本文通过STL中的map,string,vector,ifstream等,来快速实现ini文件的识别类class IniFile? 。IniFile可以实现常见查找功能,并提供完整的源码。

1 设计需求:

ini文件的格式一般如下:

[section1] key1=value1 key2=value2 ......  [section2] key1=value1 key2=value2    #注释 ...... 实际的例子是:  #ini for path [path] dictfile = /home/tmp/dict.dat inputfile= /home/tmp/input.txt outputfile= /home/tmp/output.txt  #ini for exe [exe] user= winter       //user name passwd= 1234567    #pass word database= mydatabase 

其中有五种元素:section 名,Key名,value值,注释 #或者//开头,标志字符"[" "]" "="。查找项的对应关系为sectiong-key和value对应。需要得到是value。class IniFile? 要实现的是两个函数:读入ini文件,读取sect-key对应的value值。即实现下面的接口:

class IniFile{ public:     IniFile();     //打开ini文件     bool open(const char* pinipath);     //读取value值     const char* read(const char* psect, const char*pkey);   }; 

2 设计实现:

用ifstream按行读入ini文件的内容

识别每一行的字符串,分析出sectiong,key,value,和注释。

用map<string, string, less >来记录所有的sectiong-key和value。

重新定义class IniFile?

typedef map<string, string, less<string> > strMap; typedef strMap::iterator strMapIt;  const char*const MIDDLESTRING = "_____***_______"; class IniFile { public:     IniFile( ){};     ~IniFile( ){};     bool open(const char* pinipath)     {         return do_open(pinipath);     }     string read(const char*psect, const char*pkey)     {         string mapkey = psect;         mapkey += MIDDLESTRING;         mapkey += pkey;         strMapIt it = c_inimap.find(mapkey);         if(it == c_inimap.end())             return "";         else             return it->second;     } protected:     bool do_open(const char* pinipath)     {         ifstream fin(pinipath);         if(!fin.is_open())             return false;         vector<string> strvect;         while(!fin.eof())         {             string inbuf;             getline(fin, inbuf,'\n');             strvect.push_back(inbuf);         }         if(strvect.empty())             return false;         for_each(strvect.begin(), strvect.end(), analyzeini(c_inimap));         return !c_inimap.empty();     }     strMap c_inimap; }; 

其中do_open是用来真正实现初始化ini内容的函数。先用ifstream fin打开一个文件,然后用is_open判断文件是否正常打开。顺序读取文件的时候用eof()判断是否到文件尾。getline是一个字符处理函数:直接从fin中读取一行。然后用while循环过滤一行末尾的空格等字符。最后保存到一个vector中,完成读入文本工作。其中比较值得关注的是以下为体,你知道为什么这么做么?

  • 用ifstream和getline来读入而不是用fopen和fread。
  • 用is_open判断是否打开,而不是直接读取。
  • 用vector的push_pack而不是insert。
  • 用empty判断是否为空,而不是用size()==0。

下一步用for_each函数来完成字符串的内容提取工作。声明一个结构,实现对操作符()的重载。代码如下:

truct analyzeini{     string strsect;     strMap *pmap;     analyzeini(strMap & strmap):pmap(&strmap){}     void operator()( const string & strini)     {         int first =strini.find('[');         int last = strini.rfind(']');         if( first != string::npos && last != string::npos && first != last+1)         {             strsect = strini.substr(first+1,last-first-1);             return ;         }         if(strsect.empty())             return ;         if((first=strini.find('='))== string::npos)             return ;         string strtmp1= strini.substr(0,first);         string strtmp2=strini.substr(first+1, string::npos);         first= strtmp1.find_first_not_of(" \t");         last = strtmp1.find_last_not_of(" \t");         if(first == string::npos || last == string::npos)             return ;         string strkey = strtmp1.substr(first, last-first+1);         first = strtmp2.find_first_not_of(" \t");         if(((last = strtmp2.find("\t#", first )) != string::npos) ||             ((last = strtmp2.find(" #", first )) != string::npos) ||             ((last = strtmp2.find("\t//", first )) != string::npos)||             ((last = strtmp2.find(" //", first )) != string::npos))         {             strtmp2 = strtmp2.substr(0, last-first);         }         last = strtmp2.find_last_not_of(" \t");         if(first == string::npos || last == string::npos)             return ;         string value = strtmp2.substr(first, last-first+1);         string mapkey = strsect + MIDDLESTRING;         mapkey += strkey;         (*pmap)[mapkey]=value;         return ;     } }; 
这里大量使用了字符串的查找和字串功能。string的find_last_of系列和find系列,功能确实十分强大。所有在string中没有找到都会返回一个变量string::npos。

函数先找sectiong,然后分离key值和value值。符合要求的,把section和key值通过中间加上MIDDLESTRING组成一个新的string,插入map中。这里值得注意的是:

* for_each的使用,结构可以传递参数。 * string的查找函数及返回值 * string的链接和合并函数。 * map的下标操作符的使用。

3 具体使用

把所有代码放在一个头文件中,以后别人使用的时候,只需要包含头文件就可以了,点击查看inifile.h文件。在使用的过程中,注意判断返回值。使用代码如下:

#include <iostream> #include "inifile.h" using namespace std; int main() {     IniFile ini;     if(!ini.open("test.ini"))        return -1;     string strvalue = ini.read("sect1","key1");     if(strvalue.empty())         return -1;     else         cout<<"value="<<strvalue<<endl;     return 0; }      


  • Set MYTITLE = 用STL快速编写ini配置文件识别类

评论

此博客中的热门博文

【转】AMBA、AHB、APB总线简介

AMBA 简介 随着深亚微米工艺技术日益成熟,集成电路芯片的规模越来越大。数字IC从基于时序驱动的设计方法,发展到基于IP复用的设计方法,并在SOC设计中得到了广泛应用。在基于IP复用的SoC设计中,片上总线设计是最关键的问题。为此,业界出现了很多片上总线标准。其中,由ARM公司推出的AMBA片上总线受到了广大IP开发商和SoC系统集成者的青睐,已成为一种流行的工业标准片上结构。AMBA规范主要包括了AHB(Advanced High performance Bus)系统总线和APB(Advanced Peripheral Bus)外围总线。   AMBA 片上总线        AMBA 2.0 规范包括四个部分:AHB、ASB、APB和Test Methodology。AHB的相互连接采用了传统的带有主模块和从模块的共享总线,接口与互连功能分离,这对芯片上模块之间的互连具有重要意义。AMBA已不仅是一种总线,更是一种带有接口模块的互连体系。下面将简要介绍比较重要的AHB和APB总线。 基于 AMBA 的片上系统        一个典型的基于AMBA总线的系统框图如图3所示。        大多数挂在总线上的模块(包括处理器)只是单一属性的功能模块:主模块或者从模块。主模块是向从模块发出读写操作的模块,如CPU,DSP等;从模块是接受命令并做出反应的模块,如片上的RAM,AHB/APB 桥等。另外,还有一些模块同时具有两种属性,例如直接存储器存取(DMA)在被编程时是从模块,但在系统读传输数据时必须是主模块。如果总线上存在多个主模块,就需要仲裁器来决定如何控制各种主模块对总线的访问。虽然仲裁规范是AMBA总线规范中的一部分,但具体使用的算法由RTL设计工程师决定,其中两个最常用的算法是固定优先级算法和循环制算法。AHB总线上最多可以有16个主模块和任意多个从模块,如果主模块数目大于16,则需再加一层结构(具体参阅ARM公司推出的Multi-layer AHB规范)。APB 桥既是APB总线上唯一的主模块,也是AHB系统总线上的从模块。其主要功能是锁存来自AHB系统总...

【转】vector成员函数

函数 表述 c.assign(beg,end) c.assign(n,elem) 将 [beg; end) 区间中的数据赋值给 c 。 将 n 个 elem 的拷贝赋值给 c 。 c.at (idx) 传回索引 idx 所指的数据,如果 idx 越界,抛出 out_of_range 。 c.back() 传回最后一个数据,不检查这个数据是否存在。 c.begin() 传回迭代器重的可一个数据。 c.capacity() 返回容器中数据个数。 c.clear() 移除容器中所有数据。 c.empty() 判断容器是否为空。 c.end() 指向迭代器中的最后一个数据地址。 c.erase(pos) c.erase(beg,end) 删除 pos 位置的数据,传回下一个数据的位置。 删除 [beg,end) 区间的数据,传回下一个数据的位置 。 c.front() 传回地一个数据。 get_allocator 使用构造函数返回一个拷贝。 c.insert(pos,elem) c.insert(pos,n,elem) c.insert(pos,beg,end) 在 pos 位置插入一个 elem 拷贝,传回新数据位置。 在 pos 位置插入 n 个 elem 数据。无返回值。 在 pos 位置插入在 [beg,end) 区间的数据。无返回值。 c.max_size() 返回容器中最大数据的数量。 c.pop_back() 删除最后一个数据。 c.push_back(elem) 在尾部加入一个数据。 c.rbegin() 传回一个逆向队列的第一个数据。 c.rend() 传回一个逆向队列的最后一个数据的下一个位置。 c.resize(num) 重新指定队列的长度。 c.reserve() 保留适当的容量。 c.size() 返回容器中实际数据的个数。 c1.swap(c2) swap(c1,c2) 将 c1 和 c2 元素互换。 ...

【转】VxWorks套接口

int m_socket;   // Open a socket        m_socket = socket(AF_INET, SOCK_STREAM, 0);   第一个参数 domain 说明我们网络程序所在的主机采用的通讯协族 (AF_UNIX 和 AF_INET 等 ). AF_UNIX 只能够用于单一的 Unix 系统进程间通信 , 而 AF_INET 是针对 Internet 的 , 因而可以允许在远程主机之间通信 . VxWorks 套接字仅支持 Internet 域地址族 , 不支持 UNIX 域地址族 . 因此在需要 domain 参数的函数中 , 使用 AF_INET 作为函数参数值 . 第二个参数 type 说明我们网络程序所采用的通讯协议 ( SOCK_STREAM , SOCK_DGRAM 等 ). SOCK_STREAM 表明我们用的是 TCP 协议 , 这样会提供按顺序的 , 可靠 , 双向 , 面向连接的比特流 . SOCK_DGRAM  表明我们用的是 UDP 协议 , 这样只会提供定长的 , 不可靠 , 无连接的通信 . 此外,还有 SOCK_RAW 代表是原始协议套接字 . 第三个参数 protocol, 由于我们指定了 type, 所以这个地方我们一般只要用 0 来代替就可以了 . socket 为网络通讯做基本的准备 , 成功打开则返回一个套接字描述符 , 如果失败则返回 ERROR. 套接字描述符是一个标准的 I/O 系统文件描述符 (fd, file descriptor), 可以被 close(), read(), write() 和 ioctl() 函数使用 .   // Make the socket sending alive messages when connected int flag = 1; setsockopt(m_socket, SOL_SOCKET, SO_KEEPALIVE, (char*)&flag, sizeof(flag));   // increase receive buffer siz...