读书人

300分征文件格式的设计思路

发布时间: 2012-03-21 13:33:14 作者: rapoo

300分征求一个文件格式的设计思路
要设计一种本地文件存储格式,可以象队列一样向队尾存数据,从队首取数据。
数据不定长,而且可能会很大,要求尽量快速。

大家有没有好想法?用access数据库不知道是不是可行(随着数据的不断存取,文件会不会越来越大?用什么SQL语句可以快速取到“队首”并删除之?),目前还有一个想法是用复合文件,不过同样对其效率不放心。故上来向弟兄们征求意见,谢谢!

[解决办法]
我觉得access或SQL都慢不错的。建议使用之。。
[解决办法]
<队首地址><队列块8><队列块9><队列块10>....<队列块n><队列块1>...<队列块7><EOF>
[解决办法]
sqlite一个内置嵌入式数据库,
本地就是一个文件.
速度超级快,最差也和你本地文件操作一样快.



[解决办法]
1、数据量的大小变化不大的情况下,数据库变化不大。
2、delete from tab1 where rows 1;(interbase)
3、要求速度用楼上说的sqlite,比access快。
4、更要求速度,用内存式数据库,具体叫什么名字不清楚,只是听说过。
[解决办法]
我觉得微软的结构化存储就挺好的啊。如果考虑数据库的话,建议用SQLite,开源,而且功能比较强大。
[解决办法]
rows是interbase和firebird的关键字,其他数据库里面有相同功能的关键字,关键字不同罢了。
splite你到官方下载有个单文件版的源代码amalgamation版,那个好编译。
官方下载地址:http://www.sqlite.org/sqlite-amalgamation-3_6_0.zip

探讨
谢谢阿丁第一个回贴:-)

