之前在写一个分布式计算小项目时,频繁地使用文件IO,为简化代码,减少重复操作,降低出错可能性,便将I/O模块进行封装。
当时我的需求是对大文本文件进行读写,而且该文本文件是规整的,如:
标签 | 特征1 | 特征2 | 特征3 |
---|---|---|---|
1 | 3.4 | 2.2 | 1 |
0 | 1.1 | 0.7 | 4 |
所以,遇到新需求时还需改写,这是这个模块还需改进的地方。
为了提升自己英文读写能力,部分代码注释使用英文,不知道几年之后,自己看着这蹩脚英语是啥感受!
1. 底层设计
对底层文件的(此处指文本文件)操作应该有3种,分别是读、写、追加操作,所以,我们可以用enum声明操作方式:
enum class FileOpenMode:int
{
Write = 0,
Read = 1,
Append = 2
};
应对当时的需求,只完成了对应功能,后续功能再有需求时完善。
class FileOperator
{
public:
FileOperator(const std::string& path, FileOpenMode mode);
~FileOperator();
void Write(const void *buf, size_t size);
/*
* @param buf pointer to a memory buffer
* @param size data size
* @return the true size of data
*/
inline size_t Read(void *buf, size_t size);
//void Append(const void *buf, size_t size);
inline bool Good();
private:
bool is_good_;
FILE *fp_;
std::string path_;
};
对应的函数实现较为简单。实现代码附在文章末尾。
2. 中间层
用一个TextReader来封装对文本文件的操作。由于文件很大,将整个文件一次读入内存,无法实现,也不必要,所以每次读写一部分数据,因此,TextReader的关键成员变量就应为文件名、缓冲区大小(每次读入多少数据)。
class TextReader
{
public:
TextReader(const std::string &path, size_t buf_size = 1024);
~TextReader();
size_t GetLine(std::string &line);
private:
size_t LoadBuffer();
char* buf_;
size_t pos_, buf_size_, length_;
FileOperator* op_;
};
其中,构造函数与析构函数的设计实现较为简单,析构函数附在文末。
TextReader::TextReader(const std::string &path, size_t buf_size)
{
stream_ = new LocalStream(path, FileOpenMode::Read);
buf_size_ = buf_size;
pos_ = length_ = 0;
buf_ = new char[buf_size_];
}
根据需求,我们按行读取文件,通过调用GetLine函数,读取每一行数据,我个人不太喜欢指针,所以,利用string类的引用传递数据。
其中length_
指buf_
种实际的数据长度,pos_
指外部读取每行时,读取到了哪个位置。当读取到换行符时或文件已经全部读取完成即可跳出外层循环并返回,否则需要读取一个数据。在读取一个数据时,我们可能会遇到当前buf_
的内容已全部被读取的情况,即内层while
循环跳出,此时就需重新loadBuffer
(访问文件)。
size_t TextReader::GetLine(std::string &line)
{
line.clear();
bool isEnd = false;
while (!isEnd)
{
while (pos_ < length_)
{
char &c = buf_[pos_++];
if (c == '\n')
{
isEnd = true;
break;
}
else
{
line += c;
}
}
if (isEnd || LoadBuffer() == 0)
break;
}
return line.size();
}
size_t TextReader::LoadBuffer()
{
pos_ = length_ = 0;
return length_ = stream_->Read(buf_, buf_size_ - 1);
}
3.顶层设计
这一层,可以根据实际需求改动。为了节省内存空间,同时提高CPU利用率,我们读一部分数据,处理一部分数据,在CPU处理数据的时候,可以继续读文件,这样可以保证CPU一直运转。所以,我们考虑多线程的设计。
一些关键部分用中文描述,怕之后连自己都看不懂
标签:std,封装,buffer,void,C++,int,IO,buf,size From: https://www.cnblogs.com/caieleven/p/17017021.html