Класс TStringList
ГЛАВА 7. Списки и коллекции
🕛 14.11.2006, 17:00
Класс 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 := Editl.Text;
или так:
SomeStrings := 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 := 'Санкт-Петербург';
SomeStrings.Objects := SPb; (Связывание объекта и строки}
Можно поступить и подобным образом (помните, что строка уже должна существовать):
...
SomeStrings.Strings := 'Санкт-Петербург';
SomeStrings.Objects := TCityProps.Create;
(SomeStrings.Objects 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.Destroy;
...
Если при удалении связанного объекта необходимо выполнить некоторые действия, предусмотренные в деструкторе, приведение типов
TCityProps(SomeList.Objects).Destroy;
выполнять не обязательно - нужный деструктор будет вызван автоматически, хотя в данном случае приведение типов ошибкой не является.
Метод
procedure Clear; override;
полностью очищает список, удаляя все его элементы.
Помимо перечисленных, класс TStringList обладает рядом дополнительных свойств и методов. Вспомогательные свойства класса обеспечивают разработчика информацией о состоянии списка. Дополнительные методы осуществляют поиск в списке и взаимодействие с файлами и потоками.
Свойство только для чтения
property Count: Integer;
возвращает число элементов списка.
Так как основу списка составляет динамический массив, то для него в процессе работы должна выделяться память. При добавлении в список новой строки память для нее выделяется автоматически. Свойство
property Capacity: Integer;
определяет число строк, для которых выделена память. Вы можете самостоятельно управлять этим параметром, помня при этом, что значение Capacity всегда должно быть больше или равно значению Count.
Свойство
property Duplicates: TDuplicates;
определяет, можно ли добавлять в список повторные значения.
Тип
type
TDuplicates = (duplgnore, dupAccept, dupError);
определяет реакцию списка на добавление повторного элемента: dupignore - запрещает добавление повторных элементов; dupAccept - разрешает добавление повторных элементов; dupError - запрещает добавление повторных элементов и генерирует исключительную ситуацию.
Класс TStringList немыслимо представить себе без возможностей сортировки. Если вас удовлетворит обычная сортировка, то для этого можно использовать свойство sorted (сортировка выполняется при значении True) или метод Sort. Под "обычной" имеется в виду сортировка по тексту строк с использованием функции Ansicomparestr (т. е. с учетом национальных символов, в порядке возрастания). Если вы хотите отсортировать список по другому критерию, к вашим услугам метод:
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.