AnsiCompareStr — Функция Delphi

Функции Delphi модуля SysUtils

Модуль SysUtils

Предлагаем список функций модуля SysUtils, используемого в среде разработки Delphi.

AnsiCompareStr Сравнение двух строк на равенство
AnsiCompareText Сравнение двух строк на равенство
AnsiLowerCase Символы верхнего регистра изменяются в строку со строчными буквамм
AnsiPos Находит позицию одной строки в другой
ChangeFileExt Изменяет расширение имени файла
CompareStr Сравнивает две строки, чтобы увидеть, какая из них больше
CompareText Сравнивает две строки, игнорируя регистр
CreateDir Создаёт директорию
CurrToStr Преобразует денежную величину в строку
CurrToStrF Преобразует денежную величину в строку с форматированием
Date Возвращает текущую дату
DateTimeToFileDate Преобразует значение TDateTime в формат date/time формат файла
DateTimeToStr Конвертирует значение даты и времени TDateTime в строку
DateToStr Преобразует значение даты TDateTime в строку
DayOfWeek Выдает индекс дня недели для значения TDateTime
DeleteFile Удаляет файл, указанный в параметре
DirectoryExists Возвращает true, если указанная директория существует
DiskFree Выдает число свободных байтов на указанном диске
DiskSize Выдает размер указанного диска в байтах
EncodeDate Формирует значение TDateTime из значений года, месяца и дня
EncodeTime Формирует значение TDateTime из значений часа, минуты, секунды и миллисеккунды
ExtractFileDir Извлекает из полного имени файла название папки
ExtractFileDrive Извлекает из полного имени файла название диска
ExtractFileExt Извлекает из полного имени файла его расширение
ExtractFileName Извлекает из полного имени файла краткое имя файла
ExtractFilePath Извлекает из полного имени файла название патча
FileAge Получение дата/время последнего изменения файла, не открывая его
FileDateToDateTime Конвертирует формат даты/времени файла в значение TDateTime
FileExists Возвращает True если указанный файл существует
FileGetAttr Выдаёт атрибуты файла
FileSearch Поиск файла в одной или более папках
FileSetAttr Устанавливает атрибуты файла
FindClose Закрывает успешный FindFirst поиск файла
FindCmdLineSwitch Определяет, был передан некоторый параметр выключатель
FindFirst Находит все файлы, соответствующие маске файла и атрибутов
FindNext Находит следующий файл после успешного FindFirst
FloatToStr Преобразует значение с плавающей запятой в строку
FloatToStrF Преобразует значение с плавающей запятой в строку с форматированием
ForceDirectories Создаёт новый путь каталогов
Format Богатое форматирование чисел и текста в строке
FormatCurr Богатое форматирование значений валюты в строку
FormatDateTime Богатое форматирование переменной TDateTime в строку
FormatFloat Богатое форматирование числа с плавающей запятой в строку
GetCurrentDir Возвращает текущий каталог (диск плюс каталог)
IncMonth Увеличивает TDateTime переменную на некоторое число месяцев
IntToHex Преобразует целое число в шестнадцатеричную строку
IntToStr Конвертирует целое число в строку
IsLeapYear Возвращает True, если данный календарный год високосный
LastDelimiter Находит последнюю позицию указанных символов в строке
LowerCase Изменяет символы верхнего регистра в строке в строчные буквы
Now Выдает текущую дату и время
RemoveDir Позволяет удалить директорию
Rename Переименовка фала
RenameFile Переименование файла или директории
SetCurrentDir Изменяет текущую директорию
StrScan Ищет заданные символы в строке
StrToCurr Преобразует числовую строку в денежное выражение
StrToDate Конвертирует строку с датой в значение типа TDateTime
StrToDateTime Конвертирует строку с датой и временем в значение типа TDateTime
StrToFloat Преобразует числовую строку в значение с плавающей запятой
StrToInt Преобразует строку с целым значением в Integer
StrToInt64 Преобразует строку с целым значением в Int64
StrToInt64Def Преобразует строку с целым значением в Int64, учитывая значение по умолчанию
StrToIntDef Преобразует строку с значение с типом Integer, учитывая значение по умолчанию
Time Возвращает текущее время
TimeToStr Конвертирует значение времени типа TDateTime в строку
Trim Удаляет начальные и конечные пробелы в строке
TrimLeft Удаляет начальные пробелы в строке
TrimRight Удаляет конечные пробелы в строке
UpperCase Изменяет символы в строке из нижнего регистра в верхний
WrapText Добавьте перенос строки в строку, чтобы имитировать перенос слов
Abort Прерывает обработку команд и выходит к последнему исключительному блоку
AppendStr Конкатенация одной строки в конец другой
Beep Делает звук гудка
DateTimeToString Огромные возможности форматирования даты в строку
DecodeDate Извлекает значения года, месяца, дня из TDateTime переменной
DecodeDateTime Разбивает TDateTime переменную на ее части даты/времени
DecodeTime Разбивает значение TDateTime на отдельные значения времени
FreeAndNil Освобождение памяти объекта и установка его в nil
FreeMem Освобождает память, используемую переменной
GetLocaleFormatSettings Получает региональные значения для безопасных потоков функций.
ReplaceDate Изменяет только часть даты TDateTime переменной
ReplaceTime Изменяет только часть времени TDateTime переменной

AnsiCompareStr — Функция Delphi

This is the modern, Locale safe form of CompareStr.

All Ansi commands support multi-byte and accented characters.

It returns these values :

String1 String2 : +ve number

The comparison is not affected by length — it is carried out on a letter by letter basis. But a longer string is greater than a shorter, otherwise matching string.

The comparison is case sensitive.

Notes In Delphi :

Upper case letters > Lower case letters
Lower case letters > Numbers

AnsiCompareStr — Функция Delphi

Работа со строками Delphi позволяет извлечь из строки необходимую информацию и представить её в нужном виде. Система предоставляет весь спектр необходимых функций для работы со строками Delphi и преобразования строк Delphi в необходимые форматы:

  • числовой формат, целый и дробный с плавающей точкой;
  • формат времени, даты, даты-времени;
  • преобразование символов к верхнему или нижнему регистру;
  • сравнение строк, поиск в строке и копирование подстроки;
    и многие другие.

