分享
 
 
 

C++设计日志:读写定界符文件

王朝c/c++·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

C++设计日志:读写定界符文件

荣耀 2003

我将撰写的“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完整头文件说明,大略浏览一下,就可以跳过它。在这个冗长的说明之后,还有一个演示用法的简单例子,以及另外一些文字说明,它们或许更有看头。

/****************************************************************************

* 文件名称: delimfile.h

* 功能特点: 用于读写具有定界符的平文件,采用标准C++/STL编写.

* 作 者: 荣耀

* 发布时间: 2003年1月11日

* 声 明: 任何个体/组织都可以将此代码用于非商业用途.

* Version : beta1

* Copyright (c) royaloo.com by royal. All rights reserved.

****************************************************************************/

//

#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 : 列名.将对该列进行条件判断.注意,并没有限定列名不允许重复

// 倘有重复,将以具有该列名的第一列为准.

//condition : 条件.

//only_first: 默认为false,更新符合条件的所有行,否则,只更新符合条件的第一行.

//

bool update_rows(const std::vector<std::string>& value, std::string col, std::string condition, bool only_first = false);

//

//作用 : 删除符合条件的行.

//col : 列号,将对该列进行条件判断.

//condition : 条件.

//only_first: 默认为false,删除符合条件的所有行,否则,只删除符合条件的第一行.

//

bool delete_rows(int col, const std::string& condition, bool only_first = false);

//

//作用 : 删除符合条件的行.

//col : 列名,将对该列进行条件判断.注意,并没有限定列名不允许重复,

// 倘有重复,将以具有该列名的第一列为准.

//condition : 条件.

//only_first: 默认为false,删除符合条件的所有行,否则,只删除符合条件的第一行.

//

bool delete_rows(const std::string& col, const std::string& condition, bool only_first = false);

//

//作用 : 读取符合条件的一行.

//value : 读取结果存放于此变量之中.

//col : 列号,将对该列进行条件判断.

//condition: 条件.

//

bool read_row(std::vector<std::string>& value, int col, const std::string& condition);

//

//作用 : 读取符合条件的一行.

//value : 读取结果存放于此变量之中.

//col : 列名,将对该列进行条件判断.倘有列名重复情况,将以具有该列名的第一列为准.

//condition: 条件.

//

bool read_row(std::vector<std::string>& value, const std::string& col, const std::string& condition);

//

//作用 : 读取符合条件的所有行.

//values : 读取结果存放于此变量之中.

//col : 列号,将对该列进行条件判断.

//condition: 条件.

//

bool read_rows(std::vector<std::vector<std::string> >& values, int col, const std::string& condition);

//

//作用 : 读取符合条件的所有行.

//values : 读取结果存放于此变量之中.

//col : 列名.将对该列进行条件判断.倘有列名重复情况,将以具有该列名的第一列为准.

//condition: 条件.

//

bool read_rows(std::vector<std::vector<std::string> >& values, const std::string& col, const std::string& condition);

//

//作用 : 追加一列.

//name : 列名.即列标题.即显示在第一行的那个cell的名字.

//value: 除了第一行之外的所有该列的值,默认为带有一个空格的字符串.

//

bool append_col(const std::string& name, const std::string& value = " ");

//

//作用 : 插入一列.

//name : 列名,即列标题名,即显示在第一行的那个cell的名字.

//col : 位置列,将在该列之前插入一列.

//value: 除了第一行之外的所有该列的值,默认为带有一个空格的字符串.

//

bool insert_col(const std::string& name, int col, const std::string& value = " ");

//

//作用 : 插入一列.

//name : 列名,即列标题,即显示在第一行的那个cell的名字.

//col : 列名.将对该列进行条件判断.倘有列名重复情况,将以具有该列名的第一列为准.

//value: 除了第一行之外所有该列的值,默认为带有一个空格的字符串.

//

bool insert_col(const std::string& name, const std::string& col, const std::string& value = " ");

//

//作用 : 更新一列.

//col : 列号.将更新该列.

//value: 除了第一行之外所有该列的值,默认为“updated”.

//

bool update_col(int col, const std::string& value = "updated");

//

//作用 : 更新一列.

//col : 列名.将对该列进行条件判断.倘有列名重复情况,将以具有该列名的第一列为准.

//value: 除了第一行之外所有该列的值,默认为“updated”.

//

bool update_col(const std::string& col, const std::string& value = "updated");

//

//作用: 删除一列.

//col : 列号.将删除该列.

//

bool delete_col(int col);

//

//作用: 删除一列.

//col : 列名.将对该列进行条件判断.倘有列名重复情况,将以具有该列名的第一列为准.

//

bool delete_col(const std::string& col);

//

//作用 : 读取一列,含列标题.

//value: 读取结果存放于此.

//col : 列号.将读取该列.

//

