2010-06-23 95 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

相关问题