Воскресенье, 05.05.2024
Королевство Delphi
Главное меню
Статьи
Наш опрос
Нравится раздел статьи?
Всего ответов: 68
Статистика
Онлайн всего: 1
Гостей: 1
Пользователей: 0
Форма входа
Главная » Статьи » Разные » Советы Дельферу

Чудеса TStringList. Почему надо использовать объекты TStringList везде

Object Pascal в сочетании с ассемблером в современной его форме Delphi 5/6/7 предоставляет неограниченные возможности для полета мысли программиста, и этой статьей мы откроем серию, в которой последовательно будем это демонстрировать.

Начнем мы с простого, крайне полезного, но мало известного, по крайней мере, для начинающих, стандартного класса Дельфи TStringList. Сейчас мы посмотрим, как красиво решаются типичные задачи при помощи этого класса.

Для начала в двух словах, какие такие замечательные свойства есть у объектов данного класса. TStringList - это класс, предназначенный для хранения списка строк и списка объектов с текстовым представлением (прямо как в 1С - это СписокЗначений). Кроме того, этот список может быть отсортирован по алфавиту или при помощи сравнительной функции, написанной программистом. Кроме того, этот список может быть интерпретирован как список значений (Name=string). Кроме того, этот список может быть сохранен в файл или поток, преобразован в непрерывную строку или строку, разделенную запятыми. Физически получаемая строка или поток представляет собой обычный текст, в котором строки разделены символами CR/LF (стандартный Windows Text File), или запятыми (CommaText, Excel). Ну и само собой, есть возможность загружать список строк из файла, потока, строки и строки, разделенной запятыми. Следует особо отметить замечательное свойство упаковки в строку, разделенную запятыми: при обратной распаковке строки всегда восстанавливаются в их исходном виде. Это означает, что допустима многократная вложенность строк, разделенных запятыми, дающая огромный выигрыш при упаковке/распаковке многомерных структурных данных в текстовый формат, что мы и продемонстрируем во второй задаче.

Итак, для начала рассмотрим список строк как список объектов с текстовым представлением, т. к. именно в данном ключе следует использовать список строк в реальных приложениях. Что из себя представляет объект с текстовым представлением? Это может быть, например, список товаров, имеющих помимо наименования еще и дополнительные параметры типа единицы измерения, количества в упаковке и цены. Итак, имеем предопределение типов:

type 
 TEdIzm=class
 public
 Name:string;
 Weight:double;
 end;
 TTovar=class
 public
 Name:string; //наименование
 EdIzm:TEdIzm; //ед. изм.
 CountUp:integer; //кол-во в упаковке
 PriceOut:currency; //цена продажная
 end;
var
 TovarList:TStringList;

Набор объектов TTovar - это классический справочник однородных товаров, например, хлебобулочных изделий. Поле Weight в классе TEdIzm требуется для перевода одних единиц в другие. Вернемся к нашим булкам. Допустим, поставщик "Карякинский Хлебозавод" предоставил нам текстовый файл, в котором находится информация о его новой продукции и отпускных ценах в формате текстового файла:

*Начало файла*
Товар1=[наименование товара]
ЕдИзм1=[наименование ед.изм.]
Вес1=[вес единицы измерения]
КолУп1=[количество в упаковке]
Цена1=[цена поставщика]
--
Товар2=[наименование товара]
ЕдИзм2=[наименование ед.изм.]
Вес2=[вес единицы измерения]
КолУп2=[количество в упаковке]
Цена2=[цена поставщика]
--
*Конец файла*

Всего в файле содержится, например, 2000 наименований. Наша задача - загрузить данные этого файла в список строк ListBox1:TListBox (лежащий на форме) в отсортированном виде так, чтобы была возможность по двойному щелчку просмотреть параметры каждого товара (для этого к каждой строке будет прикреплен объект типа TTovar).

Если решать эту задачу в лоб (как чтение построчно и разбор текстового файла), а так же напрямую добавлять в ListBox, то это обернется неэффективной работой компьютера, его подтормаживанием (на слабых машинах), и вообще, для дальнейших внесений изменений в программный код это решение не является лучшим. Гораздо эффективнее сделать "финт ушами", а именно, создать TStringList, загрузить в него исходный файл, создать второй TStringList, загрузить в него товары, отсортировать их, и в конце концов, присвоить свойству ListBox.Items:

function LoadTovary(filename:string):TStrings; 
var str:TStringList;
 tovar:TTovar;
 edizm:TEdIzm;
 I:integer;
begin
 //загружаем файл с данными
 str:=TStringList.Create;
 str.LoadFromFile(filename);
 //посчитаем, сколько будет товаров
 result:=TStringList.Create; // TStrings являедся предком TStringList, поэтому данное присвоение корректно
 result.Capacity:=str.count div 6;
 for I:=0 to result.capacity do
 begin
 tovar:=TTovar.Create;
 edizm:=TEdIzm.Create;
 tovar.Name:=str.Values['Товар'+inttostr(i)];
 edizm.Name:=str.Values['ЕдИзм'+inttostr(i)];
 edizm.Weight:=strtofloat(str.Values['Вес'+inttostr(i)]);
 tovar.CountUp:=strtoint(str.Values['КолУп'+inttostr(i)]);
 tovar.PriceOut:=strtoint(str.Values['Цена'+inttostr(i)]);
 tovar.EdIzm:=edizm;
 result.AddObject(tovar.Name,tovar);
 end;
 result.Sort;
end;

...
ListBox1.Items:=LoadTovary('fromhlzd.txt');
...

Заметим, что если написать функцию типа TStringListSortCompare, то можно будет сортировать не только по текстовому представлению, но и по любым другим признакам, например, цене:

