在C++ Builder 和GCC(MinGW)中读写数据文件的类

常常在GCC(MinGW)和C++ Builder 中都有读写数据的时候,也就是从文件中将一组数据读入二维数组<vector>,或者将数组中的数据格式化写入文件,甚至有时还想给文件加个文件头,当然文件头也要对齐了才好看一点,两个软件实现的方法都不一样,常常让人恼火,今天有空,编写了个类,两个都能通用的文件读写类FileData.

 

有3点请大家注意:

1.FileData类读的数据文件的格式是任意的,数据的排列方式也是任意的,

也就是说FileData可以自动检测数据的排列方式和分割方式,每行的数据个数可以互不相同!

一句话,只要文件里面有数据就可以正确识别!

 

2.FileData将数组和文件头写入文件的时候,自动对齐数据和文件头.

也就是说你用记事本打开一看,数据和文件头是排列得整整齐齐的.

 

3.FileData中使用了宽字符(汉字),所以为了让MinGW识别,请将FileData.hpp保存为UTF-8的格式.

 

// -----------------------------------------------------------------------------
// 功能强大的读写数据文件类 FileData.hpp
// 经常要将数据导入C++,使用这个很方便
// Wu Xuping 2012-03-10
// 测试环境:
// C++ builder 2010
// MinGW 4.5.1 windows
// 使用很简单,看看最后附上的测试代码即可
// FileData.hpp一定要保存为UTF-8的格式,因为这是MinGW的默认格式
// -----------------------------------------------------------------------------
#ifndef FileData_H
#define FileData_H
// --------------------
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <iterator>
#include <algorithm>
using namespace std;

// =============================================================================
// IsCommentString::识别字符串是否是以"#%!"开头的注释行,空行也是注释行
// =============================================================================
bool IsCommentString(const string &istr) {
	bool IsValid = false;
	if (istr.size() > 0) {
		unsigned i = 0;
		while (i < istr.size()) {
			if (istr[i] == ' ' || istr[i] == '\t' || istr[i] == '\r' || istr[i]
				== '\n') { // 这些字符忽略不计
				i++;
			}
			else {
				if (istr[i] == '#' || istr[i] == '%' || istr[i] == '!') {
					// 这些都是注释字符
					IsValid = true;
				}
				break; // 可以中断循环了
			}

		};

	}
	else {
		IsValid = true; // 空行也是注释行
	}
	return IsValid;
};

// =============================================================================
// SplitString::分割字符串的函数,默认使用五种分隔符号": ;,\t"
// =============================================================================
void SplitString(string str, vector<string>&sv, string separator = ": ;,\t") {
	// ---------------------------------------
	if (sv.size() > 0) {
		sv.clear();
	}
	// ---------------------------------------
	vector<long int>index_v; // 分割索引
	// ---------------------------------------
	for (unsigned i = 0; i < str.length(); i++) {
		for (unsigned j = 0; j < separator.length(); j++) {
			if (str[i] == separator[j]) {
				index_v.push_back(i);
			}
		}
	}
	// ---------------------------------
	index_v.push_back(-1); // 加上开始的字符
	index_v.push_back(str.length()); // 加上结尾的字符
	// -----------------------------------
	sort(index_v.begin(), index_v.end()); // 排序
	// --------------------------------
	for (unsigned i = 1; i < index_v.size(); i++) {
		long int startindex = index_v[i - 1] + 1;
		long int endindex = index_v[i];
		if (startindex < endindex) {
			string cstr(str.begin() + startindex, str.begin() + endindex);
			sv.push_back(cstr);
		}
	};
	// --------------------------------
};

// =============================================================================
// StringToDouble::字符串转化为double的函数
// =============================================================================
bool StringToDouble(std::string Str, double &num) {
	bool isvalid = false;
	std::istringstream stream(Str);
	while (stream >> num) {
		isvalid = true;
	}
	return isvalid;
};

// =============================================================================
// StringToInteger::字符串转化为int的函数
// =============================================================================
bool StringToInteger(std::string istr, int &num) {
	bool isvalid = false;
	std::istringstream stream(istr); ;
	while (stream >> num) {
		isvalid = true;
	}
	return isvalid;
};

