Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Центр помощи > Как отсортировать строки нужным мне образом?


Автор: ДЫМ 26.6.2006, 01:15
У меня есть список строк (если кому интересно, номера домов)

2
10
2а
1а
1
110
20
1б

Мне надо их отсортировать, чтобы список выглядел так

1
1а
1б
2
2а
10
20
110

Я знаю, что это возможно, потому что Total Commander, например, файлы с такими именами сорирует как мне надо, но метод TStringList.Sort выдает такой результат

1
10
110
1а
1б
2
20
2а


Как мне поступить? Подскажите, очень надо. 

Автор: Демо 26.6.2006, 01:55
ДЫМ, 

Используй метод TStringList.CustomSort 

Автор: Poseidon 26.6.2006, 03:18
Использован http://forum.vingrad.ru/index.php?showtopic=3202&view=findpost&p=15643

Код
function LPadCh(const S : string; Ch: Char; Len: Integer) : string;
 // Возвращает строку "добитую" символом Ch слева до длинны Len
var SLen : Integer;
begin
 Result := S;  SLen := Length(S);
 if Len <= SLen then  Exit;
 SetLength(Result,Len);
 if SLen > 0  then  Move(Result[1],Result[Succ(Word(Len))-SLen], SLen);
 FillChar(Result[1], Len-SLen, Ch);
end;

function IsInteger(const Value: string): Boolean;
var
  OutResult: Integer;
  Code: Integer;
begin
  Val(Value, OutResult, Code);
  Result := (Code = 0);
end;

function RPadCh(const S: string; Ch: Char) : string;
begin
  if not IsInteger(S[Length(s)])
  then Result:= '0' + s
  else Result:= s + Ch;
end;


function MyCompare (List: TStringList; Index1, Index2: Integer): Integer;
begin
 Result := CompareStr(RPadCh(LPadCh(List.Strings[Index1],'0',3),'_'),RPadCh(LPadCh(List.Strings[Index2],'0',3),'_'));
end;

procedure TForm1.Button1Click(Sender: TObject);
var h: TStringList;
i: integer;
begin
h:= TStringList.Create;

h.Add('2');
h.Add('10');
h.Add('2а');
h.Add('1а');
h.Add('1');
h.Add('110');
h.Add('20');
h.Add('1б');

h.CustomSort(MyCompare);

ShowMessage(h.Text);
end;


Код написан безобразно, т.к. уже 3 часа ночи smile Если что не понятно, то спрашивай. 

Автор: ДЫМ 27.6.2006, 02:05
Poseidon,  за ночную работу держи "плюсик", для моей задачи худо-бедно сгодится. Но вот не дает мне покоя Total Commander, как сортирует хорошо зараза, но как это делает - не понятно.

Ну скажите, как получить такой же порядок строк:

Строка №1
Строка №1а
Строка №1аб
Строка №2
Строка №10
Строка №20
Строка №110



или

1/1
1/2
1/10