to sczyq,
存入的数据不定长,要用块的方式存取的话得模拟文件系统的方式,觉得很复杂:-(,你的呢称是不是改过啦?

to 僵哥,
逻辑顺序文件的话删除队首会造成整个文件数据移动(要是系统有重定义文件头位置就好了-_-),另外啥是MQ?

to 大头娃娃,
sqlite确实不错,就是我没法在bcb下编译成功,不知为何。虚拟文件系统估计和复合文件一样,如果得不到好方案我就使用复合文件…

[解决办法]
我的一个贴子也讨论了,记录队列解决方法,目的是限制记录数目,我的观点:
1。不要删除队首,留下空洞会使用空间越占越大,解决方法是,覆盖队首记录;
2。用一个时间字段做增序字段,select top 1... order by 时间,
显然第一个记录就是队首;

[解决办法]
楼主有见过dbase或foxbase没?你直接用它的存贮就可以了。采用记录块存贮,再建一个索引文件就可以了!
[解决办法]
想了一个晚上,导致今天上班迟到!我认为按照下面的结构会很快,也会很方便 :

typedef struct tagFileHeader
{
DWORD dwTail; // 队列尾的相对于文件头的偏移量,初始化设置成为sizeof(TFileHeader)
DWORD dwHeader; // 队列头的相对于文件头的偏移量,初始化设置成为sizeof(TFileHeader)
DWORD dwCount; // 队列的元素个数,初始化设置为0
} TFileHeader,*PFileHeader;

typedef struct tagFileInfo
{
DWORD dwOffset;
DWORD dwSize; // 数据大小
DWORD dwAddress; // 数据的相对于文件头的偏移量
DWORD dwNext; // 下一个节点有对于文件头的偏移量
} TFileInfo,*PFileInfo;


使用方式 :
每次启动程序的时候,将文件头TFileHeader 读入到内存,这样可以获得队列的头与尾和节点的数量.

写入一个节点 :
SetFilePointer(hFile,PFileHeader->dwTail,NULL,FILE_BEGIN);
// 写入节点(假设节点为一个100字节大小)
TFileInfo.dwSize = 100;
TFileInfo.dwAddress = PFileHeader->dwTail;
TFileInfo.dwNext = 0;
// 写入节点头
WriteFile(hFile,&TFileInfo,sizeof(TFileInfo),&dwWritten,NULL);
// 写入文件数据
WriteFile(hFile,&FileData,100,&dwWritten,NULL);
// 更新 队列头结点的信息
PFileHeader->dwTail = sizeof(TFileInfo) + 100; // 计算出新的偏移
PFileHeader->dwCount++;
// 保存队列头节点信息至文件
WriteFile(hFile,PFileHeader,sizeof(TFileHeader),&dwWritten,NULL);
// 写入一个节点完毕

读取一个节点 :
SetFilePointer(hFile,PFileHeader->dwHeader,File_BEGIN);
// 读取节点头信息
ReadFile(hFile,&TFileInfo,sizeof(TFileInfo),&dwRead,NULL);
// 读取数据
//SetFilePointer(hFile,TFileInfo.dwAddress,NULL,FILE_BEGIN); // 可以考虑不需要定位直接读取,因为节点头与节点内容是连续存储的
ReadFile(hFile,FileBuffer,TFileInfo.dwSize,&dwRead,NULL);

删除一个节点 :
// 可以考虑使用下表来删除,也可以考虑使用关键字来删除(可以在TFileInfo 中增加一个识别关键字),删除数据之后并不重新移动其它节点的内容
DWORD dwPrev; // 记录上一个节点偏移,假设要删除节点K(delete FileQueue[k])



// 删除的是头节点
if ( k == 0 )
{
SetFilePointer(hFile,PFileHeader->dwHeader,NULL,FILE_BEGIN);
ReadFile(hFile,&TFileInfo,sizeof(TFileInfo),&dwRead,NULL);
PFileHeader->dwHeader = TFileInfo.dwNext;
PFileHeader->dwCount--;
}
else
{
SetFilePointer(hFile,PFileHeader->dwHeader,NULL,FILE_BEGIN);
ReadFile(hFile,&TFileInfo,sizeof(TFileInfo),&dwRead,NULL);
dwPrev = PFileHeader->dwHeader; // 记录上一个节点

for ( int i = 1; i < PFileHeader->dwCount; i++ )
{
// 读取上一个节点头信息
SetFilePointer(hFile,dwPrev,NULL,FILE_BEGIN);
ReadFile(hFile,&TFileInfo_Prev,sizeof(TFileInfo),&dwRead,NULL);

if ( k == i )
{
// 读取待删除节点的节点头
SetFilePointer(hFile,FileInfo_Prev.dwNext,NULL,FILE_BEGIN);
ReadFile(hFile,&FileInfo_Delete,sizeof(TFileInfo),&dwRead,NULL);
TFileInfo_Prev.dwNext = FileInfo_Delete.dwNext; // 删除当前节点
break;
}
else
{
dwPrev = TFileInfo_Prev.dwAddress;
}
}
}


压缩文件 :
// 针对删除了一个节点之后,并不会删除这个节点的内容,导致文件的容量越来越大,可以使用重新建立一个文件的方式来压缩文件
方法 :
遍历整个队列,然后把这个队列中的有效节点(包括节点头 + 节点内容)重新按照排列写入到其它的一个文件中,待全部写入完毕.然后删除旧的文件,
再将新的文件rename 成旧的文件名,达到压缩文件的目的


队列的排序与查找 :
// 在程序第一次启动的时候,可以将整个队列的节点头(不包括节点内容)全部装载到内存,然后按照某种方式排序.
// 在排序好之后的查找就很快了.
// 当查找到某一个节点之后,直接使用这个节点的dwAddress 就可以定位到文件中这个节点的内容,使用ReadFile 读取出来就可以了



呵呵 :
一个弱智的想法,请大家讨论



[解决办法]

探讨
内存式数据库就算了,如果用内存的话我就直接使用队列了,呵呵

[解决办法]
主要的是一先先出列queue.管理是比支持取的 vector 容易些。
看量大小。
如果量太大, > 1G , 用多文件容易做些。如果量小於10M,也就什好心的了。
可以考每文件 10M 左右,可以保取速度,用一索引文件文件及列。
因是自行控制的,所以就能做到除而不用重文件,只要重索引(先先出列的尾指)即可。也能很容易算到要的,的回收重用即可。程度不大,在於索引的格式,因索引文件是要全部入存的。

如果大,都放在同一文件中的,就要熟悉底的文件操作了。但使用底的文件操作,直接操作硬,速度更快些,要FAT32 NTFS等格式的不同而不同的程序,程度大。光ISO文件及GHOST克隆文件就是超大文件的例子。

自行程直接取foxbase文件都比通用 foxbase引擎取得快,所以使用foxbase也就有行速度上的,但能快速程的目的。
在queue的上,只需要支持queue尾操作的,自行程要比用任何一都快得多。因都是支持取的。


[解决办法]
我曾的做法是,使用文件索引+的方式,但文件索放在文件尾部,不怕文件大小超文件索引表限制。
[解决办法]
建议用 SQLite.

[解决办法]
首先,我先否定前面一部分兄弟的方案,要么内存开销大,要么文件会无限增长;

因为你的数据结构是个队列,队列成员是块,块大小不一定(还可能比较大),
所以,你可以这样设计:

首先,把文件分成一个一个的页,每个页的大小是固定的;(比如4k字节一个页)
一个数据块可能使用多个页,所以,需要为每个块建立一个[页表];
还需要一个垃圾回收机制,就是全局的[垃圾页表];
如果要加入一个块,按照块大小去[垃圾页表]申请页,如果[垃圾页表]里面不足,就建立新页;
如果要释放一个块,就把这个块的页加到[垃圾页表]里面去;

这样一来,我们就可以在文件里面随意加块删块了;

最后,别忘记在文件前面开辟一个固定大小的空间,存储一个[队列块表],一切就ok了;
[解决办法]
好复杂!
就用access好了,建个表用个自增长字段当主键;
选择队首时就直接用select top 100 * from table 来查;
插入数据到队尾就是直接 insert 语句即可,
效率怎么样未知,就是简单而已
[解决办法]
和上面某些观点类似,建立若干相同大小的文件,比如1M,然后用一个文件索引。
以文件为单位,如果要存2.1M数据,就占3个文件。删除的时候删除对应文件就行。
这种方式类似于FAT32存储,个人认为是最快的方式。
------解决方案--------------------


1:感觉是在设计一个数据库系统
2:如果支持索引之类的话,看来需要考虑存储结构,而读写嘛,应该都是直接的API了,最重要的地方应该是在数据文件的存储结构方面,能够协助快速定位的模式。没有阅读过mysql的代码,如果看一下他的文件存储数据结构,我想一定会有很大收获
3:xml出现之后,很多人开始使用xml存储数据库,个人感觉大部分时候都是夸大了xml的能力,xml除了结构方便定义之外,在存储以及定位方面效率很低,两种模型都非常的不利于检索
4:对于多线程读写的时候,必须加锁,mysql中在写入数据都是
LOCK TABLES `cc_alarm` WRITE;
//operator
UNLOCK TABLES;

所以,同样也是应该加锁,另外记得刀客在看到我的一个操作mysql的类的时候,提出了我没有考虑事务过程,我想如果考虑事务的话,我想应该是在Log中保留此次执行的所有模式,在某个失败通过Log回滚。
5:大家继续

读书人网 >C++ Builder

热点推荐