代码改变世界

CSV文件读取类

2015-04-19 23:38  rangers  阅读(609)  评论(0编辑  收藏  举报

最近项目中,经常需要读取Csv文件。基本步骤是:

(1)按行读取

(2)然后将一行数据按逗号,分割为字符串数组

(3)将各列字符串转换成相应类型的数据 ,如int double类型

写了一个简单的Csv文件读取类,方便使用,可以按列名或列索引读取数据。将字符串转换成数字变量使用的是stringstream类。

如下:

#ifndef CSV_READER_H_
#define CSV_READER_H_
#include <vector>
#include <map>
#include <fstream>
#include <sstream>
#include <string>
#include <algorithm>

using std::string;
using std::vector;
using std::map;
using std::ifstream;
using std::stringstream;

class CsvReader
{
public:
	//不区分大小写 比较字符串
	struct NoCaseCompareStr
	{
		bool operator()(const string & str1, const string & str2)
		{
			string upper_str1(str1);
			string upper_str2(str2);
			std::transform(upper_str1.begin(),upper_str1.end(),upper_str1.begin(),toupper);
			std::transform(upper_str2.begin(),upper_str2.end(),upper_str2.begin(),toupper);
			return upper_str1.compare(upper_str2) >= 0 ? false : true;
		}
	};

	typedef map<string,int,NoCaseCompareStr> ColumnIndexMap;

	CsvReader();
	virtual ~CsvReader();
	//加载Csv文件
	bool LoadFile(const string & fileName);
	//读取一行数据
	bool Read();
	void Close();
	//得到列名字段的索引
	int GetColumnIndex(const string & columnName);
	
	//按列名索引读取
	template<typename T>
	bool GetValue(int columnIndex, T & val);

	template<typename T>
	T GetValue(int columnIndex);

	//按列名提取数据
	template<typename T>
	bool GetValue(const string & columnName, T & val);

	template<typename T>
	T GetValue(const string & columnName);

	//分割字符串为字符数组
	static void SplitString(const string & str, vector<string> & str_vec, char delimiter);

private:
	ColumnIndexMap _columnIndex;	//字段所对应列的索引
	vector<string> _lineData;//Csv文件一行数据
	ifstream _fi;
	bool _fileIsOpen;
	string _line;
	stringstream _ss;
};

template<typename T>
T CsvReader::GetValue( const string & columnName )
{
	int columnIndex = this->GetColumnIndex(columnName);
	return this->GetValue<T>(columnIndex);
}

template<typename T>
bool CsvReader::GetValue( const string & columnName, T & val )
{
	int columnIndex = this->GetColumnIndex(columnName);
	return this->GetValue(columnIndex,val);
}

template<typename T>
T CsvReader::GetValue( int columnIndex )
{
	T val;
	_ss.clear();
	_ss.str("");
	if (columnIndex < 0 || columnIndex >= (int)_lineData.size())
	{
		return T();
	}
	else
	{
		_ss.str(_lineData[columnIndex]);
	}
	_ss >> val;
	return val;
}

template<typename T>
bool CsvReader::GetValue( int columnIndex, T & val )
{
	if (columnIndex < 0 || columnIndex >= (int)_lineData.size())
	{
		return false;
	}
	_ss.str("");
	_ss.clear();
	_ss.str(_lineData[columnIndex]);
	_ss >> val;
	return true;
}

#endif

 cpp文件:

#include "CsvReader.h"

CsvReader::CsvReader()
	: _fileIsOpen(false)
{

}

CsvReader::~CsvReader()
{
	this->Close();
}

bool CsvReader::LoadFile( const string & fileName )
{
	if (_fileIsOpen)	//先关闭原来的文件
	{
		this->Close();
	}
	_fi.open(fileName);
	if (!_fi.is_open())
	{
		return false;
	}
	_fileIsOpen = true;
	//读取第一行数据,得到CSV文件各列字段名称,并生成索引
	if (!this->Read())
	{
		return false;
	}
	int columnCount = _lineData.size();
	for (int columnIndex = 0; columnIndex < columnCount; columnIndex++)
	{
		_columnIndex[_lineData[columnIndex]] = columnIndex;
	}
	return true;
}

void CsvReader::Close()
{
	_fi.close();
	_lineData.clear();
	_columnIndex.clear();
	_fileIsOpen = false;
}

bool CsvReader::Read()
{
	while (_fileIsOpen && !_fi.eof())
	{
		_line.clear();
		std::getline(_fi,_line);
		if (_line.empty()) continue;
		_lineData.clear();
		CsvReader::SplitString(_line,_lineData,',');
		if (_lineData.size() < _columnIndex.size())
		{
			continue;
		}
		return true;
	}
	return false;
}

void CsvReader::SplitString( const string & str, vector<string> & str_vec, char delimiter )
{
	if (str.empty())
		return;
	std::string::size_type pos = 0;
	std::string::size_type pre_pos = 0;
	while((pos = str.find_first_of(delimiter,pos)) != std::string::npos)
	{
		string tmp_str = str.substr(pre_pos,pos - pre_pos);
		str_vec.push_back(tmp_str);
		pre_pos = ++pos;
	}
	string tmp_str;
	if (pre_pos < str.size())
	{
		tmp_str = string(&str[pre_pos]);
	}
	str_vec.push_back(tmp_str);
}

int CsvReader::GetColumnIndex( const string & columnName )
{
	ColumnIndexMap::const_iterator indexIt = _columnIndex.find(columnName);
	if (indexIt != _columnIndex.end())
	{
		return indexIt->second;
	}
	return -1;
}