Раз смогли авторы Total Commander`а, то ведь можно разгадать это? Или нет? 
 

Автор: comtat 27.6.2006, 12:00
ДЫМ,
берешь строку выкидываешь из нее например "Строка №" и 
делаешь как в предыдушем примере
А потом при
Код

var str:string;
...
begin
str:='Строка №';
h:= TStringList.Create;

h.Add(str+'2');
....


Вот и все....  smile  

Автор: ДЫМ 27.6.2006, 21:33
Пример Poseidon не работает для набора 

1/1
1/2
1/10


да и вообще нужно что-то универсальное, а оно есть...  

Автор: Akina 27.6.2006, 21:50
Следует разделить строку на кванты и сортировать уже их.

Скажем строка 

Строка №1аб

делится на кванты

q1 = Строка №
q2 = 1
q3 = аб


и далее

order by q1 as string, q2 as integer, q3 as string

Аналогично строка 

1/10

делится на кванты

q1 = 1
q2 = /
q3 = 10

и далее

order by q1 as integer, q3 as integer
 

Автор: LesnoyChelovek 27.6.2006, 22:14
Я вот думаю, что можно для сортировки использовать таблицу ASCII. ПОка не знаю как, но может додумаюсь, так код выложу.

Примерные набросок, смотрим число, после него встречаем букву и сравниваем ASCII код последующих букв. 

Автор: ДЫМ 28.6.2006, 01:48
Akina , но как же определить где кончается один квант и начинается другой? Выходит что надо разделить строку на цепочки букв и цифр, последние надо рассматривать как числа и соответственно сортировать как числа, так что ли?

А это что за строка? Напоминает SQL
order by q1 as string, q2 as integer, q3 as string

Если бы было все так просто. Мне надо на Delphi. 

Автор: comtat 28.6.2006, 07:38
ДЫМ, у тебя фиксированные строки или произвольные ??
Цитата
А это что за строка? Напоминает SQL

Это и есть часть кода на SQL 

Автор: ДЫМ 28.6.2006, 22:18
Вопрос решен! 
Если кому-то интересно, решил написать как, потому что подобную проблему поднимали на форуме и тогда так и не нашли решения.
Понятно, что надо использовать StringList.CustomSort, я это подразумевал с самого начала, проблема как раз была в написании функции сравнения TListSortCompare. 
Логика такая: встречающиеся в строке числа добиваем нулями слева, чтобы они были одной разрядности, буквы и другие символы не трогаем, например:
строку:
Строка12Строка5
преобразуем в
Строка00012Строка00005
(в данном случае разрядность 5, с запасом)
потом сравниваем отформатированные строки


Код

//**********************
// Функция форматирования строки
// 
function fcGetFormattedString(sStr: string): string;
var i:Integer;
    sDigits:string;

begin
 sDigits:=''; Result:='';

 for i:=1 to Length(sStr)+1 do
 begin
  // выделяем из строки число
  if sStr[i] in ['0'..'9'] then
   begin
     sDigits:=sDigits+sStr[i];
   end
  else  // не цифры
   begin

    if sDigits<>'' then
     begin
      // добиваем текущее число нулями слева до 5
      Result:=Result+DupeString('0',5-Length(sDigits))+sDigits;
      sDigits:='';
     end;
     if sStr[i]<>#0 then Result:=Result+sStr[i];
   end;// не цифры

 end;//for
end;

//**********************
//  Функция сравнения для сортировки списка
//
function fcCompareItems(List: TStringList; Index1, Index2: Integer): Integer;
begin
 Result:=AnsiCompareText(
         fcGetFormattedString(List[Index1]),
         fcGetFormattedString(List[Index2]));
end;

Использование
Код

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

 List.Add('Строка №2');
 List.Add('Строка №10');
 List.Add('Строка №20');
 List.Add('Строка №110');
 List.Add('1');
 List.Add('1/1');
 List.Add('1б');
 List.Add('2');
 List.Add('Строка №1а');
 List.Add('2а');
 List.Add('10');
 List.Add('20');
 List.Add('110');
 List.Add('1а/1');
 List.Add('1а/10');
 List.Add('1а/2');
 List.Add('Строка №1');
 List.Add('Строка №1аб');
 List.Add('1/2');
 List.Add('1/10');
 List.Add('1а');
 List.Add('1аб');

 List.CustomSort(fcCompareItems);

 ShowMessage(List.Text);
end;
...

На выходе получаем то что нам надо
1
1/1
1/2
1/10
1а
1а/1
1а/2
1а/10
1аб
1б
2
2а
10
20
110
Строка №1
Строка №1а
Строка №1аб
Строка №2
Строка №10
Строка №20
Строка №110



Единственное слабое место - если в строках встретятся числа большей разрядности, чем предусмотрено, но всегда можно взять разрядность побольше, скажем не 5, а 10.  

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)