bool read_col(std::vector<std::string>& value, int col);

//

//作用 : 读取一列,含列标题.

//value: 读取结果存放于此.

//col : 列名.将对该列进行条件判断.倘有列名重复情况,将以具有该列名的第一列为准.

//

bool read_col(std::vector<std::string>& value, const std::string& col);

//

//作用 : 更新列标题.

//value: 欲以此值进行更新.

//col : 列号.将更新该列.

//

bool update_col_name(const std::string& value, int col);

//

//作用 : 更新列标题.

//value: 欲以此值进行更新.

//col : 列名.将对该列进行条件判断.倘有列名重复情况,将以具有该列名的第一列为准.

//

bool update_col_name(const std::string& value, const std::string& col);

//

//作用 : 更新cell(row, col)值.

//value: 欲以此值进行更新.

//col : 列号.

//row : 行号.

//

bool update_cell(const std::string& value, int col, int row);

//

//作用 : 更新cell(row, col)值.

//value: 欲以此值进行更新.

//col : 列名.倘有列名重复情况,将以具有该列名的第一列为准.

//row : 行号.

//

bool update_cell(const std::string& value, const std::string& col, int row);

//

//作用 : 读取cell(row, col)值.

//value: 读取值存放于此.

//col : 列号.

//row : 行号.

//

bool read_cell (std::string& value, int col, int row);

//

//作用 : 读取cell(row, col)值.

//value: 读取值存放于此.

//col : 列名.倘有列名重复情况,将以具有该列名的第一列为准.

//row : 行号.

//

bool read_cell (std::string& value, const std::string& col, int row);

//

//作用 : 交换两列位置.

//first_col : 列号.

//second_col: 行号.

//

bool swap_cols(int first_col, int second_col);

//

//作用: 交换两列位置.

//col : 列名.倘有列名重复情况,将以具有该列名的第一列为准.

//col : 列名.倘有列名重复情况,将以具有该列名的第一列为准.

//

bool swap_cols(const std::string& first_col, const std::string& second_col);

//

//作用 : 交换两行位置.

//first_row : 行号.

//second_row: 行号.

//

bool swap_rows(int first_row, int second_row);

//

//作用: 对该列进行排序(按字母顺序).

//col : 列号.

//asc : true为升序,false为降序.默认为升序.

//

bool sort_col(int col, bool asc = true);

//

//作用: 对该列进行排序(按字母顺序).

//col : 列名.倘有列名重复情况,将以具有该列名的第一列为准.

//asc : true为升序,false为降序,默认为升序.

//

bool sort_col(const std::string& col, bool asc = true);

//

//作用: 清空缓存,并清空文件内容.

//

bool clear();

//

//作用: 开始事务.

//

void begin_transaction();

//

//作用: 提交事务.将所有内存更改,持久化到数据文件中.

//

bool commit();

//

//作用: 回滚到最近一次事务提交点,放弃所有内存修改.

//

bool rollback();

//

//作用: 判断给定的列号是否在有效范围内[1, m_col_total_count].

//col : 列号.

//

bool is_col_in_range(int col);

//

//作用: 判断给定的行号是否在有效范围内[1, m_row_total_count].

//row : 行号.

//

bool is_row_in_range(int row);

//

//作用: 判断给定的cell(row, col)是否在有效范围内[1, 1] [1, m_col_total_count]

// [m_row_total_count, 1] [m_row_total_count, m_col_total_count].

//col : 列号.

//row : 行号.

//

bool is_cell_in_range(int col, int row);

//

//作用 : 返回所有列标题.

//value: 返回值存放于此.

//

void get_col_names (std::vector<std::string>& value) const;

//

//作用: 由给定列名,返回对应列号.

//col : 列名.倘有列名重复情况,将以具有该列名的第一列为准.

//

int get_col_no(const std::string& col) const;

//

//作用: 返回总行数.

//

int get_row_total_count() const;

//

//作用: 返回总列数.

//

int get_col_total_count() const;

//

//作用: 返回当前行号.

//

int get_current_row_no() const;

//

//作用: 返回备份状态.

//

bool get_backup_status() const;

//

//作用: 返回事务状态.

//

bool get_transaction_status() const;

//

//作用: 返回最近一次出错信息.

//

const std::string& get_last_error() const;

protected:

//

//作用: 打开文件,将内容读取到m_rows中,行尾以"\r"定界.

//

bool open();

//

//作用: 将vector中的字符串,进行定界处理,并返回结果字符串.

//

std::string delimit(const std::vector<std::string>& value);

private:

//

//作用: 文件备份状态.

//

bool m_backup;

//

//作用: 事务状态.

//

bool m_transaction;

//

//作用: 当前行行号.

//

int m_current_row;

//

//作用: 总共行数.

//

int m_row_total_count;

//

//作用: 总共列数.

