我正在以二进制形式将文件加载到数组中,这似乎需要一段时间 有没有更快更有效的方法来做到这一点。 我正在使用类似的方法写回文件。
procedure openfile(fname:string);
var
myfile: file;
filesizevalue,i:integer;
begin
assignfile(myfile,fname);
filesizevalue:=GetFileSize(fname); //my method
SetLength(dataarray, filesizevalue);
i:=0;
Reset(myFile, 1);
while not Eof(myFile) do
begin
BlockRead(myfile,dataarray[i], 1);
i:=i+1;
end;
CloseFile(myfile);
end;
答案 0 :(得分:16)
如果您真的想快速阅读二进制文件,请让Windows担心缓冲;-)使用Memory Mapped Files。使用它,您可以简单地将文件映射到内存位置,读取就像是一个数组。
你的职能将成为:
procedure openfile(fname:string);
var
InputFile: TMappedFile;
begin
InputFile := TMappedFile.Create;
try
InputFile.MapFile(fname);
SetLength(dataarray, InputFile.Size);
Move(PByteArray(InputFile.Content)[0], Result[0], InputFile.Size);
finally
InputFile.Free;
end;
end;
但我建议不要使用全局变量dataarray
,但要么将其作为参数传递给var,要么使用返回结果数组的函数。
procedure ReadBytesFromFile(const AFileName : String; var ADestination : TByteArray);
var
InputFile : TMappedFile;
begin
InputFile := TMappedFile.Create;
try
InputFile.MapFile(AFileName);
SetLength(ADestination, InputFile.Size);
Move(PByteArray(InputFile.Content)[0], ADestination[0], InputFile.Size);
finally
InputFile.Free;
end;
end;
TMappedFile来自我的文章Fast reading of files using Memory Mapping,本文还包含了如何将其用于更多“高级”二进制文件的示例。
答案 1 :(得分:15)
您通常不应该逐字节读取文件。使用较大值的BlockRead(通常最好使用512或1024)并使用其返回值来查找读取的字节数。
如果大小不是太大(并且你使用SetLength似乎支持这个),你也可以使用一次BlockRead调用一次读取整个文件。因此,修改您的方法,这将是:
AssignFile(myfile,fname);
filesizevalue := GetFileSize(fname);
Reset(myFile, 1);
SetLength(dataarray, filesizevalue);
BlockRead(myFile, dataarray[0], filesizevalue);
CloseFile(myfile);
也许您也可以将过程更改为名为OpenAndReadFile的布尔函数,如果无法打开或读取该文件,则返回false。
答案 2 :(得分:5)
这取决于文件格式。如果它由几个相同的记录组成,您可以决定创建该记录类型的文件。
例如:
type
TMyRecord = record
fieldA: integer;
..
end;
TMyFile = file of TMyRecord;
const
cBufLen = 100 * sizeof(TMyRecord);
var
file: TMyFile;
i : Integer;
begin
AssignFile(file, filename);
Reset(file);
i := 0;
try
while not Eof(file) do begin
BlockRead(file, dataarray[i], cBufLen);
Inc(i, cBufLen);
end;
finally
CloseFile(file);
end;
end;
答案 3 :(得分:3)
如果它是一个足够长的文件,以这种方式读取它会花费相当多的时间,我会使用流来代替。块读取速度会快得多,并且不需要担心循环。像这样:
procedure openfile(fname:string);
var
myfile: TFileStream;
filesizevalue:integer;
begin
filesizevalue:=GetFileSize(fname); //my method
SetLength(dataarray, filesizevalue);
myFile := TFileStream.Create(fname);
try
myFile.seek(0, soFromBeginning);
myFile.ReadBuffer(dataarray[0], filesizevalue);
finally
myFile.free;
end;
end;
从您的代码中可以看出,您的记录大小为1个字节长。如果没有,则将读取行更改为:
myFile.ReadBuffer(dataarray[0], filesizevalue * SIZE);
或类似的东西。
答案 4 :(得分:0)
寻找缓冲的TStream后代。由于磁盘读取速度很快,它会使您的代码更快,但您可以轻松地遍历缓冲区。有各种各样的,或者你可以写自己的。
答案 5 :(得分:0)
如果你感觉很笨,你可以完全绕过Win32并调用NT Native API函数ZwOpenFile(),这在我的非正式测试中确实削减了一点点。否则,我会使用Davy的Memory Mapped File解决方案。