ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 18.01.2025
Просмотров: 763
Скачиваний: 2
СОДЕРЖАНИЕ
Строковые типы в Delphi. Особенности реализации и использования
Какие строковые типы существуют в Delphi, и чем они отличаются друг от друга?
Преобразование строк из одного типа в другой
Функции для работы со строками, о которых многие часто забывают, или вовсе не знают
If AnsiMatchText(YouName,['Вася','Петя','Гриша']) then
Запись строк в файл и чтение из файла
Использование строк в качестве параметров и результатов функций размещенных в dll.
Функции для работы со строками, о которых многие часто забывают, или вовсе не знают
Как ни странно, но очень часто в форумах можно встретить вопросы, а главное ответы на них, показывающие, что многие программисты, пишущие на Delphi не знают о существовании некоторых, весьма полезных функций.
В этой части статьи я кратко расскажу о тех, которые, на мой взгляд, бывают полезны. Я не буду подробно о них рассказывать. Точное их описание Вы сможете найти в справочной системе. Здесь же я лишь упомяну о том, как обычно их использую я, и для некоторых из них приведу пример использования из реальной жизни. Рассматривайте данный раздел, как ссылки на функции, о которых вам следует почитать в справочной системе.
procedure SetLength (var S; NewLength: Integer)
Позволяет распределить память для динамической строки. Замечу, если строка уже содержит некоторое число символов, они копируются в результирующий экземпляр. Если конечно влезут. Это позволяет "расширять" уже существующий экземпляр строки.
Очень часто я использую эту функцию, когда хочу использовать динамическую строку как буфер. Например, при вызове функций Windows API:
function ComputerNetName :String;
// Возвращает сетевое имя компьютера
var Len :Cardinal;
begin
Len := MAX_COMPUTERNAME_LENGTH+1;
SetLength(Result,Len); // выделить память для буфера
if GetComputerName(pChar(Result),Len) then
SetLength(Result,Len) // установить точный размер
else
Result := '';
end;
procedure SetString(var s: string; buffer: PChar; len: Integer);
Распределяет память под экземпляр строки, копирует в него len байт из буфера buffer (если он не Nil), и присваивает ссылку на этот экземпляр переменной s. После вызова этой функции, значение счётчика ссылок полученного экземпляра всегда равно 1.
Её удобно использовать, когда нужно преобразовать в строку массив символов, не заканчивающийся #0.
function SysErrorMessage(ErrNo: Integer): string;
// Получение описания ошибки по коду
var
Size :Integer;
Buffer :array[0..1024] of Char;
begin
Size := FormatMessage
(FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_ARGUMENT_ARRAY
,nil ,ErrNo ,0 ,@Buffer,SizeOf(Buffer) ,nil
);
SetString(Result, Buffer, Size);
end;
procedure UniqueString (var str: string)
Гарантирует что после вызова, str будет указывать на уникальный экземпляр значеничя строки. Т.е., str будет указывать на строку счетчик ссылок которой будет равен 1. Если необходимо, делает копию исходного значения. Обычно нет необходимости использовать эту функцию. Однако, иногда мне надо получить указатель на строку (pChar), для того что бы изменить её. А поскольку, как я уже говорил, указатели pChar не обеспечиваются контролем за изменением строк. Это может привести к побочным изменениям в "других" строках, если счётчик ссылок исходной строки был >1. Вот и приходится применять эту функцию.
procedure Wr (Msg :String);
// вывод строки в консольное окно в OEM кодировке
begin
UniqueString(Msg); // получить "персональный" экземпляр строки
Windows.CharToOem(pChar(Msg),pChar(Msg)); // преобразование его
Write(Msg); // вывод преобразованного экземпляра на консоль
end;
Здесь, поскольку параметр передаётся по значению, при вызове будет создана локальная копия переменной (указателя) Msg. Об этом я ещё расскажу ниже. Эта переменная, при завершении процедуры будет уничтожаться, что приведёт к освобождению "персональной" копии экземпляра переданной и преобразованной строки.
function AnsiCompareStr(const S1, S2: string): Integer;
function AnsiCompareText(const S1, S2: string): Integer;
function CompareStr(const S1, S2: string): Integer;
function CompareText(const S1, S2: string): Integer;
Функции этой группы используются для сравнения строк. Результатом будет одно из значений:
>0 если S1 > S2 <0 если S1 < S2 =0 если S1 = S2
Особенно удобно использование этих функций при реализации алгоритмов сортировки.
Разница между ними следующая:
Те, чьё имя заканчивается на Str, выполняют точное сравнение с учётом регистра символов, а те, что заканчиваются на Text, выполняют сравнение без учета регистра символов.
Те чьё имя начинается на Ansi, сравнивают с учётом упорядоченности символов определяемой текущей языковой настройкой Windows. Функции без Ansi, сравнивают символы как 8-битные значения, что для некоторых языковых кодировок может дать не совсем правильные результаты. В общем случае, при таком сравнении, правильным можно считать лишь результат сравнения строк содержащих только латинские символы. Например, если сравниваемые строки содержат русскую буквы 'Ё' или 'ё', то результат сравнения будет неправильным. Поэтому, я практически всегда использую функции с префиксом Ansi. Даже учитывая то, что они работают чуть медленнее.
Такой же принцип формирования имён используется и в следующих функциях. Поэтому, я больше не буду останавливаться на различиях подобных функций.
function AnsiSameStr (const S1, S2: string): Boolean;
function AnsiSameText (const S1, S2: string): Boolean;
function SameText (const S1, S2: string): Boolean;
Эти функции возвращают True если обе строки равны, иначе их результат False. Обратите внимание, на отсутствие в этой группе функции SameStr. Её просто не существует! Да и зачем, если тот же результат можно получить, выполнив выражение S1 = S2.
function AnsiStartsStr (const ASubText, AText: string): Boolean;
function AnsiStartsText (const ASubText, AText: string): Boolean;
Возвращают True если строка AText начинается с ASubText.
function AnsiMatchStr (const AText: string; const AValues: array of string): Boolean;
function AnsiMatchText (const AText: string; const AValues: array of string): Boolean;
Возвращают True если строка AText встречается среди списка AValues. Пример использования:
If AnsiMatchText(YouName,['Вася','Петя','Гриша']) then
ShowMessage('Ты мальчик')
else if AnsiMatchText(YouName,['Света','Оля','Настя']) then
ShowMessage('Ты девочка')
else
ShowMessage('Я не знаю тебя!');
function AnsiIndexStr (const AText: string; const AValues: array of string): Integer;
function AnsiIndexText (const AText: string; const AValues: array of string): Integer;
Возвращают индекс строки AText в списке AValues. Первый элемент списка имеет индекс 0. Если же AText в списке AValues не встречается, возвращает -1. Очень удобные функции, жаль что об их существовании многие не догадываются. Приведу пример использования:
case AnsiIndexText(Cmd,['вверх','вниз','влево','вправо']) of
0 : Dec(y);
1 : Inc(y);
2 : Dec(x);
3 : Inc(x);
else ShowMessage('Незнакомая команда: "'+Cmd+'"');
end;
function AnsiReplaceStr (const AText, AFromText, AToText: string): string;
function AnsiReplaceText (const AText, AFromText, AToText: string): string;
Позволяют выполнить замену контекста AFromText на AToText в строке AText.
function StuffString(const AText: string; AStart, ALength: Cardinal; const ASubText:
string): string;
Заменяет фрагмент строки AText, начиная с позиции AStart и длиной ALength, строкой ASubText.
function StringOfChar(Ch: Char; Count: Integer): string;
Возвращает строку длинной Count, состоящую из символов Ch.
...
Короче, не забывайте на досуге читать файлы справки, найдёте там ещё много интересных и полезных функций.
Некоторые приемы использования строк типа AnsiString
В этом разделе, я постараюсь воздержаться от пространных комментариев, а попробую продемонстрировать некоторые приёмы работы с динамическими строками, учитывающие их реализацию.
Передача строк в качестве параметров
Как известно, есть четыре способа передачи параметров в процедуру или функцию. Вот какие особенности они имеют если параметр - динамическая строка:
-
входной параметр (const) – его значения нельзя изменять внутри, поэтому, передаётся адрес экземпляра динамической строки, без увеличения счётчика ссылок;
-
выходной параметр (out) – передаётся адрес строковой переменной которая будет получать результат. В общем случае значение такого параметра на входе в функцию считаются неопределённым, но для динамических строк, он всегда равен пустой строке. Естественно, счётчик ссылок не увеличивается – у пустой строки его просто нет. В месте вызова, и в начале процедуры, выполняется дополнительный код, обеспечивающий очистку строки. Конечно, на него тратится память и время.
-
параметр переменная (var) – передаётся адрес строковой переменной. Изменение этого параметра отражается изменением переданной переменной. Счётчик ссылок на экземпляр строки не увеличивается, поскольку передаётся не ссылка, а ссылка на ссылку;
-
параметр-значение – для такого параметра создается локальная копия переданного значения. При этом передаётся адрес экземпляра динамической строки, а в начале процедуры, при создании локальной копии параметра, увеличивается счётчик ссылок на экземпляр. На это тратится память и время.
Прочитав это, должна стать понятным следующее:
Если в качестве параметра передаётся динамическая строка, которая не будет изменяться внутри процедуры, то желательно описать этот параметр как входной (const). В отличие от простых типов (Integer, Boolean, и т.п.), это уменьшит код программы, и увеличит скорость ее работы.