Непосредственно сами строки Delphi поддерживают единственную операцию, так называемую операцию конкатенации, то есть присоединения. Несмотря на «научное» название, операция конкатенации выполняет очень простую процедуру. С помощью операции конкатенации одна строка присоединяется к другой:

Результирующая строка S будет суммой двух слагаемых строк. Длина строки, то есть количество символов в строке, возвращается встроенной функцией

function Length(S: String): Integer;

Delphi работает со строками типа String, в котором длина строки записывается в начале строки, перед первым символом. Поэтому индекс первого символа в строке не 0, а 1. То есть, если:

S:=’Строка типа String’;

то S[1] — символ ‘С’, S[2] — символ ‘т’ , последний символ в строке — S[Length(S)], равный ‘g’.

Однако часто приходится иметь дело со строками типа PChar, которые использует операционая система Windows. В строках типа PChar длина строки определяется специальным символом конца строки — #0. Поэтому для использования функций Windows тип String необходимо предварительно переводить в тип PChar. Преобразование типа String в тип PChar выполняет функция

function PChar(S: String): PChar;

Для полноценной работы со строками Delphi используются следующие стандартные процедуры и функции:

Функции преобразования в числовой формат и обратно

Выполняя вычисления, используем для ввода данных и отображения результатов следующие функции, работающие со строками Delphi:

функция IntToStr(N: Integer): String
Преобразует целое число N в строку.
функция StrToInt(S: String): Integer
Преобразует строку S в целое число.
функция FloatToStr(X: Extended): String
Преобразует число с плавающей точкой X в строку.
функция StrToFloat(S: String): Extended
Преобразует строку S в число с плавающей точкой.

Процедуры и функции преобразования дат и времени

Сначала собственно функции, предоставляющие информацию о текущих дате и времени:

функция Now: TDateTime
Возвращает текущую дату и время.
функция Date: TDateTime
Возвращает текущую дату.
функция Time: TDateTime
Возвращает текущее время.

Далее, функции, работающие с составляющими даты и времени (год, месяц, число, день недели, часы, минуты, секунды и даже миллисекунды):

функция DayOfWeek(Date: TDateTime): Integer
Возвращает текущий номер дня недели: 1 — воскресенье, 7 — суббота.
процедура DecodeDate(Date: TDateTime; var Year, Month, Day: Word)
Разбивает дату Date на год — Year, месяц — Month и день — Day.
процедура DecodeTime(Time: TDateTime; var Hour, Min, Sec, MSec: Word)
Разбивает время Time на час — Hour, минуты — Min, секунды — Sec и миллисекунды — MSec.
функция EncodeDate(Year, Month, Day: Word): TDateTime
Объединяет год — Year, месяц — Month и день — Day в значение типа TDateTime.
функция EncodeTime(Hour, Min, Sec, MSec: Word): TDateTime
Объединяет час — Hour, минуты — Min, секунды — Sec и миллисекунды — MSec в значение типа TDateTime.

Наконец, собственно, функции, переводящие дату и время из формата TDateTime в строчный формат:

функция DateTimeToStr(DateTime: TDateTime): String
Преобразует дату и время DateTime в строку.
функция DateToStr(Date: TDateTime): String
Преобразует дату Date в строку.
функция TimeToStr(Time: TDateTime): String
Преобразует время Time в строку.

Следующие функции работают с параметрами возвращаемой строки. Функции с приставкой Ansi работают в том числе и с кириллическими символами. Если символы в строках предполагаются исключительно латинские, то можно использовать аналогичные функции, но без приставки Ansi:

функция AnsiLowerCase(const S: String): String
Возвращает строку S, преобразованную к нижнему регистру.
функция AnsiUpperCase(const S: String): String
Возвращает строку S, преобразованную к верхнему регистру.
функция Length(const S: String): Integer
Возвращает количество символов в строке S.
функция Trim(const S: String): String
Удаляет из строки S начальные и завершающие пробелы и управляющие символы.
функция TrimLeft(const S: String): String
Удаляет из строки S начальные пробелы и управляющие символы.
функция TrimRight(const S: String): String
Удаляет из строки S завершающие пробелы и управляющие символы.

Следующие функции сравнивают две строки между собой:

функция AnsiCompareStr(const S1, S2: String): Integer
Сравнивает две строки S1 и S2 с учётом регистра символов.
Возвращает значение 0 если S1>S2
функция AnsiCompareText(const S1, S2: String): Integer
Сравнивает две строки S1 и S2 без учёта регистра символов.
Возвращает значение 0 если S1>S2

Следующие функции осуществляют поиск в текущей строке подстроки, вставляют, удаляют или заменяют подстроку:

функция Pos(Substr: String; Str: String): Integer
Возвращает позицию (индекс) первого вхождения Substr в строке Str. Если Substr нет в Str, возвращает 0.
функция Insert(Source: String; var S: String; Index: Integer): Integer
Вставляет строку Source в строку S, начиная с номера символа, равного Index
процедура Delete(var S: String; Index, Count: Integer)
Удаляет из строки S подстроку, начинающуюся с номера символа, равного Index, и содержащую до Count символов.
функция StringReplace(const S, OldPattern, NewPattern: string; Flags: TReplaceFlags): String
Заменяет в строке S подстроку OldPattern на строку NewPattern с учётом флага TReplaceFlags. Для работы с этой функцией нужно создать переменную типа TReplaceFlags — это множество, и включить в него одно или оба значения из следующих:
rfReplaceAll — будут заменены все вхождения. Если это значение не будет включено во множество, то будет заменено только первое вхождение;
rfIgnoreCase — замена будет без учёта регистра символов. Если это значение не будет включено во множество, то замена будет чувствительна к регистру символов.

Наконец, функция копирования части строки:

функция Copy(S: String; Index, Count: Integer): String
Возвращает подстроку строки S, начиная с номера символа, равного Index и содержащую до Count символов.

Помимо прочего, эту функцию можно использовать для вывода нецелого числа с нужным количеством цифр после запятой. Для этого, сначала получаем строку из числа формата Real, затем находим функцией Pos позицию запятой в этой строке, прибавляем нужное количество и копируем в результат это количество символов. Вот готовая функция:

