有谁有 变长记录文件存取 的代码?(200分)

  • 主题发起人 主题发起人 cemi
  • 开始时间 开始时间
C

cemi

Unregistered / Unconfirmed
GUEST, unregistred user!
我有一个结构要进行储存, 但其中的Value字段的长度从几个字节到几千个字节, 加上这些记录的数量超一万个, 用定长的记录结构好像不太好, 因为大富翁的全文搜索不能用, 请有相关代码的大侠帮帮忙了。
TRegStru = record
KeyIndex: integer;
KeyType: TRegKeyType;
Action: TActionType;
Name: string;
Value: string;
end;
 
好写啊,,以 Delphi 的 Stream 为例,
先 write(Length(S), SizeOf(Integer)) 然后再 write(PChar(S)^, Length(S))
读取时
先 read(nLen, SizeOf(Integer)), 然后 SetLength(S, nLen), read(PChar(S)^, nLen)
 
PRegStru=^TRegStru
TRegStru = record
KeyIndex: integer;
KeyType: TRegKeyType;
Action: TActionType;
Name: string;
Value: string;
end;
RegList: TList

AReg: PRegStru;
.......
RegList.Add(AReg);//加入 1 条记录

.......
AReg:=RegList.Items;//读 1 条记录

function SaveToFile(FileName: string): Integer;//存储到文件
var
f : file of TRegStru ;
n : Integer;
begin
AssignFile(f, FileName);
result := IOResult

if result = 0 then
begin
for n := 0 to RegList.Count - 1 do
begin
AReg:=RegList.Items[n];
Write(f, AReg^);
end;
CloseFile(f);
end;

function LoadFromFile(FileName:string):integer;
var
f : file of TRegStru ;
n, Count: Integer;
begin
AssignFile(f, FileName);
result := IOResult

if result = 0 then
begin
Count:= FileSize(f);
for n := 0 to Count- 1 do
begin
New(AReg);
Read(f, AReg^);
RegList.Add(AReg);[8D]
end;
CloseFile(f);
end;
end;
试一下!哈!
代码:
 
To LSUPER
你说的只是实现这个的储存的最基本的方法, 但我想要的是具体一些的代码, 主要是考虑到稳定性的问题。

To dorry
你的办法对于那些固定大小的结构可以, 但如果你不规定那两个字符串的长度的话是不行的。
 
记录本来就是固定长度的,谈何变长记录。
 
To 楼主:
1、首先纠正您一个错误,string 是一种指针,它的大小固定是 4 个字节,因此您的 TRegStruc 记录大小也是固定的。
2、含有字符串和数字混合的文件,跟您用何种记录存储没有太大关系,应该解决的主要问题是如何进行文件存取操作。
3、第一步,应该设计文件的存储结构。既然又要写,又要读,那么您首先应该想到,如果读取记录的时候不知道文件中一共有多少记录那是多么可怕。因此文件头中至少要含有 4 个字节的空间,用于记录文件中含有的记录数。
4、第二步,因为 Name、Value 字段都是 string 类型,您应该想到存储在文件中的每个记录都应该加上一个 4 字节的字段,用于存储本条记录的大小,否则读取文件时无法判断每次应该读取多少字节才算是一条完整的记录。另外 Name、Value 相邻,并且都是字符串,它们之间至少应该用 0 隔开,否则你无法判断哪个属于 Name 哪个属于 Value。
文件的结构如下(注意文件结构跟您的 TRegStruc 记录毫无关系):
------------文件开头-------------
记录总数(4字节)
------------第一条记录-----------
本条记录大小(4字节)
KeyIndex(4字节)
KeyType(谁知道 TRegKeyType 多少狗屁字节,只有某人知道)
Action(同上)
Name(N 字节)
#0
Value(M 字节)
#0
------------第二条记录-----------
...
------------文件结尾-----------
5、200 分还算可以,楼主迫切想知道代码,给您写了一个。其实这类问题本没有难度,只是很多人拘泥于什么“二进制文件vs文本文件vs无类型文件”、“固定长度记录文件vs变长记录文件”,造成这个后果的罪魁祸首是 Delphi 中的所谓“类型文件”。其实文件本身没有区别,只是读取方式不同。
type
TRegStruc = record
KeyIndex: Integer;
KeyType: TRegKeyType;
Action: TActionType;
Name: string;
Value: string;
end;
TRegStrucs = array of TRegStruc;

const
//HEAD_SIZE 是 TRegStruc 中固定长度部分的大小
HEAD_SIZE: Integer = SizeOf(TRegStruc) - SizeOf(string)*2;

