Невозможно удалить "нулевые символы" из строки

Я задавал подобный вопрос пару месяцев назад. Благодаря Rob Kennedy я мог загрузить весь свой текст в Richedit НО я не мог удалить Null chars , Я мог загрузить свой текст, потому что я использовал Stream,


Теперь в этом коде:

var
  strm : TMemorystream;
  str  : UTF8string;
  ss   : TStringstream;

begin
  strm := tmemorystream.Create;

  try
    strm.LoadFromFile('C:\Text.txt');
    setstring(str,PAnsichar(strm.Memory),strm.Size);
    str := StringReplace(str, #0, '', [rfReplaceAll]);  //This line doesn't work at all
    ss  := tstringstream.Create(str);
    Richedit1.Lines.LoadFromStream(ss);
  finally
    strm.Free;
    ss.Free;
  end;
end;

Я преобразовал TMemorystream в string удалять Null Chars с StringReplace() а затем преобразовал его снова в TStringstream загрузить его Richedit.lines.LoadFromStream,

Но моя проблема в том, что я не могу удалить Null Character с помощью StringReplace(), Я могу заменить другие символы, но не #0,

Есть ли способ удалить нулевые символы прямо в TMemorystream и загрузить его в Richedit? Как? Если это невозможно или очень сложно, как я могу удалить их, когда преобразую текст в string?

Благодарю.

2 ответа

Решение

Насколько я вижу, все утилиты поиска / замены, в тот или иной момент, приводят входные данные к PChar, который '#0' является символом завершения. Следовательно, они никогда не проходят мимо струнной части, предшествующей первому Null. Возможно, вам придется разработать свой собственный механизм. Просто быстрый пример:

var
  i: Integer;
begin
  Assert(str <> '');
  i := 1;
  while i <= Length(str) do
    if str[i] = #0 then
      Delete(str, i, 1)
    else
      Inc(i);

Замена в потоке также будет включать в себя тестирование каждого символа, а затем настройку потока соответствующим образом, прежде чем двигаться дальше после того, как вы решите удалить его.

Ответ Сертака точен, и вы должны принять его. Если производительность важна, и у вас есть большая строка с частыми экземплярами нулевого символа, вам следует попытаться уменьшить количество выделений кучи. Вот как я бы это реализовал:

function RemoveNull(const Input: string): string;
var
  OutputLen, Index: Integer;
  C: Char;
begin
  SetLength(Result, Length(Input));
  OutputLen := 0;
  for Index := 1 to Length(Input) do
  begin
    C := Input[Index];   
    if C <> #0 then
    begin
      inc(OutputLen);
      Result[OutputLen] := C;
    end;
  end;
  SetLength(Result, OutputLen);
end;

Если вы хотите сделать это прямо в потоке памяти, то вы можете сделать это так:

procedure RemoveNullFromMemoryStream(Stream: TMemoryStream);
var
  i: Integer;
  pIn, pOut: PByte;
begin
  pIn := Stream.Memory;
  pOut := pIn;
  for i := 0 to Stream.Size-1 do
  begin
    if pIn^ <> 0 then
    begin
      pOut^ := pIn^;
      inc(pOut);
    end;
    inc(pIn);
  end;
  Stream.SetSize(NativeUInt(pOut)-NativeUInt(Stream.Memory));
end;
Другие вопросы по тегам