我将撰写的“C++设计实践”系列文章,会讲到一些数据处理系统设计方法。我并不希望文章局限于特定数据库产品,我也不喜欢空对空地讲述太多抽象道理。我必须编写一些模拟数据库操作的代码,用于读写定界符文件的类Delimfile首当其冲。
最初并没有打算编写一个“完备类”。倘若只为撰写文章,一个具有十来个成员函数的“演示类”就足够。但在代码编写过程中,我渐渐意识到,这个类肯定具有实用价值,比如说,一个小型数据采集系统,就可采用文件方式存储。何况,它功能完备,也有助于我撰写后续代码和相应文章。既然利人利己,何乐而不为?
动手之前,我照例先察看有无现成资源可以利用。在http://codeguru.earthweb.com/mfc_database/CSpreadSheet.html,我找到了CSpreadSheet。这是一个利用MFC编写的可以读取.xls文件和定界符文件的类,尽管功能有限,但总的来说写得不错,可它并不符合我的要求。
我不打算使用MFC。我希望这些代码,以及后续的一系列代码,能够具有良好的移植性。无论你使用何种C++编译器,工作于何种操作系统之上,我都希望它们对你有用。显然,最好的表达手段,就是标准C++和STL,所以,就有了Delimfile,但我要感谢CSpreadSheet作者的开创性工作(对我而言)。
我必须对这个类的功能做出取舍。提供对行/列数据(假如这一行/列是数值型的话)的max/min/mean/sum等数值运算操作,不是Delimfile的责任,它的重心在于对文件的灵活存取。至于究竟如何处理读取到的值,那是使用者的事。对我来说,我会编写另外一些代码,来完成这些常见数值运算。
你现在看到的Delimfile类,提供了大约五十个public成员函数,支持对定界符文件所有常见存取操作。比如read、insert、update、delete行/列/数据,并支持对列进行sort、对行/列进行swap之类的扩展操作。
这个类的设计思想源于CSpreadSheet。先将文件内容读入内存,所有后续操作都于内存中进行。你可以begin_transaction()开始,对内存数据进行修改,调用commit()方法,即将修改持久化到磁盘文件中去。倘若要放弃自最近一次提交事务以来的所有内存数据修改,调用rollback()即可。
下面是Delimfile完整头文件说明,大略浏览一下,就可以跳过它。在这个冗长的说明之后,还有一个演示用法的简单例子,以及另外一些文字说明,它们或许更有看头。
//
#ifndef _DELIMFILE_H_
#define _DELIMFILE_H_
//
#include <string>
#include <vector>
#include <algorithm>
#include <fstream>
#include <functional>
//
//说明: 出错信息常量清单,你可以自由转换为你所需要的语言.
//
const std::string err_row_smaller_than_2 = "total row count is smaller than two\n";
const std::string err_col_smaller_than_2 = "total col count is smaller than two\n";
const std::string err_col_initial_already = "cols have been initialized already\n";
const std::string err_first_row_equal_second_row = "first row no. equal second row no.\n";
const std::string err_first_col_equal_second_col = "first col no. equal second col no.\n";
const std::string err_col_out_of_range = "col no. out of range\n";
const std::string err_row_out_of_range = "row no. out of range\n";
const std::string err_cell_out_of_range = "cell coordinate out of range\n";
const std::string err_row_size_too_large = "row size is larger than total cols\n";
const std::string err_fail_to_initial_col_headers = "fail to initialize col headers\n";
const std::string err_fail_to_write_file = "fail to write to file\n";
const std::string err_fail_to_open_file = "fail to open file\n";
const std::string err_fail_to_clear = "fail to clear file content\n";
const std::string err_fail_to_append_row = "fail to append row\n";
const std::string err_fail_to_insert_row = "fail to insert row\n";
const std::string err_fail_to_update_row = "fail to update row\n";
const std::string err_fail_to_delete_row = "fail to delete row\n";
const std::string err_fail_to_read_row = "fail to read row\n";
const std::string err_fail_to_append_col = "fail to append col\n";
const std::string err_fail_to_insert_col = "fail to insert col\n";
const std::string err_fail_to_update_col = "fail to update col\n";
const std::string err_fail_to_delete_col = "fail to delete col\n";
const std::string err_fail_to_sort_col = "fail to sort col\n";
const std::string err_fail_to_update_cell = "fail to update cell\n";
//
//用途: 每行字符串缓冲区大小,你可以根据需要,自由设定.
//
const int ROW_BUF_SIZE = 3000;
//
class Delimfile
{
public:
//
//file_name: 意欲操作的文件名,存在即打开,不存在即创建.
//separator: 文件中每个cell的定界符,比如若干空格" "或一个分号";"(考虑"人类"可读性).
//backup : 打开文件时是否需要做备份.是,则备份文件名为file_name + ".bak".
//
Delimfile(const std::string& file_name, const std::string& separator, bool backup = true);
//
//无任何代码.
//
~Delimfile();
public:
//
//作用 : 初始化列标题.标题栏和普通row并无区别,行号为1的row,就是标题栏.
//value: 将作为标题栏的一组列名.注意,这个方法并不限制列标题名字唯一.
//
bool initial_col_headers(const std::vector<std::string>& value);
//
//作用 : 插入一行.
//value: 欲插入行的值,所有对行进行写操作的方法,都会判断value里的数目是否
// 恰好和列数相等,否则不准进行写操作.此举为了保证所有数据呈矩形.
//row : 行号.在这一行之前插入.
//
bool insert_row(const std::vector<std::string>& value, int row);
//
//作用 : 更新一行.
//value: 将用此值去更新目标行.
//row : 行号.此行将被更新.
//
bool update_row(const std::vector<std::string>& value, int row);
//
//作用: 删除一行.
//row : 行号.此行将被删除.
//
bool delete_row(int row);
//
//作用 : 读取一行.
//value: 读取值存放于此.
//row : 行号.此行将被读取.
//
bool read_row(std::vector<std::string>& value, int row);
//
//作用 : 在末尾附加一行.
//value: 欲被附加的行的值.
//
bool append_row(const std::vector<std::string>& value);
//
//作用 : 更新符合条件的行.
//value : 欲以此值进行更新.
//col : 列号.将对该列进行条件判断.
//condition : 条件.
//only_first: 默认为false,更新符合条件的所有行,否则,只更新符合条件的第一行.
//
bool update_rows(const std::vector<std::string>& value, int col, std::string condition, bool only_first = false);
//
//作用 : 更新符合条件的行.
//value : 欲以此值进行更新.
//col : 列名.将对该列进行条件判断.注意,并没有限定列名不允许重复