//RegStrucsNum 用于返回文件中含有的记录总数
//RegStrucsToRead 不用人工初始化,本过程会自动初始化
procedure ReadRegFile(const FileName: string
var RegStrucsToRead: TRegStrucs;
var RegStrucsNum: DWORD);
var
hFile: THandle;
n, r, i: DWORD;
buffer, p: PChar;
begin
hFile := CreateFile(PChar(FileName), GENERIC_READ, FILE_SHARE_READ, nil,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_SEQUENTIAL_SCAN, 0);
ReadFile(hFile, n, SizeOf(n), r, nil);
RegStrucsNum := n;
SetLength(RegStrucsToRead, RegStrucsNum);
for i := 0 to RegStrucsNum - 1 do
begin
ReadFile(hFile, n, SizeOf(n), r, nil);
Dec(n, SizeOf(n));
GetMem(buffer, n);
ReadFile(hFile, buffer^, n, r, nil);
p := buffer;
Move(p^, RegStrucsToRead, HEAD_SIZE);
Inc(p, HEAD_SIZE);
RegStrucsToRead.Name := p;
Inc(p, StrLen(p) + 1);
RegStrucsToRead.Value := p;
FreeMem(buffer);
end;
CloseHandle(hFile);
end;
//Overwrite 变量如果为 True 表示重写记录文件,否则是追加模式
//RegStrucsToWrite 需要人工初始化,并且事先赋值
procedure WriteRegFile(const FileName: string
RegStrucsToWrite: TRegStrucs;
const Overwrite: Boolean = False);
var
hFile: THandle;
n, w, i: DWORD;
buffer, p: PChar;
begin
n := 0;
if not Overwrite then
begin
hFile := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if GetFileSize(hFile, nil) > 0 then
ReadFile(hFile, n, SizeOf(n), w, nil);
SetFilePointer(hFile, 0, nil, FILE_BEGIN);
end
else hFile := CreateFile(PChar(FileName), GENERIC_WRITE, 0, nil, CREATE_ALWAYS, 0, 0);
Inc(n, Length(RegStrucsToWrite));
WriteFile(hFile, n, SizeOf(n), w, nil);
SetFilePointer(hFile, 0, nil, FILE_END);
for i := 0 to Length(RegStrucsToWrite) - 1 do
begin
n := SizeOf(n) + HEAD_SIZE + Length(RegStrucsToWrite.Name) + Length(RegStrucsToWrite.Value) + 2;
GetMem(buffer, n);
p := buffer;
Move(n, p^, SizeOf(n));
Inc(p, SizeOf(n));
Move(RegStrucsToWrite, p^, HEAD_SIZE);
Inc(p, HEAD_SIZE);
Move(PChar(RegStrucsToWrite.Name)^, p^, Length(RegStrucsToWrite.Name));
Inc(p, Length(RegStrucsToWrite.Name));
p^ := #0;
Inc(p);
Move(PChar(RegStrucsToWrite.Value)^, p^, Length(RegStrucsToWrite.Value));
Inc(p, Length(RegStrucsToWrite.Value));
p^ := #0;
WriteFile(hFile, buffer^, n, w, nil);
FreeMem(buffer);
end;
CloseHandle(hFile);
end;
6、也可以用 Delphi 自带的 AssignFile、CloseFile、Reset、Rewrite、Write、Read 之类的函数或过程,但是 Delphi 的 TextFile 文件类型会把 Write(f, 2) 中的整数 2 直接当作字符写入文件;而对于无类型文件,Write(f, 'abc') 之类的函数根本就不能用。
 
至于 delphi 的所谓“类型文件”,适宜操作不含有变长字段类型的记录,否则写进文件的无非都是 string 的指针。而用“无类型文件”的 BlockRead、BlockWrite,还不如用 Windows 的 API 来得实在,况且 delphi 的文件操作函数在底层也是调用的 API。
 
还不如用数据库来表示,实在不行就用clientdataset
 
楼主开始玩失踪了...
建议大富翁开个黑名单功能,把长久不结贴的主脱进去,他们没有资格在这里提什么问题...
如果再说得“官方”一点,大家来这里是交流的,一个巴掌拍不响,光“交”没“流”似乎也是违反了论坛的公平原则...
 
我准备开个 200 分的贴,请楼主来领,然后你再回馈给我 100 分,也算我没白在这回帖,给个安慰...
此贴我会一天一顶,谢谢!
 
哈哈
看把高手郁闷的
 
向楼主致敬!
 
多人接受答案了。
 

Similar threads

回复
0
查看
1K
不得闲
S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
后退
顶部