代码之家  ›  专栏  ›  技术社区  ›  Mason Wheeler

StringReplace的二进制版本

  •  3
  • Mason Wheeler  · 技术社区  · 14 年前

    我正在尝试对一个RawByteString运行AnsiStrings.StringReplace,它包含一个blob数据,其中一些需要被替换。它可以工作,除了在StringReplace中,它将我的字符串转换为PAnsiChar,因此一旦找到blob中的第一个#0字节,搜索就结束了。

    我正在寻找一个像StringReplace一样工作的例程,但是可以安全地用于可能包含空字节的blob。有人知道吗?

    3 回复  |  直到 14 年前
        1
  •  4
  •   Ken Bourassa    14 年前

    我猜StringReplace中的“冒犯”函数是AnsiPos->ANSISTROPS公司

    function RawByteStringReplace(const S, OldPattern, NewPattern: AnsiString;
      Flags: TReplaceFlags): AnsiString;
    var
      SearchStr, Patt, NewStr: AnsiString;
      Offset: Integer;
    begin
      //Removed the uppercase part...
      SearchStr := S;
      Patt := OldPattern;
    
      NewStr := S;
      Result := '';
      while SearchStr <> '' do
      begin
        Offset := AnsiStrings.PosEx(Patt, SearchStr);
        if Offset = 0 then
        begin
          Result := Result + NewStr;
          Break;
        end;
        Result := Result + Copy(NewStr, 1, Offset - 1) + NewPattern;
        NewStr := Copy(NewStr, Offset + Length(OldPattern), MaxInt);
        if not (rfReplaceAll in Flags) then
        begin
          Result := Result + NewStr;
          Break;
        end;
        SearchStr := Copy(SearchStr, Offset + Length(Patt), MaxInt);
      end;
    end;
    
        2
  •  1
  •   Andreas Rejbrand    14 年前

    我没有进行过广泛的测试,但我认为这段代码是有效的。

    type
      TDynByteArray = packed array of byte;
    
    procedure BufReplace(var BufStart: PByte; var BufLen: cardinal; const Find: TDynByteArray; const Replace: TDynByteArray);
    var
      pos: PByte;
      BufEnd: PByte;
      i: Integer;
      Match: boolean;
    begin
      {$POINTERMATH ON}
      if Find = nil then Exit;
      pos := BufStart;
      BufEnd := BufStart + BufLen;
      while pos < BufEnd do
      begin
        Match := false;
        if pos^ = Find[0] then
          if pos + length(Find) < BufEnd then
          begin
            Match := true;
            for i := 1 to high(Find) do
              if PByte(pos + i)^ <> Find[i] then
              begin
                Match := false;
                break;
              end;
          end;
          if Match then
          begin
            if length(Find) = length(Replace) then
              Move(Replace[0], pos^, length(Replace))
            else
            begin
              if length(Replace) < length(Find) then
              begin
                Move(Replace[0], pos^, length(Replace));
                MoveMemory(pos + length(Replace), pos + length(Find), BufEnd - pos - length(Find));
                dec(BufLen, length(Find) - length(Replace));
                ReallocMem(BufStart, BufLen);
              end
              else
              begin
                inc(BufLen, length(Replace) - length(Find));
                ReallocMem(BufStart, BufLen);
                MoveMemory(pos + length(Replace), pos + length(Find), BufEnd - pos - length(Find));
                Move(Replace[0], pos^, length(Replace))
              end;
            end;
            inc(pos, length(Replace));
          end
          else
            inc(pos);
      end;
    end;
    

    要测试它:

    procedure TestIt;
    var
      len: cardinal;
      a, b: TDynByteArray;
    begin
      len := 16;
      GetMem(buf, len);
      FillChar(buf^, 16, $11);
      PByte(buf + 3)^ := $55;
    
    
      SetLength(a, 2);
      a[0] := $55;
      a[1] := $11;
      SetLength(b, 1);
      b[0] := $77;
    
      BufReplace(buf, len, a, b);
    end;
    
        3
  •  0
  •   Chris Thornton    14 年前

    嗯。看来写自己的也不难。只需遍历缓冲区,直到找到第一个字节的匹配项。然后查看后续字节是否匹配。如果是,你找到了,现在换掉。继续或退出,取决于你需要什么。如果尺寸相同,显然更简单。如果没有,则可以设置第二个缓冲区,并将字节从基本缓冲区复制到新缓冲区中。