我想阅读二进制文件并在备忘录中显示结果,但不知道如何处理此错误:"不兼容的类型:' string'和'数组'",代码就是这个
unit yo;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
F: TFileStream;
Buffer: array [0 .. 1023] of byte;
begin
F := TFileStream.Create(ExtractFilePath(Application.ExeName)
+ 'yo.exe', fmOpenRead);
while F.Position < F.Size do
begin
F.Read(Buffer, 1024);
Memo1.Lines.Add(Buffer);
end;
F.Free;
end;
设法避免此错误并运行程序而没有错误?
任何人都可以帮助我吗?
答案 0 :(得分:10)
您无法直接绕过此错误,因为string
和array[] of Byte
无法直接分配。
由于二进制内容(特别是#0
字符或0x00
十六进制值(C / C ++))无论如何都不会在TMemo
中显示(文本将是在第一个#0
值终止时,您需要将其替换为某些内容。
最简单通过编译器错误的方法是将您的数组从array[] of Byte
更改为array[] of AnsiChar
,可以直接将其分配给string
(或者对一个人进行类型转换:
var
Buffer: array[0..1023] of AnsiChar;
TempStr: string;
begin
// Fill buffer from stream
TempStr := Buffer;
Memo1.Lines.Add(TempStr);
// The next line eliminates the need for `TempStr`
// Memo1.Lines.Add(String(Buffer));
end;
然而,就像我说的,这不会解决备忘录中显示的问题。例如,当实际读取Windows可执行文件时,第一个缓冲区显示MZP
,因为第四个字节是#0
,备忘录终止字符串。
要克服此限制,您需要将所有#0
个字符替换为其他字符。当然,问题在于,您替换它的任何值实际上也可能出现在可执行文件中(因为它们是字节,只有256个可能的值)。同样,简单的解决方案是用#0
替换所有0
个字符(#216):
var
Buffer: array[0..1023] of AnsiChar;
i: Integer;
TempStr: string;
begin
// Fill buffer as before
for i := Low(Buffer) to High(Buffer) do
if Buffer[i] = #0 then
Buffer[i] := `Ø`; // Try #144 instead
TempStr := Buffer;
Memo1.Lines.Add(TempStr);
// You can still eliminate the string variable by typecasting
// Memo1.Lines.Add(String(TempStr));
end;
这是实际从Delphi控制台应用程序读取1K缓冲区的TForm.FormCreate
事件的代码,执行上述替换,并在TMemo
中显示内容。将TMemo
放在表单上,将其Alignment
属性设置为alClient
,将ScrollBars
设置为ssVertical
。向表单添加FormCreate
事件处理程序,并对该事件使用以下代码:
procedure TForm1.FormCreate(Sender: TObject);
var
Stream: TFileStream;
Buffer: array[0..1023] of AnsiChar;
TempStr: string;
i: Integer;
begin
Memo1.Clear;
// Populate buffer elements
Stream := TFileStream.Create('D:\Temp\Project2.exe', fmOpenRead);
try
Stream.Read(Buffer[0], SizeOf(Buffer));
finally
Stream.Free;
end;
// Replace null (#0) values with #216 (Ø)
for i := Low(Buffer) to High(Buffer) do
if Buffer[i] = #0 then
Buffer[i] := 'Ø';
TempStr := Buffer;
Memo1.Lines.Add(TempStr);
end;
注意:如果您实际上是在读取整个二进制文件而不是第一个缓冲区,那么最后一个缓冲区可能不会完全填满文件内容(您可能在最后一次传递时没有读取完整缓冲区)。在这种情况下,您希望使用#0
标记缓冲区的末尾,以便备忘录正确显示该部分缓冲区。您可以更改for
循环以使用以下内容:
for i := Low(Buffer) to High(Buffer) do
begin
if (i = BytesRead) then
begin
Buffer[i] := #0; // Mark the end of the buffer and exit loop;
Break;
end
else if (Buffer[i] = #0) then
Buffer[i] := 'Ø';
end;
这是读取单个缓冲区的输出:
答案 1 :(得分:6)
字节数组不是字符串,因此编译器的消息意味着它所说的内容。字节是数字;字符串是文本的。数字不是文本,因此在过程的某个地方,您需要告诉程序如何将数字转换为文本。
一种方法是将每个数值转换为相应的数字。例如:
F.Read(Buffer, 1024);
s := '';
for b in Buffer do
s := s + IntToStr(b);
Memo1.Lines.Add(s);
如果您希望将每个字节转换为具有相应数值的字符,那么您根本不需要任何转换;只是撒谎并告诉程序该文件包含文本,以便您可以将其直接加载到备忘录控件中:
Memo1.Lines.LoadFromFile(FileName);
如果您希望文件中可以表示可打印字符的字节按原样显示,并且表示不可打印字符的字节以数字形式显示,则可以单独处理每个字符,类似于上面的循环:
F.Read(Buffer, 1024);
s := '';
for b in Buffer do begin
c := AnsiChar(b);
if TCharacter.IsControl(c) then
s := s + IntToStr(b)
else
s := s + c;
end;
Memo1.Lines.Add(s);
您可以按照自己的方式定义数据转换。您所要做的就是指定所需的输出,然后编写生成它的代码。如果您没有指定您想要的内容,并且无法准确描述,那么您还没有为代码做好准备。