function RealToStr(X: Real; Count: Integer): String; //Count — количество цифр после запятой
var S: String;
N: Integer;
begin
S:=FloatToStr(X); //после запятой — длинная последовательность цифр
//DecimalSeparator — константа, содержащая истинный разделитель целой и дробной частей числа N:=Pos(DecimalSeparator, S); //позиция запятой в строке
//вычисляем длину строки с нужным количеством знаков после запятой:
if N=0 //если в строке нет запятой — это целое число, и
then N:=Length(S) //тогда просто выводим это число
else N:=N+Count; //иначе вычисляем длину строки
Result:=Copy(S, 1, N); //копируем часть строки в результат
end;

Илон Маск рекомендует:  Как добавить линию возле текста

Или вот, к примеру, текст модуля, обеспечивающего ввод в компонент Edit только чисел.

Сравнение строк

If ((UpperCase(raz)=’.GTP’) or (UpperCase(raz)=’.PTB’) or (UpperCase(raz)=’.GP3′)) then

Функция UpperCase создает копию MixedString со всеми символами преобразованными в верхний регистр. Эта функция является устаревшей, используйте вместо неё AnsiUpperCase, которая принимает во внимание многобайтовые строки, и символы с диакритическим знаком.

Функция AnsiCompareStr сравнивает String1 и String2 на равенство, игнорируя регистр. Это современная Региональная безопасная форма CompareStr. Все Ansi команды поддержкой многобайтовые символы. Она возвращает следующие значения: String1: отрицательное число String1 = String2: 0 String1 > String2: положительное число Сравнение не затрагивает длину оно выполняется по буквам. Но более длинная строка больше чем короткая.

Функция AnsiCompareStr сравнивает String1 и String2 на равенство, игнорируя регистр.

AnsiCompareStr — Функция Delphi

Интересует такой вопрос — как нужно правильно сравнивать между собой на равенство строки string ?

Я обычно сравниваю по старинке как S1 = S2 , иногда привожу к одному регистру через LowerCase .

Но заметил что в исходникак VCL строки часто сравниваются не с = , а спомощью функций CompareStr , AnsiCompareStr , AnsiCompareText и подобных.

Так чем же грозит сравнивание S1 = S2 и как правильно сравнивать строки ?

AnsiCompareStr сравнивает с учетом локали. CompareStr может не работать на национальных алфавитах


> как правильно сравнивать строки ?

S1 = S2

все остальное имеет отношение к сравнению текста, ибо строка это тип переменной

Если только на равенство и без учета тек.локали, то вариант S1=S2 вполне достаточен.

CompareStr — регистрочувствительное сравнение на меньше/равно/больше без учета тек.локали

CompareText — регистронечувствительное сравнение на меньше/равно/больше без учета тек.локали

AnsiCompareStr — регистрочувствительное сравнение на меньше/равно/больше с учетом тек.локали

AnsiCompareText — регистронечувствительное сравнение на меньше/равно/больше с учетом тек.локали.

Что «правильней» — это решать тебе в каждом конкретном случае.

«»ABC и «ABC » — это разные или одинаковые строки?

Т.е. если мне нужно сравнивать на посимвольное равенство — то метод S1 = S2 вполне рабочий ?

Просто прочитал статью http://www.delphikingdom.com/asp/viewitem.asp?catalog >поразился множеству вариантов сравнения, и стали меня одолевать смутные сомнения, что я чегото неправильно делаю.

И еще смутило то, что для CompareStr используется функция, написанная на ассемблере, но уже для AnsiCompareStr — функция WinAPI.


> Anatoly Podgoretsky © (08.07.09 11:45) [4]
> «»ABC и «ABC » — это разные или одинаковые строки?

Если набранные только латиницей или только кирилицей — то одинаковые. В чём фокус ?


> Т.е. если мне нужно сравнивать на посимвольное равенство
> — то метод S1 = S2 вполне рабочий ?

а в чем сомнения? конечно

> И еще смутило то, что для CompareStr используется функция,
> написанная на ассемблере, но уже для AnsiCompareStr — функция
> WinAPI

а чего смущаться? CompareString — она как раз с учетом локали может сравнивать

Фокуса нет, здесь нужен еще и TRIM кроме регистронезависимого сравнения.

Вот для примера кусок кода:

const
PARAM_Username = «user_name»;
PARAM_Password = «password»;

function GetParamValue(Params: TStrings; const ParamName: string; Erase: boolean = false): string;
var
i: integer;
begin
result := «»;
for i := Params.Count -1 downto 0 do
// а можно написать и вот так
// if Params.Names[i] = ParamName
if 0 = AnsiCompareText(Params.Names[i], ParamName)
then begin
result := Params.ValueFromIndex[i];
if Erase
then Params.Delete(i);
break;
end;
end;

Понимаю теперь — что зря я втулил AnsiCompareText — достаточно было и простого сравнения :)

> достаточно было и простого сравнения

достаточно было и Params.IndexOfName )

а нафига Erase при этом — я не понимаю, функция должна возвращать значения, а удалять — отдельная процедура(если так хочется удалить из параметров)

> Просто прочитал статью
> http://www.delphikingdom.com/asp/viewitem.asp?catalog >> поразился множеству вариантов сравнения

Странно. «Compare» в тексте статьи встречается всего 1 раз :)

Это связано с тем, что функция AnsiPos использует функции StrPos и CompareString, предназначенные для работы со строками PChar .


> clickmaker © (08.07.09 12:48) [11]
> достаточно было и Params.IndexOfName )


> Игорь Шевченко © (08.07.09 20:54) [12]
> Result := Params.Values[ParamName]
>
> а нафига — я не понимаю, функция должна возвращать значения, а удалять — отдельная процедура(если так хочется удалить из параметров)

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

Erase при этом нафига — для того чтобы отделить обработанные параметры от необработанных. Например нужно просто узнать из параметров имя пользователя — для этого достаточно и Values[ParamName], но иногда имя пользователя нужно занести в одно поле — а остальные параметры в мемо.

AnsiCompareStr — Функция Delphi

AnsiCompareText сравнивает строки без учета регистра:

Ins, совсем забыл

03.01.2008, 19:08 #5
Функция AnsiCompareStr сравнивает String1 и String2 на равенство, игнорируя регистр.