// =============================================================================
// StringToDoubleVector::将字符串转换为Double数组的函数
// =============================================================================
void StringToDoubleVector(string istr, vector<double> &ov) {
	if (ov.size() > 0) {
		ov.clear();
	}
	// -------分割字符串
	vector<string>sv;
	SplitString(istr, sv);
	// -----------字符转为double类型的数
	for (unsigned int i = 0; i < sv.size(); i++) {
		double num;
		if (StringToDouble(sv[i], num)) {
			ov.push_back(num);
		}

	}

};

// =============================================================================
// StringToIntegerVector::将字符串转换为Integer数组的函数
// =============================================================================
void StringToIntegerVector(string istr, vector<int> &ov) {
	if (ov.size() > 0) {
		ov.clear();
	}
	// -------分割字符串
	vector<string>sv;
	SplitString(istr, sv);
	// -----------字符转为double类型的数
	for (unsigned int i = 0; i < sv.size(); i++) {
		int num;
		if (StringToInteger(sv[i], num)) {
			ov.push_back(num);
		}

	}

};

// -----------------------------------------------------------------------------
// FileData类::读写文件中的数据到二维数组的类
// --------------------------------------------------
// 1.关于FileData::LoadDataFromFile(string FileName)
// 以"#%!"开头的是注释行,空行忽略
// 数据分隔符可以是五种常规分隔符": ;,\t"的任意组合
// 数据每行的个数可以不相同
// 每个数据的前后可以包含文字说明,不影响数据的正确读入
// --------------------------------------------------
// 2.关于SaveDataToFile(string FileName, string Headstr = "");
// 保存二维数组到文件的函数,可以加文件头
// 每个数据的输出宽度是12个字符,文件头也一样
// 文件头和数据都会自动对齐
// 文件头可以使用五种常规分割符": ;,\t"
// --------------------------------------------------
// 3. 关于void GetData(vector<vector<double> >&Data)
// 提取数据
// --------------------------------------------------
// 4.关于 void SetData(vector<vector<double> >&Data)
// 设置数据
// --------------------------------------------------
// 5.关于unsigned int GetRow()
// 提取二维数组的行数
// --------------------------------------------------
// 6.关于unsigned int GetMinColumn()
// 提取二维数组的最小列数
// --------------------------------------------------
// 7.关于unsigned int GetMaxColumn()
// 提取二维数组的最大列数
// --------------------------------------------------
// 8.关于string GetHeadstr()
// 提取文件第一行字符串
// --------------------------------------------------
// 9.关于void PrintData();
// 方便控制台打印和查看二维数组函数
// -----------------------------------------------------------------------------
class FileData {
private:
	vector<vector<double> >_Data; // 二维数组

	unsigned int _Row; // 数组的行数
	unsigned int _MinColumn; // 数组最小列
	unsigned int _MaxColumn; // 数组最大列

	string _FileName; // 文件名
	string _Headstr; // 文件第一行字符串

	// -读写文件中的数据-----------------------------------------------
	void LoadDataFromFile();
	void SaveDataToFile();
	// -----------------------------------------------------------------

public:

	// ---构造函数 -------------
	FileData() : _Row(0), _MinColumn(0), _MaxColumn(0), _FileName(""),
	_Headstr("") {
	};
	// -----------------------------------------------------------------
	// --读取文件中的数据
	void LoadDataFromFile(string FileName);
	// -----------------------------------------------------------------
	// --保存数据到文件中
	void SaveDataToFile(string FileName, string Headstr = "");

	// ------------------------------------------------------------------------
	unsigned int GetRow() {
		_Row = _Data.size();
		return _Row;
	};

	// ------------------------------------------------------------------------
	unsigned int GetMinColumn() {
		_MinColumn = 999999999;
		for (unsigned int i = 0; i < _Data.size(); i++) {
			if (_MinColumn > _Data[i].size()) {
				_MinColumn = _Data[i].size(); // 获取最小行
			}
		}
		if (_MinColumn >= 999999999) {
			_MinColumn = 0;
		}
		return _MinColumn;
	};

	// ------------------------------------------------------------------------
	unsigned int GetMaxColumn() {
		_MaxColumn = 0;
		for (unsigned int i = 0; i < _Data.size(); i++) {
			if (_MaxColumn < _Data[i].size()) {
				_MaxColumn = _Data[i].size(); // 获取最小行
			}
		}
		return _MaxColumn;
	};

