Тонкости работы со строками в Delphi - Нулевой символ в середине строки

ОГЛАВЛЕНИЕ


Нулевой символ в середине строки

Хотя символ #0 и добавляется в конец каждой строки AnsiString, он уже не является признаком её конца, т.к. длина строки хранится отдельно. Это позволяет размещать символы #0 и в середине строки. Но нужно учитывать, что полноценное преобразование такой строки в PChar невозможно - это иллюстрируется примером Zero (в этом примере на форме одна кнопка и две метки):
procedure TForm1.Button1Click(Sender: TObject);
var
  S1, S2, S3: string;
  P: PChar;
begin
  S1 := 'Test'#0'Test';
  S2 := S1;
  UniqueString(S2);
  P := PChar(S1);
  S3 := P;
  Label1.Caption := IntToStr(Length(S2));
  Label2.Caption := IntToStr(Length(S3));
end;
В первую метку будет выведено число 9 (длина исходной строки), во вторую - 4. Мы видим, что при копировании одной строки AnsiString в другую символ #0 в середине строки - не помеха (вызов UniqueString добавлен для того, чтобы обеспечить реальное копирование строки, а не только копирование указателя). А вот как только мы превращаем эту строку в PChar, информация о её истинной длине теряется, и при обратном преобразовании компилятор ориентируется на символ #0, и строка обрубается.

Потеря куска строки после символа #0 происходит всегда, когда есть преобразование ShortString или AnsiString в PChar, даже неявное. Например, все API-функции работают с нуль-терминированными строками, а визуальные компоненты - просто обёртки над этими функциями, поэтому вывести с их помощью на экран строку, содержащую #0, целиком невозможно.

Но главный подводный камень, связанный с символом #0 в середине строки, заключается в том, что целый ряд стандартных функций для работы со строками AnsiString на самом деле используют API-функции (или даже библиотечные функции Delphi, предназначенные для работы с PChar), что приводит к игнорированию "хвоста" после #0. Следующий код (пример ZeroFind) иллюстрирует эту проблему:
procedure TForm1.Button1Click(Sender: TObject);
begin
  Label1.Caption := IntToStr(AnsiPos('Z', 'A'#0'Z'));
end;
Хотя символ "Z" присутствует в строке, в которой производится поиск, на экран будет выеден "0", что означает отсутствие искомой подстроки. Это связано с тем, что функция AnsiPos использует функции StrPos и CompareString, предназначенные для работы со строками PChar, поэтому поиск за символом #0 не производится. Если заменить в этом примере функцию AnsiPos на Pos, которая работает с типом AnsiString должным образом, на экран будет выведено правильное значение "3".

Описанные проблемы заставляют очень осторожно относиться к использованию символа #0 в середине строк AnsiString - это может стать источником неожиданных проблем.