Это современная Региональная безопасная форма CompareStr.

Все Ansi команды поддержкой многобайтовые символы.

Она возвращает следующие значения:

String1: отрицательное число
String1 = String2: 0
String1 > String2: положительное число

AnsiCompareStr Routine

Unit Edit

Description Edit

Definition (Delphi 6):

Definition (Delphi 2009):

Definition (Delphi 2010, XE):

Technical Comments Edit

(Known issues / Documentation clarifications / Things to be aware of)

Examples Edit

(Please provide links to articles/source code that show how to use this item.)

See Also Edit

(Please provide links to items specifically related to this item.)

User Comments/Tips Edit

(Please leave your name with your comment.)

Строковые типы в Delphi. Особенности реализации и использования

В этой статье будут освещены следующие вопросы:

  1. Какие строковые типы существуют в Delphi, и чем они отличаются друг от друга
  2. Преобразование строк из одного типа в другой
  3. Некоторые приемы использования строк типа AnsiString:
    1. Функции для работы со строками о которых многие часто забывают или вовсе не знают
    2. Передача строк в качестве параметров
    3. Использование строк в записях
    4. Запись в файл и чтение из файла
    5. Использование строк в качестве параметров и результатов функций размещенных в DLL.

Ну что, интересно? Тогда поехали.

Какие строковые типы существуют в Delphi, и чем они отличаются друг от друга?

В Delphi 1.0 существовал лишь единственный строковый тип String, полностью эквивалентный одноименному типу в Turbo Pascal и Borland Pascal. Однако, этот тип имеет существенные ограничения, о которых я расскажу позднее. Для обхода этих ограничений, в Delphi 2, разработчики из Borland устроили небольшую революцию. Теперь, начиная с Delphi 2, имеются три фундаментальных строковых типа: ShortString, AnsiString, и WideString. Кроме того, тип String теперь стал логическим. Т.е., в зависимости от настройки соответствующего режима компилятора (режим больших строк), он приравнивается либо к типу ShortString (для совместимости со старыми программами), либо к типу AnsiString (по умолчанию). Управлять режимом, можно используя директиву компиляции <$LONGSTRINGS ON/OFF>(короткая форма <$H+/->) или из окна настроек проекта – вкладка «Compiler» -> галочка «Huge strings». Если режим включен, то String приравнивается к AnsiString, иначе String приравнивается ShortString. Из этого правила есть исключение: если в определении типа String указан максимальный размер строки, например String[25], то, вне зависимости от режима компилятора, этот тип будет приравнен к ShortString соответствующего размера.

Поскольку, как вы узнаете в дальнейшем, типы ShortString и AnsiString имеют принципиальное отличие в реализации, то я вообще не рекомендую пользоваться логическим типом String без указания размера, если Вы, конечно, не пишете программ под Delphi 1. Если же Вы все-таки используете тип String, то я настоятельно рекомендую прямо в коде Вашего модуля указывать директиву компиляции, устанавливающую подразумеваемый Вами режим работы компилятора. Особенно если Вы используете особенности реализации соответствующего строкового типа. Если этого не сделать, то однажды, когда Ваш код попадет в руки другого программиста, не будет никакой гарантии того, что его компилятор будет настроен, так же как и Ваш.

Поскольку по умолчанию, после установки Delphi, режим больших строк включен, большинство молодых программистов даже и не подозревают, что String может представлять что-то отличное от AnsiString. Поэтому, дальше в этой статье, любое упоминание типа String без указания размера, подразумевает, что он равен типу AnsiString, если не будет явно указано иное. Т.е., считается что настройка компилятора соответствует настройке по умолчанию.

Сразу же упомяну о различии между типами AnsiString и WideString. Эти типы имеют практически одинаковую реализацию, и отличаются лишь тем, что WideString используется для представления строк в кодировке UNICODE использующей 16-ти битное представление каждого символа (WideChar). Эта кодировка используется в тех случаях когда необходима возможность одновременного присутствия в одной строке символов из двух и более языков (помимо английского). Например, строк содержащих одновременно символы английского, русского и европейских языков. За эту возможность приходится платить – размер памяти, занимаемый такими строками в два раза больше размера, занимаемого обычными строками. Использование WideString встречается не часто, поэтому, я буду в основном рассказывать о строках типа AnsiString. Но, поскольку они имеют одинаковую реализацию, почти все сказанное относительно AnsiString будет действительно и для WideString, естественно с учетом разницы в размере каждого символа.

Тоже самое касается и разницы между pChar и pWideChar.

Строковый тип AnsiString, обычно используется для представления строк в кодировке ANSI, или других (например OEM) в которых для кодирования одного символа используется один байт (8 бит). Такой способ кодирования называется single-byte character set, или SBCS. Но, очень многие не знают о существовании еще одного способа кодирования многоязычных строк (помимо UNICODE) используемого в системах Windows и Linux. Этот способ называется multibyte character sets, или MBCS. При этом способе, некоторые символы представляются одним байтом, а некоторые, двумя и более. В отличие от UNICODE, строки, закодированные таким способом, требуют меньше памяти для своего хранения, но требуют более сложной обработки. Так вот, строковый тип AnsiString может использоваться для хранения таких строк. Я не буду подробно останавливаться на этом способе кодирования, поскольку он применяется крайне редко. Лично я, ни разу не встречал программ использующих данный способ кодирования.

Знатоки Delphi вероятно мне сразу напомнят еще и о типах pChar (pWideChar) и array [. ] of Char. Однако, я считаю, что это не совсем строковые типы, но я расскажу и о них, поскольку они очень часто используются в сочетании со строковыми типами.

Итак, приведу основные характеристики строковых типов:

Тип Максимальный размер строки Размер переменной Объем памяти, требуемый для хранения строки
String[n] где 0 0, поэтому то Delphi и не освобождает память, занятую строкой.

Еще, этот счётчик используется и для разрешения проблем, связанных со следующей ситуацией:

