1. 程式人生 >實用技巧 >其它 >C++使用文字檔案進行引數配置

C++使用文字檔案進行引數配置


  • 前言:程式中往往會配置一些引數,為了程式的靈活性,我們需要將引數在外部進行配置,在程式執行時進行解析並傳入程式中進行應用。

1. 常見的文字檔案配置

  • 文字只是儲存檔案的一種形式,文字是儲存在本地磁碟中,不易於丟失,儲存一般是不常用的普通訊息,如果涉及到安全之類的資訊,可能需要儲存在資料庫,或者進行混淆加密。
  • 常見的文字配置如下
[config]
name=mz
age=28
ismarried=false
tall=174.5
sex=man

[other]
technique=C++
  • 將上述檔案儲存為conf.ini,在儲存的時候注意使用nodepad++開啟檢視該檔案結尾的行結束符是否正確,在window中和linux中行結束符不一致,有時候容易導致讀取失敗。下圖就是在最後一行未新增行結束符,有可能導致讀取該行配置失敗。
  • C++使用文字檔案進行引數配置

2. 相關程式碼

  • config_ini.h
#ifndef CONFIGINI_H
#define CONFIGINI_H

#include<iostream>
#include<fstream>
#include<cstring>
#include<string>
#include<vector>
#include<cstdlib>

using namespace std;

struct ConfigINIEntry{
    ConfigINIEntry():isComment(false){}
    string index;
    string name;
    string value;
    string comment;
    bool isComment;
};

class ConfigINI
{
public:
    ConfigINI(const char *fileName, bool autoCreate=false);
    void writeConfigFile(const char *fileName=NULL);
    ~ConfigINI();
    /***********getter*************/
    bool getBoolValue(const char* index, const char *name);
    int getIntValue(const char* index, const char *name);
    float getFloatValue(const char* index, const char *name);
    const char* getStringValue(const char* index, const char *name);
    /***********setter*************/
    void setBoolValue(const char* index, const char *name, bool value);
    void setIntValue(const char* index, const char *name, int value);
    void setFloatValue(const char* index, const char *name, float value);
    void setStringValue(const char *index, const char* name, const char* value);

    /******* for test only *******/
    void printAll();
    
    /******* get all Entry *******/
    vector<ConfigINIEntry> datas;
private:
    char str[4096];//for temp string data
    void setStringValueWithIndex(const char *index, const char* name, const char* value);
    char iniFileName[4096];
    char *data;
    const char (*lineData)[4096];
    void loadConfigFile();
    fstream *fStream;
    bool autoSave;
    bool autoCreate;
};

#endif // CONFIGINI_H
  • config_ini.cpp
include "config_ini.h"
#include <iostream>
#include <sstream>
#include <string>
#include <map>

#define CONFIGINI_DEBUG 0
#define log printf
#define mlog(msg, ...) do{\
if(CONFIGINI_DEBUG) printf(msg);\
}while(0)

ConfigINI::ConfigINI(const char *fileNameWithPath, bool _autoCreate):
    data(NULL),
    fStream(NULL),
    autoSave(false),
    autoCreate(_autoCreate)
{
    strcpy(iniFileName, fileNameWithPath);
	//std::cout << "INI Path:" << iniFileName << std::endl;
    loadConfigFile();
}

