Тонкости работы со строками в Delphi - Нулевой символ в середине строки
ОГЛАВЛЕНИЕ
Страница 6 из 8
Нулевой символ в середине строки
Хотя символ #0 и добавляется в конец каждой строки AnsiString, он уже не является признаком её конца, т.к. длина строки хранится отдельно. Это позволяет размещать символы #0 и в середине строки. Но нужно учитывать, что полноценное преобразование такой строки в PChar невозможно - это иллюстрируется примером Zero (в этом примере на форме одна кнопка и две метки):procedure TForm1.Button1Click(Sender: TObject);В первую метку будет выведено число 9 (длина исходной строки), во вторую - 4. Мы видим, что при копировании одной строки AnsiString в другую символ #0 в середине строки - не помеха (вызов UniqueString добавлен для того, чтобы обеспечить реальное копирование строки, а не только копирование указателя). А вот как только мы превращаем эту строку в PChar, информация о её истинной длине теряется, и при обратном преобразовании компилятор ориентируется на символ #0, и строка обрубается.
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;
Потеря куска строки после символа #0 происходит всегда, когда есть преобразование ShortString или AnsiString в PChar, даже неявное. Например, все API-функции работают с нуль-терминированными строками, а визуальные компоненты - просто обёртки над этими функциями, поэтому вывести с их помощью на экран строку, содержащую #0, целиком невозможно.
Но главный подводный камень, связанный с символом #0 в середине строки, заключается в том, что целый ряд стандартных функций для работы со строками AnsiString на самом деле используют API-функции (или даже библиотечные функции Delphi, предназначенные для работы с PChar), что приводит к игнорированию "хвоста" после #0. Следующий код (пример ZeroFind) иллюстрирует эту проблему:
procedure TForm1.Button1Click(Sender: TObject);Хотя символ "Z" присутствует в строке, в которой производится поиск, на экран будет выеден "0", что означает отсутствие искомой подстроки. Это связано с тем, что функция AnsiPos использует функции StrPos и CompareString, предназначенные для работы со строками PChar, поэтому поиск за символом #0 не производится. Если заменить в этом примере функцию AnsiPos на Pos, которая работает с типом AnsiString должным образом, на экран будет выведено правильное значение "3".
begin
Label1.Caption := IntToStr(AnsiPos('Z', 'A'#0'Z'));
end;
Описанные проблемы заставляют очень осторожно относиться к использованию символа #0 в середине строк AnsiString - это может стать источником неожиданных проблем.