在定长记录采用数据库读写并非最佳解决方案一文中,介绍了不管文件中记录数据为多少,只要按照文中介绍的方法存储,对于文件中任意1条记录数据,在读缓冲内存恒定不变的情况下,最多只需要读文件1次,而且定位记录的算法也超级简单,只需做1次除法运算和1次求余运算。今天我在这里介绍一下它的C++实现方法。
1.写记录
#ifndef _WRITE_RECORD_H
#define _WRITE_RECORD_H
#include <qglobal.h>
#include <qfile.h>
#define INDEX_BLK_RECORD_NUM 1000
#define RECORD_BLK_RECORD_NUM 1000
#define MAGIC "BETTARECORD"
#define MAGIC_LEN sizeof(MAGIC) - 1
#pragma pack(1)
typedef struct tagRecord {
quint64 id;
float cur;
float vol;
float tempe;
float resistor;
} TRecord;
typedef struct tagFileHead {
quint8 magic[MAGIC_LEN];
quint32 version;
quint32 indexBlkNum;
} TFileHead;
typedef struct tagBlkHead {
quint64 nextBlkPos;
quint32 recordNum;
}TBlkHead;
typedef struct tagIndexBlk {
TBlkHead head;
quint32 blkAddr[INDEX_BLK_RECORD_NUM];
} TIndexBlk;
typedef struct tagRecordBlk {
TBlkHead blkHead;
TRecord record[RECORD_BLK_RECORD_NUM];
} TRecordBlk;
#pragma pack()
class CWriteRecord {
public:
CWriteRecord();
~CWriteRecord();
bool openFile(const QString &fileName);
void writeRecordHead(const TFileHead *pFileHead);
void writeRecordBlk(const TRecord *pRecord);
void flushBlk(bool bClose);
private:
void writeRecordBlkHead();
void writeIndexBlk(quint64 blkPos);
void writeIndexBlkNum();
void writeIndexBlkHead();
void init();
private:
QFile file;
TBlkHead indexBlkHead;
TBlkHead recordBlkHead;
quint32 validIndexBlkRecordNum;
quint32 validRecordBlkRecordNum;
quint32 indexBlkNum;
quint32 validIndexBlkNum;
quint32 index;
quint32 recordIndex;
quint64 indexBlkPos;
quint64 recordBlkPos;
quint64 nextBlkPos;
};
#endif
头文件提供了openFile,writeRecordHead,writeRecordBlk,flushBlk,四个公有接口,首先通过openFile打开一个文件,该文件可以是1个已存在的文件,也可以是1个不存在的文件,由fileName指定,openFile自动将文件初始化到就绪状态,打开以后,就可以使用writeRecordHead,writeRecordBlk,写记录了,flushBlk,是将文件flush到磁盘的操作,接下来我们看看实现:
#include "writerecord.h"
CWriteRecord::CWriteRecord()
{
}
CWriteRecord::~CWriteRecord()
{
}
bool CWriteRecord::openFile(const QString &fileName)
{
if (file.isOpen())
file.close();
file.setFileName(fileName);
return file.open(QIODevice::Append | QIODevice::ReadWrite);
}
void CWriteRecord::writeRecordHead(const TFileHead *pFileHead)
{
file.seek(0);
file.write((const char *)pFileHead, sizeof(TFileHead));
init();
}
void CWriteRecord::init()
{
recordBlkHead.recordNum = 0;
indexBlkHead.recordNum = 0;
validIndexBlkRecordNum = 0;
validRecordBlkRecordNum = 0;
indexBlkNum = 1;
validIndexBlkNum = 0;
index = 0;
recordIndex = 0;
indexBlkPos = sizeof(TFileHead);
recordBlkPos = indexBlkPos + sizeof(TIndexBlk);
nextBlkPos = recordBlkPos + sizeof(TRecordBlk);
}
void CWriteRecord::writeRecordBlkHead()
{
if (validRecordBlkRecordNum != recordBlkHead.recordNum
&& recordBlkHead.recordNum != 0)
{
validRecordBlkRecordNum = recordBlkHead.recordNum;
file.seek(recordBlkPos);
file.write((const char *)&recordBlkHead, sizeof(recordBlkHead));
writeIndexBlk(recordBlkPos);
}
}
void CWriteRecord::writeRecordBlk(const TRecord *pRecord)
{
quint64 writePos = recordBlkPos + recordIndex * sizeof(TRecord) + sizeof(TBlkHead);
file.seek(writePos);
file.write((const char *)pRecord, sizeof(TRecord));
recordIndex++;
recordBlkHead.recordNum = recordIndex;
if (recordIndex == RECORD_BLK_RECORD_NUM)
{
/*写当前块*/
recordBlkHead.nextBlkPos = nextBlkPos;
writeRecordBlkHead();
/*初始化下一块*/
recordBlkHead.recordNum = 0;
recordBlkPos = nextBlkPos;
recordIndex = 0;
recordBlkNum++;
nextBlkPos = nextBlkPos + sizeof(TRecordBlk);
}
}
void CWriteRecord::writeIndexBlkHead()
{
if (validIndexBlkRecordNum != indexBlkHead.recordNum
&& indexBlkHead.recordNum != 0)
{
validIndexBlkRecordNum = indexBlkHead.recordNum;
file.seek(indexBlkPos);
file.write((const char *)&indexBlkHead, sizeof(indexBlkHead));
writeIndexBlkNum();
}
}
void CWriteRecord::writeIndexBlkNum()
{
if (validIndexBlkNum != indexBlkNum)
{
validIndexBlkNum = indexBlkNum;
quint32 writePos = (quint32)&((TFileHead *)0)->indexBlkNum;
file.seek(writePos);
file.write((const char *)&indexBlkNum, sizeof(indexBlkNum));
}
}
void CWriteRecord::writeIndexBlk(quint64 blkPos)
{
quint64 writePos = indexBlkPos + index * sizeof(TIndex) + sizeof(TBlkHead);
file.seek(writePos);
file.write((const char *)&blkPos, sizeof(blkPos));
index++;
indexBlkHead.recordNum = index;
quint32 blkRecordNum = INDEX_BLK_RECORD_NUM;
if (index == blkRecordNum)
{
/*写当前块*/
indexBlkHead.nextBlkPos = nextBlkPos;
writeIndexBlkHead();
/*初始化下一块*/
indexBlkHead.recordNum = 0;
indexBlkPos = nextBlkPos;
index = 0;
indexBlkNum++;
nextBlkPos = nextBlkPos + sizeof(TIndexBlk);
}
}
void CWriteRecord::flushBlk(bool bClose)
{
if (file.isOpen())
{
writeIndexBlkHead();
writeRecordBlkHead();
if (bClose)
file.close();
else
file.flush();
}
}
2.读记录
#ifndef _READ_RECORD_H
#define _READ_RECORD_H
#include <qglobal.h>
#include <qfile.h>
class CReadRecord {
public:
static CReadRecord *getInstance();
const TRecordBlk &readRecordBlk(quint64 blkPos);
bool read(const QString &fileName);
const QVector <quint64> *getRecordBlkPosList();
private:
CReadRecord();
void readRecordHead();
void initBlkPosList();
private:
QFile file;
TRecordBlk recordBlk;
TIndexBlk indexBlk;
TFileHead fileHead;
QVector <quint64> recordBlkPosList;
static CReadRecord mSelf;
};
#endif
#include "readrecord.h"
CReadRecord CReadRecord::mSelf;
CReadRecord *CReadRecord::getInstance()
{
return &mSelf;
}
CReadRecord::CReadRecord()
{
}
bool CReadRecord::read(const QString &fileName)
{
if (file.isOpen())
file.close();
file.setFileName(fileName);
if (!file.open(QIODevice::ReadOnly))
return false;
readRecordHead();
if (memcmp(recordHead.magic,
FILE_MAGIC,
FILE_MAGIC_LEN) == 0)
{
initBlkPosList();
return true;
}
return false;
}
const QVector <quint64> *CReadRecord::getRecordBlkPosList()
{
return &recordBlkPosList;
}
void CReadRecord::readRecordHead()
{
file.seek(0);
file.read((char *)&fileHead, sizeof(TFileHead));
}
const TRecordBlk &CReadRecord::readRecordBlk(quint64 blkPos)
{
readFile(blkPos, (quint8 *)&recordBlk, sizeof(recordBlk));
return recordBlk;
}
void CReadRecord::initBlkPosList()
{
recordBlkPosList.clear();
int cnt = fileHead.indexBlkNum;
quint64 indexBlkPos = sizeof(TFileHead);
for (int i = 0; i < cnt; i++)
{
readFile(indexBlkPos, (quint8 *)&indexBlk, sizeof(indexBlk));
int cnt1 = indexBlk.blkHead.recordNum;
for (int j = 0; j < cnt1; j++)
{
recordBlkPosList.append(indexBlk.index[j]);
}
indexBlkPos = indexBlk.blkHead.nextBlkPos;
}
}