Урок Delphi 12

StringList Delphi - работа со списком строк




Уроки Delphi
  1.  Первая программа
  2.  Использование компонентов
  3.  События Delphi
  4.  Типы данных Delphi
  5.  Создание своих типов данных
  6.  Выражения и операторы
  7.  Работа с файлами в Delphi
  8.  Дополнительные формы
  9.  Подпрограммы в Delphi
  10. Исключительные ситуации
  11. Взаимодействие приложения с пользователем
  12. Указатели в Delphi
  13. Обзор компонентов
  14. Работа со строками
  15. Создание интерфейса
  16. Графика в Delphi
  17. Многопоточность в Delphi
  18. Динамическое создание
        компонентов
Поиск по сайту




 Это важно:
   Метод Application.ProcessMessages;

 Это полезно:
   Параметр Sender в обработчиках событий;










Бояться не надо



   Список строк Delphi TStringList - это структура данных, напоминающая компонент ListBox, но не визуальная, а просто хранящая в памяти и имеющая свойства и методы для работы со строками типа TString.

   Для работы со списком строк типа TStringList его сначала необходимо создать с помощью конструктора - Create:

var StrList: TStringList;
begin
  StrList:=TStringList.Create;

Теперь нужно описать способ хранения строк в списке. Список строк типа TStringList может сортировать добавляемые строки или хранить без сортировки, а также может игнорировать попытки добавления новой строки при наличии уже сохранённого дубликата:

  StrList.Sorted:=True;    //True - сортировать, False - не сортировать
  StrList.Duplicates:=dupIgnore;    //dupAccept - сохранять дубликаты (значение по умолчанию), dupIgnore - игнорировать, dupError - вызвать сообщение об ошибке
Теперь можно добавить в список строки типа TString:

  StrList.Add('Новая строка');    //Добавление в конец списка (или в порядке сортировки). Возвращается индекс строки
  StrList.Insert(Index, 'Новая строка');    //Строка добавляется на позицию с номером Index. Если список отсортирован, то возникает исключительная ситуация.
Удалить строку:

  StrList.Delete(Index);   // Удаление строки с номером Index;

Количество строк в списке:

  N:=StrList.Count;

Доступ к строке с номером Index:

  Str:=StrList[Index];

Index отсчитывается от 0, поэтому номер последней строки:

  Last:=StrList.Count-1;

Очищаем список строк:

  StrList.Clear;

Если список отсортирован, есть метод для поиска:

  StrList.Find(S, Index); // В случае удачного поиска возвращает True, и индекс строки S в переменной Index


Если список не отсортирован, то тоже есть функция поиска:

  I:=StrList.IndexOf(Text); // Возвращает индекс строки с заданным текстом Text, или -1, если поиск неудачен


В конце работы со списком его необходимо удалить из памяти:

  StrList.Free;
end;

   Как видим, список типа TStringList обладает ценным свойством, которым вряд ли обладает какой-либо другой объект в Delphi, а именно возможностью обнаруживать дубликаты строк. Как узнать, что строка уже содержится в списке? Вот алгоритм:

  1. Запретить списку запись дубликатов, что возможно только при задании сортировки списка. Это делается один раз, при создании списка, например, по событию Формы OnCreate:
      StrList.Duplicates:=dupIgnore;
      StrList.Sorted:=True;
  2. Сохранить в переменной количество строк в списке:
      begin
        Count:=StrLst.Count;
  3. Попробовать записать новую строку:
        StrList.Add(NewLine);
  4. Сравнить количество строк в списке с прежним значением. Если оно совпадёт с количеством перед записью, то такая строка в списке уже есть:
        if StrList.Count=Count
          then ShowMessage('Попытка добавления дубликата');
      end;
   Аналогичный, на мой взгляд даже более удобный, алгоритм реализации этой процедуры связан с использованием метода обработки исключений. Если вместо StrList.Duplicates:=dupIgnore для обработки дубликатов использовать значение dupError, это вызовет исключительную ситуацию при попытке добавить дубликат. Соответственно, при добавлении оригинальной строки программа идёт дальше, а при попытке добавления дубликата переходит к операторам секции except/end.

   Эти методы работают в случае если список отсортирован. Однако есть задачи, где сортировка не нужна, а находить значение в списке всё равно необходимо. В этом случае нужно пользоваться функцией IndexOf.

Рассмотрим пример

Есть таблица, содержащая список студентов с указанием их роста и веса. Нужно выделить группу студентов, чей рост или вес встречаются наиболее часто.
Алгоритм решения задачи будет такой. Создадим три списка строк. В цикле будем просматривать таблицу и добавлять в первый список строк значение анализируемого параметра, если его ещё в списке нет. Во второй список будем сохранять фамилии студентов, обладающих данным ростом или весом, а в третьем - в соответствующей строке подсчитывать сколько раз это значение уже встретилось. Вот что должно получиться:

Исходная таблица Первый список Второй список Третий список
ФамилияРостВес
Иванов17980
Петров18082
Сидоров17978
Ковалёв17578
Анализ по росту
179
180
175

