2010-06-23 3 views
3

J'essaie d'exécuter AnsiStrings.StringReplace sur un RawByteString contenant un blob de données, dont certaines doivent être remplacées. Cela fonctionnerait, sauf que dans StringReplace il convertit ma chaîne en PAnsiChar, et donc la recherche finit par renflouer dès qu'elle touche le premier octet # 0 à l'intérieur du blob.Version binaire de StringReplace

Je suis à la recherche d'une routine qui fonctionne exactement comme StringReplace, mais qui est sûre à utiliser sur les blobs qui peuvent contenir des octets nuls. Est-ce que quelqu'un en connaît un?

Répondre

3

Je suppose la fonction "Contrevenants" dans StringReplace est AnsiPos-> AnsiStrPos

Alors ... Je suppose que peu d'une solution de travail déjà , Je copierais/collerais le code StringReplace et changerais AnsiPos pour quelque chose d'autre. (c'est-à-dire 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

Hmm. On dirait que ça ne devrait pas être trop difficile d'écrire le vôtre. Passez juste à travers le tampon jusqu'à ce que vous trouviez une correspondance sur le premier octet. Ensuite, voir si les octets suivants correspondent. Si oui, vous l'avez trouvé, remplacez maintenant. Continuez ou arrêtez, selon ce dont vous avez besoin. Évidemment plus simple si les tailles sont les mêmes. Si ce n'est pas le cas, vous pouvez configurer un second tampon et copier les octets du tampon de base dans le nouveau tampon.

+0

Sure. Je préfèrerais juste voir une solution tierce testée qui a déjà été élaborée, si elle existe. –

1

Je n'ai pas effectué de tests approfondis, mais je pense que ce code fonctionne.

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; 

Pour tester:

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

Eh bien, une optimisation possible (et plutôt importante) dans le cas 'length (Replace)> length (Find)' est de supprimer le besoin de réaffecter la mémoire à chaque occurrence de Find. Il faut plutôt allouer un gros bloc pour commencer, puis garder une trace du point de fin réel, puis à la fin tronquer le bloc. (Et bien, si le bloc initial n'est pas assez grand? Alors augmentez la mémoire en gros blocs à la demande.) –

+0

En outre, si 'length (Replace) <> length (Find)' et 'BufLen' est grand (mais pas trop grand), alors il pourrait être préférable de ne pas faire un remplacement sur place, mais de créer un nouveau tampon. –

Questions connexes