Здесь, как мы уже знаем, после выполнения оператора s2 := s1, обе переменные указывают на один и тот же экземпляр строки ‘abc123’. Однако, что же произойдёт когда выполниться оператор s2[1] := ‘X’? Казалось бы, в единственном имеющимся в нашем распоряжении экземпляре строки первая буква будет заменена на ‘X’. И как следствие, обе строки станут равными ‘Xbc123’. s1 то за что «страдает»? Но, к счастью это не так. Здесь на помощь Delphi вновь приходит счетчик ссылок. Delphi, при выполнении этого оператора понимает, что строка на которую указывает s2 будет изменена, а это может повлиять на других. Поэтому, перед изменением строки, проверяется ее счётчик ссылок. Обнаружив, что на нее ссылается более одной строковой переменной, делается следующее: создается копия этой строки со счётчиком ссылок равным 1, и адрес этой копии, присваивается s2; У исходного экземпляра строки, счетчик ссылок уменьшается на 1 – ведь s2 на неё теперь не ссылается. И лишь после этого, происходит изменение первой буквы, теперь уже собственного экземпляра строки. Т.е., по окончанию выполнения этого оператора, в памяти будут находиться две строки: ‘abc123’ и ‘Xbc123’. Причем, s1 будет ссылаться на первую, а s2 на вторую.

При работе со строками определенными как константы, алгоритм работы несколько отличается. Приведу пример:

Казалось бы, при завершении работы процедуры, экземпляр строки ‘Вася’ должен быть уничтожен. Но в данном случае это не так. Ведь, при следующем входе в процедуру, для выполнения присваивания нужно будет вновь где-то взять строку ‘Вася’. Для этого, ещё при компиляции, Delphi размещает экземпляр строки ‘Вася’ в области констант программы, где её даже невозможно изменить, по крайней мере, простыми методами. Но как же при завершении процедуры определить что строка ‘Вася’ – константная строка, и ее нельзя уничтожать? Все очень просто. Для константных строк, счётчик ссылок устанавливается равным -1. Это значение, «выключает» нормальный алгоритм работы со «счётчиком ссылок». Он не увеличивается при присваивании, и не уменьшается при уничтожении переменной. Однако, при попытке изменения переменной (помните s2[1]:=’X’), значение счётчика равное -1 будет всегда считаться признаком того, что на строку ссылается более одной переменной (ведь он не равен 1). Поэтому, в такой ситуации всегда будет создаваться уникальный экземпляр строки, естественно, без декремента счётчика ссылок старой. Это защитит от изменений экземпляр строки-константы.

К сожалению, этот алгоритм срабатывает не всегда. Но об этом, мы поговорим позже, при рассмотрении вопросов преобразования строковых типов.

Где же Delphi хранит «счётчик ссылок»? Причем, для каждой строки свой! Естественно, вместе с самой строкой. Вот что представляет собой эта область памяти, хранящая экземпляр строки ‘abc’:

