首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C++ Builder >

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

2012-03-21 
300分征求一个文件格式的设计思路要设计一种本地文件存储格式,可以象队列一样向队尾存数据,从队首取数据。

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:大家继续

热点排行