	// -------------------------------------------------------------------------
	// 获取文件的第一行作为文件头
	// -------------------------------------------------------------------------
	string GetHeadstr() {
		ifstream fin(_FileName.c_str());
		if (!fin.bad()) {
			getline(fin, _Headstr);
		}
		fin.close();
		return _Headstr;
	};

	// ------------------------------------------------------------------------
	// 提取数据
	// ------------------------------------------------------------------------
	void GetData(vector<vector<double> >&Data) {
		Data.assign(_Data.begin(), _Data.end());
	};

	// ------------------------------------------------------------------------
	// 设置数据
	// ------------------------------------------------------------------------
	void SetData(vector<vector<double> >&Data) {
		_Data.assign(Data.begin(), Data.end());
	};
	// ------------------------------------------------------------------------
	void PrintData();

	// -------------默认destructor函数 -------------
	~FileData() {
	};
	// -----------
};

// ==============================================================================
// LoadDataFromFile0
// ==============================================================================
void FileData::LoadDataFromFile() {

	_Row = 0;
	_MinColumn = 999999999;
	_MaxColumn = 0;
	if (_Data.size() > 0) {
		_Data.clear();
	}
	ifstream fin(_FileName.c_str());
	if (!fin.bad()) {
		string sLine; // 行字符串
		while (getline(fin, sLine)) {
			if (!IsCommentString(sLine)) { // 该行不是空行或注释行就可以提取数据
				vector<double>tempdv;
				StringToDoubleVector(sLine, tempdv);
				if (tempdv.size() > 0) {
					_Data.push_back(tempdv);
					_Row++;
					if (tempdv.size() < _MinColumn) {
						_MinColumn = tempdv.size(); // 获取最小行
					}
					if (tempdv.size() > _MaxColumn) {
						_MaxColumn = tempdv.size(); // 获取最大行
					}
				}
			}

		}
	}
	fin.close();
	// ---还原_MinColumn和_MaxColumn以及_Data的初始值
	if (_MinColumn >= 999999999) {
		_MinColumn = 0;
	}
	if (_Row < 1) {
		_MinColumn = 0;
		_MaxColumn = 0;
		if (_Data.size() > 0) {
			_Data.clear();
		}
	}
}

// ==============================================================================
// LoadDataFromFile1
// ==============================================================================
void FileData::LoadDataFromFile(string FileName) {
	_FileName = FileName;
	LoadDataFromFile();
}

// ==============================================================================
// SaveDataToFile0
// ==============================================================================
void FileData::SaveDataToFile() {
	if (_Data.size() > 0) {
		vector<string>strv;
		for (unsigned int i = 0; i < _Data.size(); i++) {
			string cstr = "";
			for (unsigned int j = 0; j < _Data[i].size(); j++) {
				char buffer[32];
				sprintf(buffer, "%12.6g", _Data[i][j]);
				cstr = cstr + " " + buffer;
			}
			strv.push_back(cstr);
		}
		ofstream ofs(_FileName.c_str());
		if (!ofs.bad()) {
			if (_Headstr.length() > 1) {
				vector<string>hvs;
				SplitString(_Headstr, hvs);
				string cstr = "";
				for (unsigned int n = 0; n < hvs.size(); n++) {
					char buffer[256];
					if (hvs[n].size() > 12) {
						sprintf(buffer, "%s", hvs[n].c_str());
					}
					else {
						sprintf(buffer, "%12s", hvs[n].c_str());
					}
					cstr = cstr + " " + buffer;
				}
				ofs << cstr << endl;
			}
			copy(strv.begin(), strv.end(), ostream_iterator<string>(ofs, "\n"));
		}
		ofs.close();
	}

}

// ==============================================================================
// SaveDataToFile1
// ==============================================================================
void FileData::SaveDataToFile(string FileName, string Headstr) {
	_FileName = FileName;
	_Headstr = Headstr;
	SaveDataToFile();
}

