Inno setup 使用Inno设置在所需位置从文件读取字节

Inno setup 使用Inno设置在所需位置从文件读取字节,inno-setup,pascalscript,Inno Setup,Pascalscript,我想打开一个50MB的二进制文件,只读取最后4个字节,并将其转换为字符串 我现在发现的唯一方法是使用LoadStringFromFile,将文件完全加载到内存中,然后复制最后4个字节,但是这种方法非常慢,因为二进制文件很重 在Inno安装脚本中有更好的方法吗 更新: 这是我根据Martin Prikryl的答案编辑的最后一个工作函数 函数readlast4byte():AnsiString; 变量 流:TFileStream; 缓冲区:字符串; 计数:整数; 索引:整数; 开始 计数:=4;

我想打开一个50MB的二进制文件,只读取最后4个字节,并将其转换为字符串

我现在发现的唯一方法是使用
LoadStringFromFile
,将文件完全加载到内存中,然后复制最后4个字节,但是这种方法非常慢,因为二进制文件很重

在Inno安装脚本中有更好的方法吗

更新:


这是我根据Martin Prikryl的答案编辑的最后一个工作函数

函数readlast4byte():AnsiString; 变量 流:TFileStream; 缓冲区:字符串; 计数:整数; 索引:整数; 开始 计数:=4; Stream:=TFileStream.Create('C:\test.txt',fmOpenRead); 尝试 Stream.Seek(-Count,soFromEnd); 设置长度(缓冲区,1); 设置长度(结果、计数); 对于索引:=1进行计数 开始 读取缓冲区(缓冲区,1); 结果[索引]:=Chr(Ord(缓冲区[1]); 结束; 最后 免费; 结束; 结束;
更新2:

这也是TLama编写的另一个伟大的工作函数,也应该标记为答案:

[代码]
#IFNDEF Unicode
#定义字符大小1
#否则
#定义字符大小2
#恩迪夫
类型
TSeekOrigin=(
索贝金宁,
soCurrent,
苏恩德
);
#IFDEF UNICODE
函数BufferToAnsi(const Buffer:string):AnsiString;
变量
W:单词;
I:整数;
开始
设置长度(结果、长度(缓冲区)*2);
对于I:=1到长度(缓冲区)do
开始
W:=Ord(缓冲区[I]);
结果[(I*2)]:=Chr(W shr 8);//高字节
结果[(I*2)-1]:=Chr(字节(W));//低字节
结束;
结束;
#恩迪夫
函数ReadStringFromFile(常量文件名:字符串;原点:TSeekOrigin;偏移量,长度:整数;
var S:AnsiString):布尔值;
变量
缓冲区:字符串;
流:TFileStream;
开始
结果:=真;
尝试
Stream:=TFileStream.Create(文件名,fmOpenRead);
尝试
寻道(偏移,Ord(原点));
SetLength(缓冲区,长度div{#CharSize});
ReadBuffer(缓冲区,长度);
#IFNDEF UNICODE
S:=缓冲区;
#否则
S:=BufferToAnsi(缓冲区);
#恩迪夫
最后
免费;
结束;
除了
结果:=假;
结束;
结束;

您可以使用
TFileStream

TStream=class(TObject)
函数读取(缓冲区:字符串;计数:Longint):Longint;
函数写入(缓冲区:字符串;计数:Longint):Longint;
函数搜索(偏移量:Longint;原点:Word):Longint;
过程读取缓冲区(缓冲区:字符串;计数:Longint);
过程WriteBuffer(缓冲区:字符串;计数:Longint);
函数CopyFrom(源:TStream;计数:Longint):Longint;
物业位置:朗讯;读写;
属性大小:Longint;读写;
结束;
THandleStream=类(TStream)
构造函数创建(AHandle:Integer);
属性句柄:整数;阅读
结束;
TFileStream=类(THandleStream)
构造函数创建(文件名:字符串;模式:Word);
结束;
使用
.Seek(-4,soFromEnd)
属性查找指向所需位置的读取指针

一个复杂的问题是
TStream
使用的是字符而不是字节,因此您必须将读回的Unicode字符串转换为字节

仅读取四个字节时,逐字节读取更容易,防止任何多字节转换:

过程ReadFileEnd;
变量
流:TFileStream;
缓冲区:字符串;
计数:整数;
索引:整数;
开始
计数:=4;
Stream:=TFileStream.Create('my\u binary\u file.dat',fmOpenRead);
尝试
Stream.Seek(-Count,soFromEnd);
设置长度(缓冲区,1);
对于索引:=1进行计数
开始
读取缓冲区(缓冲区,1);
日志(格式('Byte%2.2x:%2.2x',[Index,Ord(Buffer[1])]));
结束;
最后
免费;
结束;
结束;

这里有一个来自@TLama的通用替代方案,可以有效地处理任意大的读取:



顺便说一句,对我来说,似乎足够有效地加载50MB的文件。只需40毫秒。

最好使用
Seek
soEnd
原点。在确定流大小时,您将节省从开始到结束的查找开销。@特拉玛:当然,谢谢,我忽略了
查找
。虽然我不能使用
TFileStream
Read
ReadBuffer
都有错误的签名。
Buffer
不是“By ref”参数。这应该可以。您可能忘记分配缓冲区。这就是
ReadBuffer
不能为您做的事情。更糟糕的是,在Unicode Inno设置中,必须将每个字符分解为字节,才能从Unicode字符中获得ANSI字符。应该有相当多的字节参数数组。另外,OP知道
LoadStringFromFile
函数。干得好!我想添加的是一个
try..finally
块,因为异常的风险相当高。但这只是一个细节。这是。哦,很抱歉,我没有注意到Inno设置中没有
soEnd
选项。还有一个是索夫罗曼德
。非常感谢Martin和@TLama,两个答案都很好,我测试了两个代码,效果很好。我昨天尝试了CreateFile/ReadFile api,我花了很多时间来申报这个职位。这个来自TLama的代码是一个完整版本的功能,对我来说很安全,你应该把它作为一个答案发布,这个网站应该有一个功能,将2个答案标记为批准的答案。