Байты с 1 по 4 Счётчик ссылок равный -1
Байты с 5 по 8 Длина строки равная 3
Байт 9 Символ ‘a’
Байт 10 Символ ‘b’
Байт 11 Символ ‘c’
Байт 12 Символ с кодом 0 (#0)

Для удобства работы с такой структурой, когда строковой переменной присваивается ссылка на эту строку, в переменную заносится адрес не начала этой структуры, а адрес её девятого байта. Т.е. адрес начала реальной строки (прямо как pChar). Для того, что бы приблизиться к реальной жизни, перепишем приведённую структуру:

Смещение Размер Значение Назначение
-8 4 -1 Счётчик ссылок
-4 4 3 Длина строки
1 ‘a’
1 1 ‘b’
2 1 ‘c’
3 1 #0

С полем по смещению -8, нам уже должно быть все ясно. Это значение, хранящееся в двойном слове (4 байта), тот самый счетчик, который позволяет оптимизировать хранение одинаковых строк. Значение этого счетчика имеет тип Integer, т.е. может быть отрицательным. На самом деле, используется лишь одно отрицательное значение – «-1», и положительные значения. 0 не используется.

Теперь, обратите внимание на поле, лежащее по смещению -4. Это, четырёхбайтовое значение длинны строки (почти как в ShortString). Думаю, Вы заметили, что размер памяти выделенной под эту строку не имеет избыточности. Т.е. компилятор выделяет под строку минимально необходимое число байт памяти. Это конечно хорошо, но, при попытке «нарастить» строку: s1 := s1 + ‘d’, компилятору, точнее библиотеке времени исполнения (RTL) придется перераспределить память. Ведь теперь строке требуется больше памяти, аж на целый байт. Для перераспределения памяти нужно знать текущий размер строки. Вероятно, именно для того, что бы не приходилось каждый раз сканировать строку, определяя её размер, разработчики Delphi и включили поле длины, строки в эту структуру. Длина строки, хранится как значение Integer, отсюда и ограничение на максимальный размер таких строк – 2 Гбайт. Надеюсь, мы не скоро упрёмся в это ограничение. Кстати, именно потому, что память под эти строки выделяется динамически, они и получили ещё одно свое название: динамические строки.

Осталось рассказать ещё о нескольких особенностях переменных AnsiString. Важнейшей особенностью значений этого типа является возможность приведения их к типу Pointer. Это впрочем, естественно, ведь в «душе» они и есть указатели, как бы они этого не скрывали. Например, если описаны переменные: s :AnsiString и p :Pointer. То выполнение оператора p := Pointer(s) приведет к тому, что переменная p станет указывать на экземпляр строки. Однако, при этом, очень важно знать: счетчик ссылок этой строки не будет увеличен. Но об этом, мы поговорим чуть позднее.

Поскольку, переменные этого типа реально являются указателями, то для них и реально такое значение как Nil – указатель в «никуда». Это значение в переменной типа AnsiString по смыслу приравнивается пустой строке. Более того, чтобы не тратить память и время на ведение счётчика ссылок, и поля размера строки всегда равного 0, при присваивании пустой строке переменной этого типа, реально, присваивается значение Nil. Это не очевидно, поскольку обычно не заметно, но как мы увидим позже, очень важная особенность.

Вот теперь, кажется, Вы знаете о строках все. Настала пора переходить к более интересной части статьи – как с этим всем жить?

Преобразование строк из одного типа в другой

Здесь, все как обычно, и просто и сложно.

Преобразование между «настоящими» строковыми типами String[n], ShortString, и AnsiString выполняются легко, и прозрачно. Никаких явных действий делать не надо, Delphi все сделает за Вас. Надо лишь понимать, что в маленькое большое не влезает. Например:

В результате выполнения этого кода, в переменной s3 окажется строка ‘abc’, а не ‘abcdef’. С преобразованием из pChar в String[n], ShortString, и AnsiString, тоже всё очень не плохо. Просто присваивайте, и все будет нормально.

Сложности начинаются тогда, когда мы начинаем преобразовывать «настоящие» строковые типы в pChar. Непосредственное присваивание переменным типа pChar значений строк не допускается компилятором. На оператор p := s где p имеет тип pChar, а s :AnsiString, компилятор выдаст сообщение: «Incompatible types: ‘String’ and ‘PChar'» — несовместимые типы ‘String’ и ‘PChar’. Чтобы избежать такой ошибки, надо применять явное приведение типа: p := pChar(s). Так рекомендуют разработчики Delphi. В общем, они правы. Но, если вспомнить, как хранятся динамические строки — с нулем в конце, как и pChar. А еще и то, что к AnsiString применимо преобразование в тип Pointer. Станет очевидным, что всего, возможно целых три способа преобразования строки в pChar:

Все они, синтаксически правильны. И кажется, что все три указателя (p1, p2 и p3) будут в результате иметь одно и то же значение. Но это не так. Всё зависит от того, что находится в s. Если быть более точным, равно ли значение s пустой строке, или нет:

Чтобы Вы понимали причину такого явления, я опишу, как Delphi выполняет каждое из этих преобразований. В начале напомню, что переменные AnsiString представляющие пустые строки, реально имеют значение Nil. Так вот:

Для выполнения преобразования pChar(s), компилятор генерит вызов специальной внутренней функции @LstrToPChar. Эта функция проверяет – если строковая переменная имеет значение Nil, то вместо него, она возвращает указатель на реально размещенную в памяти пустую строку. Т.е. pChar(s) никогда не вернет указатель равный Nil.

Тут все просто, такое преобразование просто возвращает содержимое строковой переменной. Т.е. если она при пустой строке содержит Nil, то и результатом преобразования будет Nil. Если же строка не пуста, то результатом будет адрес экземпляра строки.

Здесь, все совсем по другому. Перед выполнением такого преобразования, компилятор вставляет код, обеспечивающий ситуацию, когда указатель, хранящийся в s, будет единственным указателем на экземпляр строки в памяти. В нашем примере, если строка не пуста, то будет создана копия исходной строки. Вот ее-то адрес и будет возвращен как результат такого преобразования. Но, если строка пуста, то результатом будет Nil, как и во втором случае.

Теперь, интересно отметить, что если в приведенном примере, преобразование p3 := @(s[1]) выполнить первым, то при не пустой строке в s, все указатели (p1, p2, и p3), будут равны. И содержать они будут адрес «персонального» экземпляра строки.

Вот, теперь, зная как выполняются те или иные преобразования, Вы сможете всегда выбрать подходящий способ. Для тех, кто ещё не «проникся» до конца этой проблемой, приведу пример. В нем, преобразование, рекомендуемое разработчиками, приводит к «странному» поведению программы:

вызывает ошибку доступа к памяти при выполнении строки, помеченной 1. Вот и приходится применять эту функцию.

Здесь, поскольку параметр передаётся по значению, при вызове будет создана локальная копия переменной (указателя) Msg. Об этом я ещё расскажу ниже. Эта переменная, при завершении процедуры будет уничтожаться, что приведёт к освобождению «персональной» копии экземпляра переданной и преобразованной строки.

Функции этой группы используются для сравнения строк. Результатом будет одно из значений:

>0 если S1 > S2
255 символов приходится платить.

Если же тебе достаточно и 255 символов, то используй ShortString, или String[n].

Есть еще одно, на мой взгляд, замечание. Если Вы обратили внимание на тип переменной Len в моем примере, то возможно у Вас возник вопрос: А почему LongInt, а не Integer? Жаль если у Вас вопрос не возник – либо вы все знаете, либо ничего :). Для остальных поясню: дело в том, что тип LongInt фундаментальный тип, размер которого (4 байта) не будет меняться в последующих версиях Delphi. А тип Integer, это универсальный тип, размерность которого может меняться от версии к версии. Например, для 64-разрядных компьютеров он наверняка «вырастет» до 8-ми байт (64 бита). Лично мне, хочется, что бы файлы данных записанные моей старой версией программы могли быть нормально прочитаны более поздними версиями, возможно скомпилированными уже под 64-разрядной OS.

Использование строк в записях

Проблема возникает тогда, когда одно поле (или несколько полей) имеют тип динамической строки. В этом случае, часто возникает проблема схожая с проблемой записи динамической строки в файл – не все данные лежат в записи, динамические строки представлены в ней лишь указателями. Решается проблема также как и с записью строк в файл. Жаль только что тогда нельзя будет оперировать (записать/прочитать) целиком всей записью. Строки придется обрабатывать особо. Можно сделать, например, так:

Обратите внимание на то, что перед записью в поток я делаю так, что бы в поле f3 попал указатель Nil. Если этого не сделать, то в поток попадет адрес текущего экземпляра динамической строки. При чтении, он будет прочитан в поле f3. Т.е. поле f3 станет указывать на какое-то место в памяти. При выполнении SetLength, поскольку Delphi сочтет что текущее значение f3 лежит по указанному адресу, будет попытка интерпретировать лежащую там информацию как динамическую строку. Если же в поток записать Nil, то SetLength, никуда лезть не будет – экземпляра-то нет.

Использование строк в качестве параметров и результатов функций размещенных в DLL.

Многие, наверное, пытались хоть раз написать собственную Dll. Если при этом Вы использовали соответствующего мастера Delphi, то наверняка видели комментарий который он вставляет в начало файла библиотеки:

Общий смысл этого эпоса в том, что если Ваша Dll экспортирует хотя бы одну процедуру или функцию с типом параметра соответствующим любой динамической строке (AnsiString например), или функцию, возвращающую результат такого типа. Вы должны обязательно и в Dll, и в использующей ее программе, первым модулем в списке импорта (uses) указать модуль ShareMem. И как следствие, поставлять со своей программой и Dll еще одну стандартную библиотеку BORLNDMM.DLL.