function StringListComparePrice(List: TStringList; Index1, Index2: Integer): Integer;
begin
 Result := TTovar(List.Objects[Index1]).PriceOut-TTovar(List.Objects[Index2]).PriceOut;
end;

...
result.CustomSort(StringListComparePrice);
...

Ну и в конце концов, продемонстрируем реакцию на двойное нажатие на получившемся списке товаров. По двойному щелчку мы покажем отпускную цену товара:

procedure TForm1.ListBox1DblClick(Sender: TObject);
begin
 showmessage('Цена поставщика '+
floattostr(TTovar(TListBox(sender).Items.Objects[TListBox(sender).ItemIndex]).PriceOut)
 );
end;

Кстати, не забываем освобождать системные ресурсы объектов перед удалением строк методами ListBox1.Items.Delete()/Clear() или str.Delete()/Clear() (str:TStringList), если это требуется, а так же при закрытии приложения:

procedure TForm1.FormDestroy(Sender: TObject);
var i:integer;
begin
 for i:=0 to ListBox1.Items.Count-1 do
 with TTovar(ListBox1.Items.Objects[i]) do
 begin
 EdIzm.Free;
 Free;
 end;
end;

Теперь немного усложним задачу. Пусть у нас есть не один файл, а десять - от десяти разных поставщиков. Нам надо в общей сложности загрузить 20000 наименований и затем отсортировать их. Если мы будем именно так и делать, то сортировка такого большого списка объектов займет значительное время, это и составляет задачу. Однако такая задача решается очень просто - достаточно после создания объекта TStringList сразу присвоить его свойству Sorted значение true. После этого вставка новых строк будет осуществляться с помощью быстрого алгоритма, однако потеряется возможность сортировать по параметрам прикрепленных объектов, т.к. в случае Sorted=true сортировка производится автоматически только по текстовому представлению (см. исходники TStringList). При Sorted=true, обработка дубликатов определяется свойством Duplicates. Можно пропускать, разрешать и запрещать дубликаты объектов с одинаковым текстовым представлением. При этом, если дубликаты пропускаются или запрещены, необходимо следить за освобождением ресурсов объектов, не добавленных в список. По умолчанию, свойство Duplicates равно dupIgnore, что означает, что дубликаты пропускаются, поэтому по умолчанию надо следить за освобождением ресурсов.

Стоит отметить, что при сохранении или загрузке списка строк, прикрепленные к строкам объекты не сохраняются и не восстанавливаются в стандартном TStringList, однако можно очень красиво решить эту проблему - написать потомка TStringList с переопределенными процедурами GetTextStr и SetTextStr, в которых придумать и реализовать собственный формат хранения объектов и их текстового представления в виде непрерывного текста.

Итак, мы увидели, что TStringList позволяет решать самые разнообразные задачи от группировки объектов с текстовым представлением до простой обработки списка значений переменных. Теперь посмотрим, как этот класс позволяет решать задачи упаковки/распаковки сложно-структурированных данных в текстовый вид и обратно. Такая задача очень часто возникает в коммуникационных задачах, например, при передаче данных по сети или между программными модулями.

Предположим, что нам надо запустить некую программу obrab.exe с параметром командной строки, в которой необходимо передать выбранный товар из сформированного выше списка. Напишем упаковщик и распаковщик данных. С TStringList эта задача программируется за две секунды:

Упаковщик

procedure runobrab;
var strtov,stred:TStringList;
begin
 strtov:=TStringList.Create;
 with TTovar(ListBox1.Items.Objects[ListBox1.ItemIndex]) do
 begin
 stred:=TStringList.Create;
 stred.Values['ЕдИзм']:=EdIzm.Name;
 stred.Values['Вес']:=floattostr(EdIzm.Weight);
 strtov.Values['Товар']:=Name;
 strtov.Values['ЕдИзм']:=stred.CommaText;
 strtov.values['КолУп']:=inttostr(CountUp);
 strtov.values['Цена']:=floattostr(PriceOut);
 end;
 winexec('obrab.exe '+strtov.CommaText,SW_SHOWNORMAL);
 strtov.Free;
 stred.Free;
end;

Распаковщик

function getparam:TTovar;
var strtov,stred:TStringList;
begin
 strtov:=TStringList.Create;
 stred:=TStringList.Create;
 strtov.CommaText:=paramstr(1);
 stred.CommaText:=strtov.Values['ЕдИзм'];
 result:=TTovar.Create;
 with result do
 begin
 EdIzm:=TEdIzm.Create;
 EdIzm.Name:=stred.Values['ЕдИзм'];
 EdIzm.Weight:=strtofloat(stred.Values['Вес']);
 Name:=strtov.Values['Товар'];
 CountUp:=strtoint(strtov.values['КолУп']);
 PriceOut:=strtofloat(strtov.values['Цена']);
 end;
 stred.Free;
 strtov.Free;
end;

Продолжение следует…

Автор: Цованян Роман

Получить ссылку на материал

Категория: Советы Дельферу | Добавил: Барон (15.12.2011)
Просмотров: 1898 | Теги: TStringList | Рейтинг: 0.0/0
[ Пожертвования для сайта ] [ Пожаловаться на материал ]

Если вам помог материал сайта кликните по оплаченной рекламе размещенной в центре

Поиск
Категории раздела
Delphi.NET [3]
Kylix Delphi for Linux [9]
Советы Дельферу [6]
Хитрости в Delphi [2]
Обзор Delphi [45]
Инсталлятор [11]
Пользовательский интерфейс [18]
Примеры Delphi [93]
Функции и процедуры [15]
Разные [31]
Королевство Delphi © 2010-2024
Яндекс цитирования