C++容器之vector&map应用——解析配置文件(二)

前言

这篇博文给大家分享如何读取key-value形式的文件内容,并使用map存储。

需求引入

项目中经常用到key-value形式的配置文件,以前用java直接使用properties文件很方便,但在C/C++中,还是需要读取文件获取内容,就去尝试了一下具体如何实现。这里的配置文件内容如下:

#comment row
 key1=name   #注释
password=    12345

 key2 =   
 =
ip = 8.8.8.8
port = 9999 
time = 10#0
key5
n 10
  b              = 120   

文件中加入了注释(#)、空行、无效数据等内容,以此来模拟非法数据。当然,你也可以使用:替代=,作为key-value的分隔符。

处理思路

首先定义了一个读取配置的类ReadConfig,其中定义了一个readConfig()方法,在该方法中调用handleLine()方法处理每行数据,处理过的每行都是一对key-value,再调用trim()处理key和value前后的空格,trim()方法中调用了定义的判断空格isSpace()方法,最后依次存入map,具体流程如下图:
在这里插入图片描述

源码结构

包括类定义readConfig.h,类实现readConfig.cpp,主程序testRead.cpp
源代码结构

源码
readConfig.h
/*
 * readConfig.h
 *
 *  Created on: 2019年11月30日
 *      Author: xb
 */

#ifndef READCONFIG_H_
#define READCONFIG_H_

#include <map>
#include <string>
#include <iostream>

#define COMMENT '#' //注释符号:#

using std::string;
using std::map;

class ReadConfig {
public:
	ReadConfig();
	virtual ~ReadConfig();

	/**
	 * 判断空格
	 *
	 * @param c 字符
	 *
	 * @return true:是
	 * 		   false:不是
	 */
	bool isSpace(char c);

	/**
	 * 读取配置文件内容,存入map
	 *
	 * @param filename 读取的文件名,可以使用绝对路径或相对路径
	 * @param m 容器map,存放读取的文件内容
	 *
	 * @return true:读取成功
	 * 		   false:读取失败
	 *
	 */
	bool readConfig(const string &filename, map<string, string> &m);

	/**
	 * 去除字符串前后全部空格
	 *
	 * @param str 字符串
	 *
	 */
	void trim(string &str);

	/**
	 * 处理一行数据,格式为 key = value
	 * 有#号的行,如果#在行首,忽略该行;如果#不在行首,则读取#号之前的字符串;
	 * 按'='分割处理完'#'的字符串,切分key和value
	 *
	 * @param line 存放按行读取的内容
	 * @param key 处理过的'='左边的值
	 * @param value 处理过的'='右边的值
	 *
	 * @return true:按行处理成功
	 * 		   false:按行处理失败
	 */
	bool handleLine(const string &line, string &key, string &value);

	void printMap(const map<string, string> &m);
};

#endif /* READCONFIG_H_ */

readConfig.cpp
/*
 * readConfig.cpp
 *
 * @brief 读取的文件格式如下:
 * 1.#注释行...忽略
 * 2.key=value √
 * 3.key = value √
 * 4.key ×
 * 5.空行 ×
 * 6.key = ×
 *
 *
 *  Created on: 2019年11月30日
 *      Author: xb
 */
#include "../base/readConfig.h"
#include <string.h>
#include <fstream>
#include <errno.h>

using namespace std;

ReadConfig::ReadConfig() {
	//cout << "enter default constructor..." << endl;
}

ReadConfig::~ReadConfig() {
	//cout << "bye,program is exiting..." << endl;
}

bool ReadConfig::isSpace(char c) {
	if (' ' == c || '\t' == c) {
		return true;
	}
	return false;
}

void ReadConfig::trim(string &str) {
	if (str.empty()) {
		return;
	}
	unsigned int i, begin, end; //begin:开始位置下标,end结束位置下标
	for (i = 0; i < str.size(); i++) {
		if (!isSpace(str[i])) {
			break;
		}
	}
	if (i == str.size() - 1) {
		str = "";
		return;
	}
	begin = i; //确定开始位置

	for (i = str.size() - 1; i >= 0; i--) {
		if (!isSpace(str[i])) {
			break;
		}
	}
	end = i; //确定结束位置

	str = str.substr(begin, (end - begin) + 1); //截取字符串,从begin位置开始,截取[(end - begin) + 1]位
}

bool ReadConfig::handleLine(const string &line, string &key, string &value) {
	if (line.empty()) {
		return false;
	}
	//begin:字符串起始位置下标;end:该行字符串结束位置下标;position:查找'#'、'='出现位置的下标
	int begin = 0, end = line.size() - 1, position;
	//如果在行line中找到了#(该行有注释)
	if ((position = line.find(COMMENT)) != -1) {
		if (0 == position) { //如果在行首找到注释
			return false;
		}
		//注释不在行首
		end = position - 1;
	}

	string new_line = line.substr(begin, end - begin + 1); //删除注释

	if ((position = new_line.find('=')) == -1) {
		return false; //没有找到'='
	}

	// key = value,分别截取'='两边的字符串
	key = new_line.substr(0, position); //截取'='左边的为key
	value = new_line.substr(position + 1, (end + 1) - (position + 1)); //截取'='右边的为value

	trim(key);
	if (key.empty()) {
		return false;
	}

	trim(value);
	if (value.empty()) {
		return false;
	}

	return true;
}

bool ReadConfig::readConfig(const string &filename, map<string, string> &m) {
	ifstream infile(filename);
	if (!infile) {
		printf("open file error:%s(errno:%d)\n",strerror(errno),errno);
		return false;
	}
	string line, key, value;
	while (getline(infile, line)) {
		if (handleLine(line, key, value)) {
			m[key] = value;
		}
	}

	infile.close();
	return true;
}

void ReadConfig::printMap(const map<string, string> &m) {
	map<string, string>::const_iterator it = m.begin();
	for (; it != m.end();it++) {
		cout << it->first << ":" << it->second << endl;
	}
}
testRead.cpp
/*
 * testRead.cpp
 *
 *  Created on: 2019年11月30日
 *      Author: xb
 */
#include "../base/readConfig.h"

int main() {
	using std::cout;
	using std::endl;

	ReadConfig read;
	map<string, string> m;
	string filename = "main.cfg";//这里使用了相对路径

	cout << "read config from " << filename ;

	if (read.readConfig(filename, m)) {
		cout << ",get " << m.size() << " rows valid data total:" << endl;
		read.printMap(m);
	} else {
		printf("read config error!\n");
		return -1;
	}

	return 0;
}
运行结果

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值