Вы не задумывались над вопросами: «Зачем все эти сложности?»; «Что будет если этого не сделать?» и «Можно ли этого избежать?»; «Если да, то как?» Если не задумывались, то самое время сделать это.

Попробуем разобраться что будет происходить с экземплярами динамических строк в следующем примере:

Сначала, при выполнении процедуры X, функция IntToStr(100) создаст экземпляр динамической строки ‘100’, и ее адрес будет помещен в переменную Str. Затем, адрес этой переменной будет передан процедуре Y. В ней, при выполнении оператора s := s+’$’, будет создан экземпляр новый строки ‘100$’, Экземпляр старой строки ‘100’ станет не нужным и память, выделенная для него при создании, будет освобождена. Кроме того, при завершении процедуры X, будет освобождена и память, выделенная для строки ‘100$’, так как перестанет существовать единственная ссылка на нее — переменная Str.

Всё вроде бы хорошо. Но до тех пор, пока обе процедуры располагаются в одном исполняемом модуле (EXE-файле). Если например поместить процедуру Y в Dll, а процедуру X оставить в EXE, то будет беда.

Дело в том, что выделением и освобождением памяти для экземпляров динамических строк занимается внутренний менеджер памяти Delphi-приложения. Использовать стандартный менеджер Windows очень накладно. Он слишком универсален, и потому медленный, а строки очень часто требуют перераспределения памяти. Вот разработчики Delphi и создали свой. Он ведет списки распределенной и свободной памяти своего приложения. Так вот, вся беда в том, что Dll будет использоваться свой менеджер памяти, а EXE свой. Друг о друге они ничего не знают. Поэтому, попытка освобождения блока памяти выделенного не своим менеджером приведёт к серьезному нарушению в его работе. Причем, это нарушение может проявиться далеко не сразу, и довольно необычным образом.

В нашем случае, память под строку ‘100’ будет выделена менеджером EXE-файла, а освобождаться она будет менеджером DLL. То же произойдет и с памятью под строку ‘100$’, только наоборот.

Для преодоления этой проблемы, разработчики Delphi создали библиотеку BORLNDMM.DLL. Она включает в себя еще один менеджер памяти :). Использование же модуля ShareMem, приводит к тому, что он заменяет встроенный в EXE (DLL) менеджер памяти на менеджер расположенный в BORLNDMM.DLL. Т.е., теперь и EXE-файл и DLL, будут использовать один, общий менеджер памяти.

Здесь важно отметить то, что если какой-либо из программных модулей (EXE или DLL) не будут иметь в списке импорта модуля ShareMem, то вся работа пойдет насмарку. Опять будут работать несколько менеджеров памяти. Опять будет бардак.

Можно обойтись и без внешнего менеджера памяти (BORLNDMM.DLL). Но для этого, надо например заменить встроенный в DLL менеджер памяти, на менеджер, встроенный в EXE. Такое возможно. Есть даже соответствующая реализация от Emil M. Santos, называемая FastShareMem. Найти ее можно на сайте http://www.codexterity.com. Она тоже требует обязательного указания ее модуля FastShareMem в списках используемых модулей EXE и DLL. Но, она по крайней мере не требует таскать за собой ни каких дополнительных DLL’лек.

Ну вот, наконец-то и все. Теперь, Вы знаете о строках почти столько же как я :).

Конечно, этим тема не исчерпывается. Например, я ничего не рассказал о мультибайтовых строках (MBCS) используемых для мультиязыковых приложений. Может и еще что-то забыл рассказать. Но, не расстраивайтесь. Я свои знания получал, изучая книги, тексты своих и чужих программ, код сгенерированный компилятором, и т.п. Т.е., все из открытых источников. Значит это все доступно и Вам. Главное, чтобы Вы были любознательными, и почаще задавали себе вопросы «Как?», «Почему?», «Зачем?». Тогда во всем сможете разобраться и сами.

Разница между CompareStr и ‘=’ для строк в Delphi

Я просто хочу узнать разницу между CompareStr и = для сравнения строк в Delphi. Оба дают тот же результат.

Оба показывают сообщение Palindrome.

Используйте CompareStr , если вы просто хотите увидеть, равны ли две строки, но когда вы хотите знать, как одна строка сравнивается относительно другой. Он вернет значение меньше 0, если первый аргумент появится первым, асипически, и он вернет значение, большее нуля, если первый аргумент принадлежит после второго.

Без CompareStr у вас может быть такой код:

Это сравнивает str1 и str2 дважды. С помощью CompareStr вы можете вырезать одно из сравнений строк и заменить его более дешевым целым сравнением:

Как объясняет Джерри, эта функция особенно полезна в сортировке, тем более что она имеет тот же интерфейс, что и другие функции сравнения, такие как CompareText и AnsiCompareStr , Функция сортировки является метод шаблона, и каждая из функций служит в качестве сравнения strategy.

Если все, что вы хотите сделать, это проверка равенства, используйте оператор = — его легче читать. Используйте CompareStr , когда вам нужны дополнительные функции, которые он предоставляет.

Предполагая, что Str1 и Str2 являются строками, а не массивами (или списками) или строкой, первая версия будет более эффективной, так как вторая версия сначала скопирует str1[i] и str2[i] в две новые строки, затем вызовет с соответствующими служебными данными.

Первая версия просто сравнит одиночные символы, на которые ссылаются str1 [i] и str2 [i]

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

CompareStr особенно полезен при сортировке списков, например, с помощью TList.Sort(CompareFunc) или TStringList.Sort(CompareFunc)

Если вы хотите нечувствительность к регистру, используйте CompareText.

AnsiCompareStr — Функция Delphi

Полную версию библиотеки KOL и MCK можно скачать здесь.

function AnsiEq( const S1, S2: String ): Boolean;
Возвращает true, если AnsiLowerCase(S1) = AnsiLowerCase(S2) т.е сравнивает 2 строки без учета регистра. Данная функция подойдет только для русских символов. function DelimiterLast( const Str, Delimiters: String ): Integer;
Функция ищет в строке Str последнее вхождение строки Delimiters и возвращает номер символа с которого он идет. Если Delimiters не найден возвращается длина Str.

