Двоичная версия StringReplace - PullRequest
4 голосов
/ 24 июня 2010

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

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

Ответы [ 3 ]

4 голосов
/ 24 июня 2010

Я предполагаю, что функция "Offending" в 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;
1 голос
/ 24 июня 2010

Я не проводил расширенного тестирования, но я думаю, что этот код работает.

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 голосов
/ 24 июня 2010

Хм.Кажется, что не может быть слишком сложно написать свой собственный.Просто перебирайте буфер, пока не найдете совпадение с первым байтом.Затем посмотрите, совпадают ли последующие байты.Если это так, вы нашли его, теперь замените.Продолжайте или уходите, в зависимости от того, что вам нужно.Очевидно, проще, если размеры одинаковы.Если нет, то вы можете установить второй буфер и скопировать байты из базового буфера в новый буфер.

...