void ConfigINI::loadConfigFile()
{
    fstream fStream;
    string p = iniFileName;

    fStream.open(p,ios::in);
    if(!fStream){
        if(!autoCreate){
			cout << "inifile [" << iniFileName << "] not found " << endl;
            log("load config, file [%s] not exist", iniFileName);
        }else{
            log("inifile [%s] not found, create a new file", iniFileName);
        }
        return;
    }else{
        mlog("file open OK\n");
    }
    char line[4096];
    char ch;
    int i=0;
    string index;
    string str;
    bool isComment=false;
    while(!fStream.eof()){
        fStream.read(&ch, 1);

        ConfigINIEntry entry;
        if(ch=='#' && i==0) isComment = true;
        if(isComment==true && (ch=='\n' || ch=='\r')) {
            isComment=false;
            line[i++]='\0';
            i=0;
            entry.isComment = true;
            entry.comment = line;
            datas.push_back(entry);
        }
        if(isComment==true) {
            line[i++]=ch;
            continue;
        }
        //zfu: all up for comment
        if(ch != '\n' || ch=='\r') line[i++]=ch;
        else{
            if(i==0) continue;
            line[i]='\0';
            str = string(line);
            mlog("read one line {%s}", str.c_str());
            if(line[0]=='['){
                index = str;
            }else{
                entry.index = index.substr(1,index.length()-2);
                int fIndex = str.find_first_of('=');
                entry.name = str.substr(0,fIndex);
                entry.value = str.substr(fIndex+1, str.length()-fIndex-1);
                datas.push_back(entry);
                mlog("entry: [email protected][%s]\t [email protected][%s]\t [email protected][%s]", entry.index.c_str(), entry.name.c_str(), entry.value.c_str());
            }
            i=0;
        }
    }
    if(i!=0) {
        ConfigINIEntry entry;
        entry.index = str;
        int fIndex = str.find_first_of('=');
        entry.name = str.substr(0,fIndex);
        entry.value = str.substr(fIndex+1, str.length()-fIndex-1);
        datas.push_back(entry);
        mlog("last add entry: [email protected][%s]\t [email protected][%s]\t [email protected][%s]", entry.index.c_str(), entry.name.c_str(), entry.value.c_str());
    }
    fStream.close();
}

ConfigINI::~ConfigINI()
{
    if(autoSave){
        cout<<"AUTO save Config file["<<iniFileName<<"]"<<endl;
        writeConfigFile();
    }
}

void ConfigINI::writeConfigFile(const char* fileName)
{
    autoSave=false;
    if(fileName==NULL) fileName = iniFileName;
    fstream fStream;
    fStream.open(fileName, ios_base::out | ios_base::trunc);
    mlog("start write file[%s]", fileName);
    string index = string("");
    bool withComment = false;
    bool isStart=true;
    for(vector<ConfigINIEntry>::iterator it=datas.begin(); it!= datas.end(); it++){
        ConfigINIEntry entry = *it;
        if(entry.isComment) {
            withComment=true;
            if(isStart) fStream<<entry.comment.c_str()<<endl;
            else fStream<<endl<<entry.comment.c_str()<<endl;
            isStart=false;
            
            mlog("write comment:%s", entry.comment.c_str());
            continue;
        }
        if(strcmp(index.c_str(), entry.index.c_str()) != 0){
            index = entry.index;
            if(withComment || isStart) {
                fStream<<'['<<entry.index<<']'<<endl;
                withComment=false; isStart=false;
                mlog("write index[%s]", entry.index.c_str());
            }
            else{
                fStream<<endl<<'['<<entry.index<<']'<<endl;
                mlog("write index [%s]", entry.index.c_str());
            }
        }
        if (strlen(entry.name.c_str())==0 || strlen(entry.value.c_str())==0) {
            mlog("skip invalid entry");
            continue;
        }
        fStream<<entry.name<<'='<<entry.value<<endl;
        mlog("write :%s=%s", entry.name.c_str(), entry.value.c_str());
    }
    fStream<<endl;
    fStream.close();
    mlog("write configfile[%s] end", fileName);
}

void ConfigINI::setStringValueWithIndex(const char *index, const char* name, const char* value)
{
    autoSave = true;
    ConfigINIEntry entry;
    entry.index = index;
    entry.name = name;
    entry.value = value;
    if(datas.size() == 0) {/*cout<<"data is NULL, push and return"<<endl; */
        datas.push_back(entry);
        return;
    }
    vector<ConfigINIEntry>::iterator it=datas.begin();
    bool findIndex=false;
    bool findName=false;
    vector<ConfigINIEntry>::iterator itInsertPos;
    for(it=datas.begin(); it!=datas.end(); it++){
        if(findIndex==false){
            if(strcmp(it->index.c_str(), index) == 0){
                findIndex=true;
            }
        }
        if(findIndex==true){
            if(strcmp(it->index.c_str(), index) != 0){
                break;
            }else{
                itInsertPos = it;
            }
            if(strcmp(it->name.c_str(), name)==0){
                findName=true;
                itInsertPos = it;
                break;
            }
            continue;
        }
        itInsertPos=it;
    }
    if(findIndex && findName){
        itInsertPos->value = string(value);
        return;
    }

    datas.insert(++itInsertPos, 1, entry);
}