//////////////////////////////////////////////////////////////////////////
// --用于控制台打印数据的函数PrintData
//////////////////////////////////////////////////////////////////////////
void FileData::PrintData() {
	cout << _FileName.c_str() << endl;
	if (_Data.size() > 0) {

		string cstr = "";
		for (unsigned int n = 0; n < _Data[0].size() + 1; n++) {
			char buffer0[32];
			char buffer[32];
			if (n < 1) {
				sprintf(buffer, "%12s", "RowIndex");
				cstr = cstr + buffer + " ";
			}
			else {
				sprintf(buffer0, "Column[%d]", n);
				sprintf(buffer, "%12s", buffer0);
				cstr = cstr + buffer + " ";
			}
		}
		cout << cstr << endl;

		// 输出数组
		for (unsigned int i = 0; i < _Data.size(); i++) {
			char buffer0[32];
			char buffer[32];
			sprintf(buffer0, "Row[%d]", i);
			sprintf(buffer, "%12s", buffer0);
			cout << buffer << ' ';
			for (unsigned int j = 0; j < _Data[i].size(); j++) {
				sprintf(buffer, " %12.6g", _Data[i][j]);
				cout << buffer;
			}
			cout << endl;
		}

	}
	else {
		cout << "There is no data in this file at all!" << endl;
	}
}
// ------------------------------------------------------------------------------
#endif


 

 
下面是控制台测试程序,同样也要将测试程序保存为UTF-8的格式,

否则MinGW要使用 -finput-charset=GBK的编译选项,比较麻烦,

因为里面使用了宽字符(汉字):

 

// FileData example
// GCC::g++ test.cpp
// BCB::bcc32 test.cpp
// 请将test.cpp保存为UTF-8的文本格式,这是MinGW(GCC)的默认格式

#include "FileData.hpp"

int main() {
	string FileName = "Data.txt";
	string Headstr;
	vector<vector<double> >Data; // 二维数组

	FileData fd; // 定义类

	fd.LoadDataFromFile(FileName); // 读取文件

	fd.GetData(Data); // 获取数据

	fd.PrintData(); // 在控制台打印数据

	cout << "fd.GetRow()::" << fd.GetRow() << endl; // 数组行数
	cout << "fd.GetMinColumn()::" << fd.GetMinColumn() << endl; // 数据最小列
	cout << "fd.GetMaxColumn():: " << fd.GetMaxColumn() << endl; // 数据最大列
	Headstr = fd.GetHeadstr(); // 文件第一行字符串
	cout << "fd.GetHeadstr():: " << Headstr.c_str() << endl;
	fd.SaveDataToFile(" FileData0.txt ", Headstr); // 保存数组到文件

	if (Data.size() > 0) {
		Data.clear();
	}
	for (unsigned int j = 0; j < 3; j++) {
		vector<double>od1v;
		for (unsigned int i = 0; i < 5; i++) {
			od1v.push_back(i + j);
		}
		Data.push_back(od1v);
	}

	fd.SetData(Data); // 设置数据
	fd.SaveDataToFile(" FileData.txt "); // 保存数组到文件
	fd.SaveDataToFile(" FileDataWithHead.txt ",
		" index, Column1, column2, FileData "); // 保存数组到文件
	// -----------------------------------------------

	return 0;
}


 


下面是C++Builder GUI中的测试程序:

// ---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender) {
	FileData fd;
	String FileName = "D:\\Backup\\我的文档\\RAD Studio\\Projects\\Data.txt";
	fd.LoadDataFromFile(FileName.t_str());
	// Form1->Caption=fd.GetRow();
	// Form1->Caption=fd.GetHeadstr().c_str();
	Form1->Caption = fd.GetMaxColumn();
}
// ---------------------------------------------------------------------------


其中Data.txt的格式是任意的,数据的排列方式也是任意的,

也就是说FileData可以自动检测数据的排列方式和分割方式,每行的数据个数可以互不相同!

一句话,只要文件里面有数据就可以正确识别!

例如我测试的文件如下

 

col_1,col_2,col3,col_4,colfdsf
#这是注释行
     0.000000   3749.34324   2.000000     3.000000     4.000000
     1.000000     2.000000   dsfdsf   3.000000,374.324         5.000000
年份 1990,月份 12,收入 130000,交税 30000 等 	
     2.000000     3.000000     4.000000  37.e3,     6.000000


 

 

下面是测试程序的截图,看数据被正确提取出来:
在C++ Builder 和GCC(MinGW)中读写数据文件的类_第1张图片

 其它的,自己看看文件说明吧

你可能感兴趣的:(C++,Stream,String,gcc,buffer,destructor)