Класс TStringList
Класс TStringList обеспечивает реальное использование списков строк в приложении. По существу, класс представляет собой оболочку вокруг динамического массива значений списка, представленного свойством strings. Объявление свойства (унаследованное от TStrings) выглядит так:
property Strings[Index: Integer]: string read Get write Put; default;
Для работы со свойством используются внутренние методы Get и Put, в которых применяется внутренняя переменная FList:
type
PStringltem = 'TStringltem;
TStringltem = record FString: string;
FObject: TObject;
end;
PStringltemList = ^TStringItemList;
TStringltemList = array[0..MaxListSize] of TStringltem;
FList: PStringltemList;
Из ее объявления видно, что список строк представляет собой динамический массив записей TStringItem. Эта запись позволяет объединить саму строку и связанный с ней объект.
Максимальный размер списка ограничен константой
MaxListSize = Maxint div 16;
значение которой после нехитрых вычислений составит 134 217 727. Таким образом, видно, что строковый список Delphi теоретически конечен, хотя на практике гораздо чаще размер списка ограничивается размером доступной памяти.
Обращение к отдельному элементу списка может осуществляться через свойство strings таким образом:
SomeStrings.Strings[i] := Editl.Text;
или так:
SomeStrings[i] := Editl.Text;
Оба способа равноценны.
При помощи простого присваивания можно задавать новые значения только тогда, когда элемент уже создан. Для добавления нового элемента используются методы Add И AddStrings.
Функция
function Add(const S: string): Integer;
добавляет в конец списка новый элемент, присваивая ему значение s и возвращая индекс нового элемента в списке.
Метод
procedure Append(const S: string);
просто вызывает функцию Add. Единственное отличие заключается в том, что метод не возвращает индекс нового элемента.
Метод
procedure AddStrings(Strings: TStrings);
добавляет к списку целый набор новых элементов, которые должны быть заданы другим списком, передаваемым в параметре strings.
При необходимости можно добавить новый элемент в произвольное место списка. Для этого применяется метод
procedure Insert(Index: Integer; const S: string);
который вставляет элемент s на место элемента с индексом index. При этом все указанные элементы смещаются на одну позицию вниз.
Для удаления элемента списка используется метод
procedure Delete(Index: Integer);
Метод
procedure Move(Curlndex, Newlndex: Integer);
перемещает элемент, заданный индексом curindex, на новую позицию, заданную индексом Newlndex.
А метод
procedure Exchange(Indexl, Index2: Integer);
меняет местами элементы с индексами index1 и index2.
Довольно часто в списках размешается строковая информация следующего вида:
'Name=Value'
В качестве примера можно привести строки из файлов INI или системного реестра. Специально для таких случаев в списке предусмотрено представление строк в двух свойствах. В свойстве Names содержится текст до знака равенства. В свойстве values содержится текст после знака равенства по умолчанию. Однако символ-разделитель можно заменить на любой другой, использовав свойство
property NameValueSeparator: Char;
Доступ к значениям свойства values осуществляется по значению. Например, если в списке есть строка
City=Saint-Petersburg
то значение свойства value будет равно
Value['City'] = 'Saint-Petersburg'
Кроме этого, значение свойства value можно получить, если известен его индекс:
property ValueFormlndex[Index: Integer]: string;
Как видно из объявления внутреннего списка FList (см. выше), с каждым элементом списка можно связать любой объект. Для этого используется свойство
property Objects[Index: Integer]: TObject;
Свойство strings элемента и свойство objects связанного с ним объекта имеют одинаковые индексы. Если строка не имеет связанного объекта, то свойство objects равно Nil. Один объект может быть связан с несколькими строками списка одновременно.
Чаще всего объекты нужны для того, чтобы хранить для каждого элемента дополнительную информацию. Например, в списке городов для каждого элемента можно дополнительно хранить население, площадь, административный статус и т. д. Для этого можно создать примерно такой класс:
TCityProps = class(TObject)
Square: Longlnt;
Population: Longlnt;
Status: String/end;
Для того чтобы добавить к строке из списка объект, используется метод AddObject:
function AddObject(const S: string; AObject: TObject): Integer; virtual;
Обратите внимание, что в параметре AObject необходимо передавать указатель на объект. Проще всего это сделать таким образом:
SomeStrings.AddObject('Someltem', TCityProps.Create);
Или же так:
var SPb: TCityProps;
...
SPb := TCityProps.Create; {Создание объекта}
SPb.Population := 5000000;
...
SomeStrings.Strings[i] := 'Санкт-Петербург';
SomeStrings.Objects[i] := SPb; (Связывание объекта и строки}
Можно поступить и подобным образом (помните, что строка уже должна существовать):
...
SomeStrings.Strings[i] := 'Санкт-Петербург';
SomeStrings.Objects[i] := TCityProps.Create;
(SomeStrings.Objects[i] as TCityProps).Population := 5000000;
...
Аналогично методу insert, элемент и связанный с ним объект можно вставить в произвольное место списка методом
procedure InsertObject(Index: Integer; const S: string; AObject: TObject);
При перемещении методом Move вместе с элементом переносится и указатель на связанный объект.
Обратите внимание на две особенности, связанные с удалением указателей на объекты и самих связанных объектов.
При удалении элемента списка удаляется только указатель на объект, а сам объект остается в памяти. Для его уничтожения следует предпринять дополнительные усилия:
...
for i := 0 to SomeList.Count — 1 do
SomeList.Objects[i].Destroy;
...
Если при удалении связанного объекта необходимо выполнить некоторые действия, предусмотренные в деструкторе, приведение типов
TCityProps(SomeList.Objects[i]).Destroy;
выполнять не обязательно — нужный деструктор будет вызван автоматически, хотя в данном случае приведение типов ошибкой не является.
Метод
procedure Clear; override;
полностью очищает список, удаляя все его элементы.
Помимо перечисленных, класс TStringList обладает рядом дополнительных свойств и методов. Вспомогательные свойства класса обеспечивают разработчика информацией о состоянии списка. Дополнительные методы осуществляют поиск в списке и взаимодействие с файлами и потоками.
Свойство только для чтения
property Count: Integer;
возвращает число элементов списка.
Так как основу списка составляет динамический массив, то для него в процессе работы должна выделяться память. При добавлении в список новой строки память для нее выделяется автоматически. Свойство
property Capacity: Integer;
определяет число строк, для которых выделена память. Вы можете самостоятельно управлять этим параметром, помня при этом, что значение Capacity всегда должно быть больше или равно значению Count.
Свойство
property Duplicates: TDuplicates;
определяет, можно ли добавлять в список повторные значения.
Тип
type
TDuplicates = (duplgnore, dupAccept, dupError);
определяет реакцию списка на добавление повторного элемента:
- dupignore —- запрещает добавление повторных элементов;
- dupAccept — разрешает добавление повторных элементов;
- dupError — запрещает добавление повторных элементов и генерирует исключительную ситуацию.
type
TStringListSortCompare = function(List: TStringList; Indexl, Index2: Integer): Integer;
procedure CustomSort(Compare: TStringListSortCompare);
Чтобы отсортировать список, вы должны описать функцию сравнения двух элементов с индексами indexl и index2, которая должна возвращать следующие результаты:
- 1 — если элемент с индексом indexl вы хотите поместить впереди элемента Index2;
- 0 — если они равны;
- 1 — если элемент с индексом indexl вы хотите поместить после элемента Index2.
function SortByStatus(List: TStringList; Indexl, Index2: Integer):
Integer;
begin
Result := AnsiCompareStr((List.Objects[Indexl] as TCityProps).Status,
(List.Objects[Index2] as TCityProps).Status;
end;
function SortBySquare(List: TStringList; Indexl, Index2: Integer): Integer;
begin if (List.Objects[Indexl] as TCityProps).Square <
(List.Objects[Index2] as TCityProps). Square) then Result := -1
else if (List.Objects[Indexl] as TCityProps).Square =
(List.Objects[Index2] as TCityProps).Square then Result := 0
else Result := 1;
end;
function SortByPopulation(List: TStringList; Indexl, Ir.dex2: Integer): Integer;
begin
if (List.Objects[Indexl] as TCityProps).Population < (List.Objects[Index2] as TCityProps). Population then Result := -1
else
if (List.Objects[Indexl] as TCityProps). Population = (List.Objects[Index2] as TCityProps). Population
then Result := 0
else Result := 1;
end;
Передаем одну из процедур в метод CustomSort:
Cities.CustomSort(SortByPopulation);
Для поиска нужного элемента используется метод
function Find(const S: string; var Index: Integer): Boolean;
В параметре s передается значение для поиска. В случае успеха функция возвращает значение True, а в параметре index содержится индекс найденного элемента.
Метод
function IndexOf (const S: string): Integer;
возвращает индекс найденного элемента s. Иначе функция возвращает — 1.
Метод
function IndexOfName(const Name: string): Integer;
возвращает индекс найденного элемента, для которого свойство Names совпадает со значением параметра Name.
Для поиска связанных объектов используется метод
function IndexOfObject(AObject: TObject): Integer;
В качестве параметра AObject должна передаваться ссылка на искомый объект. А свойство
property CaseSensitive: Boolean;
включает или отключает режим поиска и сортировки с учетом регистра символов.
Помимо свойства strings, содержимое списка можно получить при помощи свойств
property Text: string;
И
property CommaText: string;
Они представляют все строки списка в виде одной строки. При этом в первом свойстве элементы списка разделены символами возврата каретки и переноса строки. Во втором свойстве строки заключены в двойные кавычки и разделены запятыми или пробелами. Так, для списка городов (Москва, Петербург, Одесса) свойство Text будет равно
Москва#$0#$АПетербург#$0#$АОдесса
а свойство CommaText равно
"Москва", "Петербург", "Одесса".
Важно иметь в виду, что эти свойства доступны не только по чтению, но и по записи. Так что заполнить список вы сможете не только циклически, вызывая и используя методы Add или insert, но и одним-единственным присвоением значения свойствам Text или CommaText.
Список может взаимодействовать с другими экземплярами класса TstringList.
Широко распространенный метод
procedure Assign(Source: TPersistent);
полностью переносит список source в данный.
Метод
function Equals(Strings: TStrings): Boolean;
возвращает значение True, если элементы списка strings полностью совпадают с элементами данного списка.
Список можно загрузить из файла или потока. Для этого используются методы
procedure LoadFromFile(const FileName: string);
И
procedure LoadFromStream(Stream: TStream);
Сохранение списка выполняется методами
procedure SaveToFile(const FileName: string);
И
procedure SaveToStreamfStream: TStream);
Перед изменением списка вы можете получить управление, описав обработчик события
property OnChange: TNotifyEvent;
а после изменения
property OnChanging: TNotifyEvent;
На дискете, прилагаемой к этой книге, вы можете ознакомиться с примером использования списков строк DemoStrings.