作者:
Winterini文件是技術人員經常用到的一種系統配置方法,如何讀取和快速識別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 >來記錄所有的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配置文件識別類