/***********getter*************/
bool ConfigINI::getBoolValue(const char* index, const char *name)
{
    const char *str = getStringValue(index, name);
    if(str == NULL) 
	{
		cout << "not found ["<< index<<"] [" << name << "] in configuration file "<< endl;
		log("not found [%s]-[%s] in configuration file ", index, name); 
		return false;
	}
    if(strcmp(str,"true") == 0) return true;
    else return false;
}

int ConfigINI::getIntValue(const char *index, const char* name)
{
    const char *str = getStringValue(index, name);
    if(!str){
		cout << "not found [" << index << "] [" << name << "] in configuration file " << endl;
        return -1;
    }else{
        return atoi(str);
    }
}

float ConfigINI::getFloatValue(const char* index, const char *name)
{
    const char *str = getStringValue(index, name);
    if(str == NULL) 
	{
		cout << "not found [" << index << "] [" << name << "] in configuration file " << endl;
		return -1.0;}
    return atof(str);
}

const char* ConfigINI::getStringValue(const char* index, const char *name)
{
    mlog("find index[%s]-name[%s]", index, name);
    for(unsigned int i=0; i<datas.size(); i++){
        if(strcmp(datas[i].index.c_str(), index) == 0){
            mlog("find index[%s]", datas[i].index.c_str());
            for(;i<datas.size();i++){
                if(strcmp(datas[i].name.c_str(), name)==0)
                    return datas[i].value.c_str();
            }
        }
    }
	cout << "not found [" << index << "] [" << name << "] in configuration file " << endl;
    //cout<<"DEBUG: ["<<index<<"] of--["<<name<<"] not found"<<endl;
    return NULL;
}

/***********setter*************/
void ConfigINI::setBoolValue(const char* index, const char *name, bool value)
{
    if(value) sprintf(str, "true");
    else sprintf(str, "false");
    setStringValueWithIndex(index,name,str);
}

void ConfigINI::setIntValue(const char* index, const char *name, int value)
{
    sprintf(str, "%d", value);
    setStringValueWithIndex(index,name,str);
}

void ConfigINI::setFloatValue(const char* index, const char *name, float value)
{
    sprintf(str, "%f", value);
    setStringValueWithIndex(index,name,str);
}

void ConfigINI::setStringValue(const char *index, const char* name, const char* value)
{
    setStringValueWithIndex(index,name,value);
}

/*------------------------------------ for DEBUG ---------------------------------------*/
void ConfigINI::printAll()
{
    log("List All Entry of INI File: [ %s ]\n", iniFileName);
    for(vector<ConfigINIEntry>::iterator it=datas.begin(); it!= datas.end(); it++){
        ConfigINIEntry entry = *it;
        if(entry.isComment) {
			//cout<<entry.comment<<endl;
			continue;
		}
        log("  [%s]:\t\%s:\t\t%s\n", entry.index.c_str(), entry.name.c_str(), entry.value.c_str());
    }
}
  • main.cpp
#include "config_ini.h"
#include <iostream>
using namespace std;

int parseConfig(std::string &conf_fname)
{
	const char *ini_path = conf_fname.c_str();
	ConfigINI *ini = new ConfigINI(ini_path,false);

	std::string excelPath("");
	string name = ini->getStringValue("config", "name");
	if (name.empty())
	{
		exit(-1);
	}



    int age = ini->getFloatValue("config", "age");
 	if (tmpTaskNum == -1)
	{
		exit(-1);
	}
	bool ismarried = ini->getBoolValue("config", "ismarried");

    float tall = ini->getIntValue("config", "tall");
 	if (tall == -1)
	{
		exit(-1);
	}

	string technique = ini->getStringValue("other", "technique");
	if (technique.empty())
	{
		exit(-1);
	}

3. 總結

  • 在配置檔案中儘量使用英文,而不是中文,在不同系統下不同的編碼可能導致亂碼問題產生,希望對大家有所幫助。