2010-06-23 3 views
3

Я пытаюсь запустить AnsiStrings.StringReplace на RawByteString с блоком данных, некоторые из которых необходимо заменить. Он будет работать, за исключением того, что внутри StringReplace он преобразует мою строку в PAnsiChar, и поэтому поиск заканчивается, как только он попадает в первый байт # 0 внутри blob.Двоичная версия StringReplace

Я ищу подпрограмму, которая работает так же, как StringReplace, но безопасна для использования в блоках, которые могут содержать нулевые байты. Кто-нибудь знает об одном?

ответ

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

Несомненно. Я бы предпочел увидеть проверенное стороннее решение, в котором уже были обработаны краевые случаи, если они существуют. –

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. Скорее нужно выделить большой блок для начала, а затем отслеживать фактическую конечную точку, а затем в конце усечь блок. (Ну, что, если начальный блок недостаточно велик, тогда увеличьте память в больших блоках по запросу.) –

+0

Кроме того, если длина (Заменить) <> length (Найти) 'и' BufLen' велика (но не слишком большой), то лучше не делать замену на месте, а создавать новый буфер. –

Смежные вопросы