You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Delphi中无需转换字节数组快速查找目标DWORD的高效方法求助

嘿,我太懂你这个痛点了——把字节数组转成十六进制字符串再搜索,光是转换步骤就吃掉了大部分性能,完全是绕远路!直接操作原始字节才是正确的优化方向,下面给你两种高效的实现方案,都不用碰字符串转换:

方案1:暴力匹配(简单高效,适合中小规模数组)

这种方法逻辑直白,直接在字节数组里逐段对比目标序列,没有任何额外的转换开销,比你原来的字符串搜索快得多。

首先先定义你的目标字节序列:

const
  TargetBytes: array[0..7] of Byte = ($01, $49, $08, $EF, $48, $C0, $C6, $91);
  TargetLength = Length(TargetBytes);

然后实现查找函数:

function FindTargetInByteArray(const ByteArray: array of Byte; out FoundIndex: Integer): Boolean;
var
  I, J: Integer;
  ArrayLen: Integer;
begin
  Result := False;
  FoundIndex := -1;
  ArrayLen := Length(ByteArray);
  
  // 如果数组长度比目标短,直接返回找不到
  if ArrayLen < TargetLength then
    Exit;
  
  // 遍历数组,最多到 ArrayLen - TargetLength 的位置(避免越界)
  for I := 0 to ArrayLen - TargetLength do
  begin
    // 先匹配第一个字节,不匹配就跳过,节省后续对比时间
    if ByteArray[I] = TargetBytes[0] then
    begin
      J := 1;
      // 对比剩余的7个字节
      while (J < TargetLength) and (ByteArray[I + J] = TargetBytes[J]) do
        Inc(J);
      
      // 如果全部8个字节都匹配成功
      if J = TargetLength then
      begin
        FoundIndex := I;
        Result := True;
        Exit; // 找到就直接退出,不用继续遍历
      end;
    end;
  end;
end;
方案2:KMP算法(极致高效,适合超大字节数组)

如果你的字节数组特别大(比如几十MB甚至更大),暴力匹配可能会有重复对比的浪费,这时候KMP算法就能发挥优势——它通过预处理目标序列生成前缀表,避免不必要的回溯,把时间复杂度降到O(n+m)(n是数组长度,m是目标长度)。

先实现前缀表生成函数:

// 生成KMP算法需要的前缀表,用来记录目标序列的最长公共前后缀
procedure ComputePrefixTable(const Pattern: array of Byte; var Prefix: array of Integer);
var
  Len, I, J: Integer;
begin
  Len := Length(Pattern);
  Prefix[0] := 0;
  I := 1;
  J := 0;
  
  while I < Len do
  begin
    if Pattern[I] = Pattern[J] then
    begin
      Inc(J);
      Prefix[I] := J;
      Inc(I);
    end
    else
    begin
      if J <> 0 then
        J := Prefix[J - 1]
      else
      begin
        Prefix[I] := 0;
        Inc(I);
      end;
    end;
  end;
end;

再实现KMP查找函数:

function FindTargetKMP(const ByteArray: array of Byte; out FoundIndex: Integer): Boolean;
var
  ArrayLen, PatternLen, I, J: Integer;
  Prefix: array[0..7] of Integer; // 目标是8字节,前缀表长度固定
begin
  Result := False;
  FoundIndex := -1;
  ArrayLen := Length(ByteArray);
  PatternLen := TargetLength;
  
  if ArrayLen < PatternLen then
    Exit;
  
  // 先生成目标序列的前缀表
  ComputePrefixTable(TargetBytes, Prefix);
  
  I := 0; // 字节数组的遍历指针
  J := 0; // 目标序列的匹配指针
  
  while I < ArrayLen do
  begin
    if ByteArray[I] = TargetBytes[J] then
    begin
      Inc(I);
      Inc(J);
      
      // 完全匹配目标序列
      if J = PatternLen then
      begin
        FoundIndex := I - J;
        Result := True;
        Exit;
      end;
    end
    else
    begin
      // 不匹配时,根据前缀表调整目标指针,避免回溯数组指针
      if J <> 0 then
        J := Prefix[J - 1]
      else
        Inc(I);
    end;
  end;
end;
调用示例

不管用哪种方法,调用方式都很简单:

var
  MyArray: array of Byte;
  PosIndex: Integer;
begin
  // 假设这里已经给MyArray填充了数据
  if FindTargetInByteArray(MyArray, PosIndex) then
    ShowMessage('找到目标,起始位置:' + IntToStr(PosIndex))
  else
    ShowMessage('未找到目标');
  
  // 或者用KMP版本(大数据量推荐)
  if FindTargetKMP(MyArray, PosIndex) then
    ShowMessage('找到目标,起始位置:' + IntToStr(PosIndex))
  else
    ShowMessage('未找到目标');
end;
额外优化小技巧

如果你的Delphi版本是XE及以上,可以在函数前加上{$RANGECHECKS OFF}来关闭范围检查(记得函数结束后加{$RANGECHECKS ON}恢复),能进一步提升执行速度——不过因为我们的代码已经做了边界判断,不用担心数组越界问题。


内容的提问来源于stack exchange,提问作者Myxbox2018acc Bdbhx

火山引擎 最新活动