我正在尝试在RawByteString上运行AnsiStrings.StringReplace,其中包含一些数据,其中一些需要被替换。它可以工作,除了在StringReplace内部它将我的字符串转换为PAnsiChar,因此一旦它到达blob内的第一个#0字节,搜索就会结束。
我正在寻找一个像StringReplace一样工作的例程,但可以安全地用于可能包含空字节的blob。有人知道吗?
答案 0 :(得分:4)
我猜StringReplace中的“Offending”函数是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 :(得分: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;
答案 2 :(得分:0)
嗯。好像自己编写起来并不难。只需遍历缓冲区,直到找到第一个字节的匹配项。然后查看后续字节是否匹配。如果是这样,你找到它,现在更换。根据您的需要继续前进或退出。如果尺寸相同,显然更简单。如果没有,那么您可以设置第二个缓冲区并将基本缓冲区中的字节复制到新缓冲区中。