Чудеса TStringList
Object Pascal у поєднанні з асемблером у сучасній його формі Delphi 5/6/7 надає необмежені можливості для польоту думки програміста, і цією статтею ми відкриємо серію, в якій послідовно це демонструватимемо.
Почнемо ми з простого, вкрай корисного, але мало відомого принаймні для початківців стандартного класу Дельфі TStringList. Зараз ми подивимося, наскільки красиво вирішуються типові завдання з цього класу.
Для початку двома словами, які такі чудові властивості є в об'єктів даного класу. TStringList - це клас, призначений для зберігання списку рядків та списку об'єктів з текстовим поданням (прямо як у 1С - це СписокЗначень). Крім того, цей список може бути відсортований за алфавітом або за допомогою порівняльної функції, написаної програмістом. Крім того, цей список можна інтерпретувати як список значень (Name=string). Крім того, цей список може бути збережений у файл або потік, перетворений на безперервний рядок або рядок, розділений комами. Фізично одержуваний рядок або потік є звичайним текстом, в якому рядки розділені символами CR/LF (стандартний Windows Text File), або комами (CommaText, Excel). Ну і само собою, є можливість завантажувати список рядків із файлу, потоку, рядка та рядка, розділеного комами. Слід особливо відзначити чудову властивість упаковки в рядок, розділений комами: при зворотному розпакуванні рядки завжди відновлюються в їхньому вихідному вигляді. Це означає, що допустима багаторазова вкладеність рядків, розділених комами, що дає величезний виграш при упаковці/розпакуванні багатовимірних структурних даних у текстовий формат, що ми продемонструємоу другому завданні.
Отже, для початку розглянемо список рядків як список об'єктів з текстовим поданням, тому що саме в даному ключі слід використовувати список рядків у додатках. Що являє собою об'єкт з текстовим поданням? Це може бути, наприклад, список товарів, що мають, крім найменування, ще й додаткові параметри типу одиниці виміру, кількості в упаковці та ціни. Отже, маємо визначення типів:
Набір об'єктів TTovar – це класичний довідник однорідних товарів, наприклад, хлібобулочних виробів. Поле Weight у класі TEdIzm потрібно для переведення одних одиниць до інших. Повернемося до наших булок. Припустимо, постачальник "Карякінський Хлібозавод" надав нам текстовий файл, в якому знаходиться інформація про його нову продукцію та відпускні ціни у форматі текстового файлу:
Якщо вирішувати це завдання в лоб (як читання рядкове і розбір текстового файлу), а також безпосередньо додавати в ListBox, то це обернеться неефективною роботою комп'ютера, його підгальмовуванням (на слабких машинах), і взагалі, для подальших внесення змін до програмного коду рішення не є найкращим. Набагато ефективніше зробити "фінт вухами", а саме створити TStringList, завантажити в нього вихідний файл, створити другий TStringList, завантажити в нього товари, відсортувати їх, і зрештою, привласнити властивості ListBox.Items:
Зауважимо, що якщо написати функцію типу TStringListSortCompare, то можна буде сортувати не лише за текстовим поданням, але й за будь-якими іншими ознаками, наприклад, ціною:
Ну і врешті-решт, продемонструємо реакцію на подвійне натискання на списку товарів, що вийшов. За подвійним клацанням ми покажемо відпускну ціну товару:
До речі, не забуваємо звільняти системні ресурси об'єктів.видаленням рядків методами ListBox1.Items.Delete()/Clear() або str.Delete()/Clear() (str:TStringList), якщо це потрібно, а також при закритті програми:
Тепер трохи ускладнимо завдання. Нехай у нас є не один файл, а десять – від десяти різних постачальників. Нам треба завантажити 20000 найменувань і потім відсортувати їх. Якщо ми саме так і робитимемо, то сортування такого великого списку об'єктів займе значний час, це і є завданням. Однак таке завдання вирішується дуже просто - достатньо після створення об'єкта TStringList одразу привласнити його властивості Sorted значення true. Після цього вставка нових рядків здійснюватиметься за допомогою швидкого алгоритму, проте загубиться можливість сортувати за параметрами прикріплених об'єктів, т.к. у випадку Sorted=true сортування проводиться автоматично лише за текстовим уявленням (див. вихідні записи TStringList). При Sorted = true обробка дублікатів визначається властивістю Duplicates. Можна пропускати, дозволяти та забороняти дублікати об'єктів з однаковим текстовим поданням. При цьому, якщо дублікати пропускаються або заборонені, слід стежити за звільненням ресурсів об'єктів, не доданих до списку. За умовчанням, властивість Duplicates дорівнює dupIgnore, що означає, що дублікати пропускаються, тому за умовчанням слід стежити за звільненням ресурсів.
Варто зазначити, що при збереженні або завантаженні списку рядків, прикріплені до рядків об'єкти не зберігаються і не відновлюються у стандартному TStringList, проте можна дуже красиво вирішити цю проблему – написати нащадка TStringList з перевизначеними процедурами GetTextStr та SetTextStr, у яких придумати та реалізувати власний формат зберігання об'єктів та їх текстового подання у вигляді безперервноготексту.
Отже, ми побачили, що TStringList дозволяє вирішувати найрізноманітніші завдання від угруповання об'єктів з текстовим поданням до простої обробки списку змінних значень. Тепер подивимося, як цей клас дозволяє вирішувати завдання упаковки/розпакування складно-структурованих даних у текстовий вигляд і назад. Таке завдання дуже часто виникає в комунікаційних задачах, наприклад, при передачі даних через мережу або між програмними модулями.