function AnsiUpperCase( const S: string ): string;
Преобразует символы в строке S к верхнему регистру.

function AnsiLowerCase( const S: string ): string;
Преобразует символы в строке S к нижнему регистру.

function AnsiCompareStr( const S1, S2: string ): Integer;
Сравнивает две строки ANSI S1 и S2 без учета регистра. Возвращает значение 0, если S1 > S2.

function _AnsiCompareStr( S1, S2: PChar ): Integer;
Аналог AnsiCompareStr только для PChar.

function CopyEnd( const S: String; Idx: Integer ): String;
Копирует строку из строки S с Idx символа до последнего

function CopyTail( const S: String; Len: Integer ): String;
Возвращает последние Len символа из строки S. Если Len > Length( S ), возвращается вся строка S.

procedure DeleteTail( var S: String; Len: Integer );
Удаляет последние Len символа из строки.

function LowerCase( const S: string ): string;
Преобразует символы в строке S к нижнему регистру. Только для латинских символов.

procedure NormalizeUnixText( var S: String );
В строке S происходит замена всех символов #10 на символ #13

function IndexOfChar( const S: String; Chr: Char ): Integer;
Возвращает положение символа Chr в строке S. Возвращаемое значение может быть то 1 до Length(S), если возвращается -1 то символ не найден.

function IndexOfCharsMin( const S, Chars: String ): Integer;
Возвращает положение ближайшего от начала(строки) символа Chr в строке S. Если возвращается -1 то символ не найден.

function IndexOfStr( const S, Sub: String ): Integer;
Возвращает положение строки Sub в строке S. Возвращаемое значение может быть то 1 до Length(S)-Length(Sub), если возвращается -1 то строка не найдена.

function Parse( var S: String; const Separators: String ): String;
Функция ищет в строке S строку Separators и возвращает все что шло до этого разделителя (т.е. до Separators). В строке S удаляется возвращаемая часть и найденный Separators.
Т.е. если S= ‘1-2-3’,а Separators= ‘-‘ то при первом вызове на выходе ‘1’ , а S=’2-3′
Если в S нет Separators то возвращаемое значение рано S, а сама S становится пуcтой.

function StrEq( const S1, S2: String ): Boolean;
Возвращает true, если LowerCase(S1) = LowerCase(S2) т.е сравнивает 2 строки без учета регистра. Данная функция подойдет только для латинских символов.

function StrIn( const S: String; A: array of String ): Boolean;
Функция возвращает True если в одна из строк массива А равна строке S. Спавнение идет с помощью функции StrEq поэтому оно не чуствительно к регистру букв.

function StrSatisfy( const S, Mask: String ): Boolean;
Возвращает True если строка S соответствует маске Mask. Строка Mask может содержать символ ‘*’ (любое количество любых символов) и ‘?’ (один любой символ). Например чтобы проверить содержит ли S адрес почты надо :
StrSatisfy(S,’*@*.*’);

function StrReplace( var S: String; const From, ReplTo: String ): Boolean;
Производит замену в строке S первой встреченной строки From на строку ReplTo. Если замена произошла успешно возвращает True.

function StrPCopy( Dest: PChar; const Source: string ): PChar;
Копирует паскалевскую строку в строку типа PChar

function StrLCopy( Dest: PChar; const Source: PChar; MaxLen: Cardinal ): PChar;
Копирует первые MaxLen символов из паскалевсокой строки в строку типа PChar.

function StrComp( const Str1, Str2: PChar ): Integer;
Быстрое сравнение 2-х строк. На выходе -1: Str1 Str2

function StrLComp( const Str1, Str2: PChar; MaxLen: Cardinal ): Integer;
Сравнивает до MaxLen символов двух строк Str1 и Str2 с учетом регистра. Возвращает значение 0, если Str1 > Str2.

function StrCopy( Dest, Source: PChar ): PChar;
Функция производит быстрое копирование строки Source в Dest и возвращает Dest.

function StrLen( const Str: PChar ): Cardinal;
Функция возвращает число символов в строке Str, не учитывая конечного нулевого символа.

function StrScanLen( Str: PChar; Chr: Char; Len: Integer ): PChar;
Быстрое сканирование строки Str длиной Len на нахождение в нем первого символа Chr . Если Chr не входит в Str, возвращается nil.

function StrScan( Str: PChar; Chr: Char ): PChar;
Полный аналог предыдущей функции только сканирование происходит всей строки Str до последнего символа.

function StrRScan( const Str: PChar; Chr: Char ): PChar;
Возвращает указатель на последнее вхождение символа Chr в строку Str. Если Chr не входит в Str, возвращается nil.

function StrIsStartingFrom( Str, Pattern: PChar ): Boolean;
Возвращает True если строка Str начинается со строки Pattern, то есть если Copy( Str, 1, StrLen( Pattern ) ) = Pattern

procedure Str2LowerCase( S: PChar );
Переводит символы в нуль-терминальной строке в верхней регистр.

function TrimLeft( const S: string ): string;
Удаляет из начала строки S пробелы и управляющие символы

function TrimRight( const S: string ): string;
Удаляет в конца строки S пробелы и управляющие символы.

function Trim( const S: string ): string;
Удаляет в конца и в начале строки S пробелы и управляющие символы.

function UpperCase( const S: string ): string;
Преобразует символы в строке S к верхнему регистру. Только для латинских символов.

function WStrReplace( var S: WideString; const From, ReplTo: WideString ): Boolean;
Производит замену в строке S первой встреченной строки From на строку ReplTo. Если замена произошла успешно возвращает True. Анналог функции StrReplace только для WideString (поэтому в Delphi 2 не будет)

function WStrLen( W: PWideChar ): Integer;
Возвращает длину строки типа PWideChar.

procedure WStrCopy( Dest, Src: PWideChar );
Копирует строку Dest тип PWideChar в Src

function WStrCmp( W1, W2: PWideChar ): Integer;
Сравнивает 2-е строки тип PWideChar. На выходе тоже что и у StrComp

function Clipboard2Text: String;
Возвращает из буфера обмена текст.

function Text2Clipboard( const S: String ): Boolean;
Помещает в буфер обмена текст

Илон Маск рекомендует:  Посещённая ссылка
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL