在Delphi中读取二进制文件

14K*_*14K 2 delphi

我想读取二进制文件并在备忘录中显示结果但不知道如何处理此错误:"不兼容的类型:'字符串'和'数组'",代码是这样的

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;
Run Code Online (Sandbox Code Playgroud)

设法避免此错误并运行程序没有错误?

有人可以帮帮我吗?

Ken*_*ite 11

您不能直接绕过此错误,因为a string和a array[] of Byte不能直接分配.

由于二进制内容(#0特别是字符或0x00十六进制值(C/C++))TMemo无论如何都不会显示(文本将以第一个#0值终止),因此您需要将其替换为某些内容.

超越编译器错误的最简单方法是将数组从更改array[] of Bytearray[] of AnsiChar,可以直接分配给string(或类型转换为1):

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;
Run Code Online (Sandbox Code Playgroud)

但是,就像我说的,这不会解决备忘录中显示的问题.例如,当实际读取Windows可执行文件时,显示第一个缓冲区MZP,因为第四个字节是a #0,而memo终止字符串.

要克服此限制,您需要#0用其他内容替换所有字符.当然,问题在于,您替换它的任何值实际上也可能出现在可执行文件中(因为它们是字节,只有256个可能的值).同样,简单的解决方案是#00(#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;
Run Code Online (Sandbox Code Playgroud)

TForm.FormCreate是实际从Delphi控制台应用程序读取1K缓冲区的事件的代码,执行上述替换,并在a中显示内容TMemo.放下TMemo表单,将其Alignment属性设置为alClient,然后设置ScrollBarsssVertical.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;
Run Code Online (Sandbox Code Playgroud)

注意:如果您实际上是在读取整个二进制文件而不是第一个缓冲区,则最后一个缓冲区可能不会完全填满文件内容(您可能在最后一次传递时没有读取完整缓冲区).在这种情况下,您希望用缓冲区标记缓冲区的末尾,#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;
Run Code Online (Sandbox Code Playgroud)

这是读取单个缓冲区满的输出:

TMemo中显示的可执行文件


Rob*_*edy 6

字节数组不是字符串,因此编译器的消息意味着它所说的内容.字节是数字; 字符串是文本的.数字不是文本,因此在过程的某个地方,您需要告诉程序如何将数字转换为文本.

一种方法是将每个数值转换为其对应的数字.例如:

F.Read(Buffer, 1024);
s := '';
for b in Buffer do
  s := s + IntToStr(b);
Memo1.Lines.Add(s);
Run Code Online (Sandbox Code Playgroud)

如果您希望将每个字节转换为具有相应数值的字符,那么您根本不需要任何转换; 只是撒谎并告诉程序该文件包含文本,以便您可以将其直接加载到备忘录控件中:

Memo1.Lines.LoadFromFile(FileName);
Run Code Online (Sandbox Code Playgroud)

如果希望文件中可以表示可打印字符的字节按原样显示,而表示不可打印字符的字节以数字形式显示,则可以单独处理每个字符,类似于上面的循环:

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);
Run Code Online (Sandbox Code Playgroud)

您可以按照自己的方式定义数据转换.您所要做的就是指定所需的输出,然后编写生成它的代码.如果您没有指定所需内容,并且无法准确描述,那么您还没有为代码做好准备.