//

int m_col_total_count;

//

//作用: 被操作的文件名.

//

std::string m_file_name;

//

//作用: 定界符,比如若干空格/符号/字母等等.

//

std::string m_separator;

//

//作用: 最近一次出错信息.

//

std::string m_last_error;

//

//作用: 所有列名.

//

std::vector<std::string> m_col_names;

//

//作用: 所有行值.

//

std::vector<std::string> m_rows;

};

#endif

/****************************************************************************

一个测试例子:

#include "delimfile.h"

#include "utils.h" //后面对此文件内容作了简要说明

using namespace std;

int main()

{

Delimfile df("test.dat"," ");

df.clear();

vector<string> tv;

string ts;

char ch = 'A';

df.begin_transaction();

for (int i = 1; i <= 5; ++i)

{

tv.clear();

for (int j = 1; j <= 5; ++j)

{

ts = stringmaker() << (ch++) << i;

tv.push_back(ts);

}

if (i == 1)

{

df.initial_col_headers(tv);

}

else

{

df.append_row(tv);

}

ch = 'A';

}

df.commit();

cout << "total number of rows = " << df.get_row_total_count() << endl;

for (int i = 1; i <= df.get_row_total_count(); ++i)

{

df.read_row(tv, i);

for (size_t j = 1; j <= tv.size(); ++j)

{

if (j != tv.size())

{

cout<<tv.at(j-1)<<"\t";

}

else

{

cout<< tv.at(j-1)<<"\n";

}

}

}

cout << "total number of columns = " << df.get_col_total_count() << endl;

tv.clear();

df.read_col(tv, "C1");

for (size_t i = 1; i <= tv.size(); ++i)

{

cout<< static_cast<int>(i) + 1 <<" : "<< tv.at(i-1) << endl;

}

if (df.read_cell(ts, 2, 4))

{

cout << "cell value at (2, 4): " << ts << endl;

}

else

{

cout << df.get_last_error()<<endl;

}

}

在“命令提示符窗口”编译、运行情况如下(先后使用了两种C++编译器):

F:\DelimfileTest>cl /GX dftest.cpp delimfile.cpp

Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 13.00.9466 for 80x86

Copyright (C) Microsoft Corporation 1984-2001. All rights reserved.

dftest.cpp

delimfile.cpp

正在生成代码...

Microsoft (R) Incremental Linker Version 7.00.9466

Copyright (C) Microsoft Corporation. All rights reserved.

/out:dftest.exe

dftest.obj

delimfile.obj

F:\DelimfileTest>bcc32 dftest.cpp delimfile.cpp

Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland

dftest.cpp:

delimfile.cpp:

Turbo Incremental Link 5.00 Copyright (c) 1997, 2000 Borland

F:\DelimfileTest>dftest

total number of rows = 5

A1 B1 C1 D1 E1

A2 B2 C2 D2 E2

A3 B3 C3 D3 E3

A4 B4 C4 D4 E4

A5 B5 C5 D5 E5

total number of columns = 5

1 : C1

2 : C2

3 : C3

4 : C4

5 : C5

cell value at (2, 4): B4

用notepad打开test.dat文件,内容如下:

"A1" "B1" "C1" "D1" "E1"

"A2" "B2" "C2" "D2" "E2"

"A3" "B3" "C3" "D3" "E3"

"A4" "B4" "C4" "D4" "E4"

"A5" "B5" "C5" "D5" "E5"

至于那个utils.h文件,上例中使用的stringmaker类的定义就包含于该文件中。这个精巧的“helper class”完整定义如下:

class stringmaker

{

public:

template <class T>

stringmaker& operator << (const T& t) { osm << t; return *this; }

operator std::string() const { return osm.str(); }

private:

std::ostringstream osm;

};

因为std::string中并无象CString(MFC)的Format那样的现成函数可以调用,我使用这个类来构造一个“带有参数的”std::string字符串。

尽管Delimifile大部分开发工作完成于一个文本编辑器和命令行式编译器,但你当然可以在任何一个C++ IDE中使用它,并可用于任何一个图形界面程序。不过,要注意包含该IDE可能需要的附加文件,比如在Visual C++项目中,别忘了包含stdafx.h文件。

倘若工作于Visual C++中,你十有八九要使用MFC,假如打算试用Delimfile,你或许需要在CString和std::string之间进行转换。下面是转换方法之一:

std::string ts1("string");

CString cs(ts.c_str());

std::string ts2(cs.GetString());

我完成了这个类的代码编写工作,但我没有更多的时间和精力进行细致测试。源码已经开放于此[delimfile.h][delimfile.cpp][utils.h](请注意,它们时刻处于更新中),希望能够得到积极反馈意见,无论是bug报告还是别的。

请给我写信:royal@royaloo.com

祝各位新年快乐。

-完-

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有