2010-06-23 99 views
3

我試圖在RawByteString上運行一個RawByteString,其中一些數據需要被替換。它會工作,除了在StringReplace內部它將我的字符串轉換爲一個PAnsiChar,所以當它遇到blob中的第一個#0字節時,搜索就結束了。StringReplace的二進制版本

我正在尋找一個像StringReplace一樣工作的例程,但可以安全地在可能包含空字節的blob上使用。有人知道嗎?

回答

3

我猜在StringReplace 「攻擊」 功能AnsiPos-> AnsiStrPos

所以...我想簡短已經工作液,我會複製/粘貼StringReplace代碼並將AnsiPos更改爲其他內容。 (即AnsiStrings.PosEx)

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; 
0

嗯。似乎寫出自己的想法不會太難。只需遍歷緩衝區,直到找到第一個字節的匹配。然後看看後面的字節是否匹配。如果是這樣,你找到了它,現在更換。繼續或退出,取決於你需要什麼。如果尺寸相同,顯然會更簡單。如果沒有,那麼你可以設置第二個緩衝區,並將基址緩衝區中的字節複製到新緩衝區中。

+0

當然。我只是希望看到一個經過測試的第三方解決方案已經有了邊緣案例,如果存在的話。 – 2010-06-23 23:35:58

1

我還沒有進行過廣泛的測試,但我認爲這段代碼有效。

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; 
+0

那麼,在'length(Replace)> length(Find)'的情況下,一種可能的(也是相當重要的)優化就是在每次Find時都不需要重新分配內存。而是應該先分配一個大塊,然後跟蹤實際的結束點,然後在最後截斷塊。 (那麼,如果初始塊不夠大,那麼按需增加大塊內存。) – 2010-06-24 00:51:19

+0

另外,如果'length(Replace)<> length(Find)'和'BufLen'很大(但不是太大),那麼最好不要做一個就地替換,而是創建一個新的緩衝區。 – 2010-06-24 09:06:12

相關問題