Иванов, Сидоров
Петров
Ковалёв

2
1
1


Переходя к новой строке в исходной таблице, мы анализируем нужный параметр, рост или вес - ищем в первом списке. Если такого значения у нас пока нет, мы в каждый список добавляем по строке, в первый записываем значение параметра, во второй - фамилию, а в третий - пишем единичку. А если такое значение параметра уже есть, то в соответствующую строку второго списка добавляем через запятую очередную фамилию, а в третьем значение в этой строке увеличиваем на 1. Естественно, списки строк не должны сортировать вводимые значения, иначе данные перепутаются!
Дойдя до конца таблицы, нам останется в третьем списке найти строку с максимальным числом, и вывести как результат содержимое этой строки второго списка. Получилась такая программка:



А вот процедура подсчёта:

procedure TForm1.Button1Click(Sender: TObject);
var i, k: Integer;
begin
Caption:='';
List1.Clear; List2.Clear; List3.Clear;
Param:=ComboBox1.ItemIndex+1;
for i:=1 to StringGrid1.RowCount-1 do
 begin
  k:=List1.IndexOf(StringGrid1.Cells[Param, i]);
  if k=-1 then
  begin
    List1.Add(StringGrid1.Cells[Param, i]);
    List2.Add(StringGrid1.Cells[0, i]);
    List3.Add('1');
  end     else
  begin
    List2[k]:=List2[k]+', '+StringGrid1.Cells[0, i];
    List3[k]:=IntToStr(StrToInt(List3[k])+1);
  end;
 end;
k:=0;
for i:=0 to List3.Count-1 do
  if StrToInt(List3[i])>k then
    k:=StrToInt(List3[i]);
for i:=0 to List2.Count-1 do
  if StrToInt(List3[i])=k then Caption:=Caption+List2[i]+': '+List1[i]+'; ';
end;


Сортировка в StringList'е

   С помощью StringList можно реализовать интересный вариант сортировки чисел. Хотя StringList сортирует строки, он также будет сортировать и записанные в него в виде строк числа. Естественно, напрямую отсортировать числа таким образом не получится, так как строка, например, '100' будет меньше, чем строка '20', что для чисел 100 и 20, естественно, неверно. Однако достаточно выровнять длину строк добавлением к целой части слева, а к дробной справа пробелов ' ', чтобы сортировка сработала верно. Программисты, создавшие класс TStringList, наделили его очень быстрым алгоритмом сортировки (к тому же реализованным, видимо, с использованием низкоуровневого программирования), чем мы просто не можем не воспользоваться. По сравнению с сортировкой пузырьком, которая 100 000 чисел сортирует за 32 с половиной секунды на моём компе (или 24 сек улучшенный алгоритм), сортировка в StringList'е длится всего 530 мсек! Вот процедура для целых чисел:

procedure StringListForSort;
var S: TStringList;
i, max: Integer;
Taim: Int64;
begin
Taim:=GetTickCount;
max:=0;
S:=TStringList.Create;
for i:=1 to Form1.Grid.RowCount-1 do
  begin
    if Length(Form1.Grid.Cells[1, i])>max
      then max:=Length(Form1.Grid.Cells[1, i]);
    S.Add(Form1.Grid.Cells[1, i]);
  end;

for i:=0 to S.Count-1 do
  while Length(S[i])<>max do
    S[i]:=' '+S[i];
S.Sort;
Form1.Caption:='Продолжительность сортировки '+IntToStr(GetTickCount-Taim)+' мсек';

for i:=1 to Form1.Grid.RowCount-1 do
  Form1.Grid.Cells[1, i]:=Trim(S[i-1]);
S.Free;
end;

   Здесь Grid - обычная таблица типа TStringGrid, предварительно заполненная целыми числами. Продолжительность сортировки возвращается в заголовке Формы.

Обзор алгоритмов сортировки

   Сортировка в StringList'е лишь в два раза медленнее так называемой "быстрой" сортировки. Для сравнения, рассмотрим и некоторые другие алгоритмы сортировки.

   Сортировка пузырьком - самый естественный, он же самый медленный алгоритм сортировки. Массив чисел просматривается от начала до конца до тех пор, пока любые два рядом стоящих числа не будут расположены по возрастанию.
   Сортировка вставками - ещё один простой способ сортировки. Массив чисел сортируется с начала, и каждое последующее число вставляется в уже отсортированную часть массива на предназначенное ему место.
   Быстрая сортировка - очень эффективный алгоритм, и известна как в среднем самая быстрая из универсальных алгоритмов сортировки. Быстрая сортировка сравнивает все элементы массива с одним, выбранным практически наугад, элементом (опорным элементом) и тем самым делит массив на две части - в одну попадают числа меньшие опорного, а в другую - большие опорного. читать далее...



Работа со строками           В начало урока          Создание интерфейса  

Уроки Delphi начинающим



Вопросы и комментарии (14)      Решение задач в Delphi

Оставить комментарий:

Имя  

Текст комментария