Практика
Естественная сортировка

:: Меню ::
:: На главную ::
:: FAQ ::
:: Заметки ::
:: Практика ::
:: Win API ::
:: Проекты ::
:: Скачать ::
:: Секреты ::
:: Ссылки ::

:: Сервис ::
:: Написать ::

:: MVP ::

:: RSS ::

Яндекс.Метрика


Сортируя строки обычным (применяя для сравнения операторы ">", "<" или “=”) методом, получаемый результат не всегда является логичным с точки зрения человека. Речь идет о тех случаях, когда строки содержат в себе числа. Допустим, у нас есть набор из следующих строк: ‘str_1’, ‘str_8’, ‘str_11’, ‘str_19’, ‘str_2’ и ‘str_20’. Ниже, в таблице, приведен пример того, как эти строки будут отсортированы обычным способом (в левой колонке), и как бы это было естественнее для восприятия человеком (в правой колонке).

Обычная сортировка Чего бы хотелось
str_1 str_1
str_11 str_2
str_19 str_8
str_2 str_11
str_20 str_19
str_8 str_20

Как же это сделать? Попробуем разобраться

function StrCmpDig( const Str1, Str2: string ): Integer;
var
  i, Len, Digit1, Digit2, DigitLen1, DigitLen2: Integer;
begin
   if Trim( Str1 ) = Trim( Str2 ) then Exit( 0 );
   if ( Str1 = '' ) then Exit( -1 );
   if ( Str2 = '' ) then Exit( 1 );

   Len := Max( Length( Str1 ), Length( Str2 ) );
   i := 1;

   while i <= Len do
   begin
      if IsDigit( Str1[i] ) and ( IsDigit( Str2[i] ) ) and ( Str1[i] <> '0' ) and ( Str2[i] <> '0' ) then
      begin
         DigitLen1 := DigitLen( Str1, i );
         DigitLen2 := DigitLen( Str2, i );
         Digit1 := StrToInt( Copy( Str1, i, DigitLen1 ) );
         Digit2 := StrToInt( Copy( Str2, i, DigitLen2 ) );
         if Digit1 < Digit2 then Exit( -1 );
         if Digit1 > Digit2 then Exit( 1 );
         Inc( i, Min( DigitLen1, DigitLen2 ) );
         Continue;
      end;
      if Str1[i] < Str2[i] then Exit( -1 );
      if Str1[i] > Str2[i] then Exit( 1 );
      Inc( i );
   end;
   Result := 0;
end;

Первые 3 строчки кода обрабатывают ситуацию, когда приходится иметь дело с пустыми строками. В случае, если заранее известно, что пустых строк в вашем приложении не будет, их можно убрать.

Запускаем цикл с числом итераций рамным длине самой большой строки. Если в одинаковой позиции в обеих строках находим цифру, то получаем число, находящееся в строках начиная с этой позиции. И та строка, число в которой окажется больше, и будет больше, все просто!

Часть первоначального условия

{...} and ( Str1[i] <> '0' ) and ( Str2[i] <> '0' ) then

нужна для того, чтобы корректно обрабатывать ситуации, в которых число в одной из строк начинается с нуля, например ‘num_01’ и ‘num_11’.

Параметризированный Exit появился в Delphi 2009, для более ранних версий нужно писать

begin
   Result := -1;
   Exit;
end;

Длину числа в строке определяем следующим образом.

function DigitLen( Str: string; Index: Integer ): Integer;
var
  i: integer;
begin
   Result := 0;
   for i := Index to Length( Str ) do
      if IsDigit( Str[i] ) then
         Inc( Result )
      else
         Break;
end;

Функцию проверки символа на равенство числу можно написать самостоятельно,

function IsDigit( c: Char ): Boolean;
begin
   Result := ( c >= '0' ) and ( c <= '9' );
end;

а можно воспользоваться классом TCharacter.

uses
  {...,} Character;

if TCharacter.IsDigit( c ) then {...}

В прилагаемом примере реализовано 2 типа сортировки на базе ListView, наглядно демонстрирующих удобство описанного в статье метода. Применение этого метода сортировки в ваших программах благоприятно скажется на отношении к ним пользователей. Удачи в программировании!

.: Пример к данной статье :.


При использовании материала - ссылка на сайт обязательна