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

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

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

1. 常見的文字檔案配置

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

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

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. 總結

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