2015-08-11 4 views
1

Я StringList с комментариями (например, содержание раздела файла INI):Как сортировать StringList с комментариями

;comment c 
c=str1 
;comment b 
b=str2 
;comment a 
a=str3 

Любые идеи, как сортировать этот список по имен для:

;comment a 
a=str3 
;comment b 
b=str2 
;comment c 
c=str1 

Комментарии для пары должны быть связаны с парой во время сортировки

ответ

7

Один из вариантов заключается в анализе содержимого TStringList во втором списке, который разделяет и группирует имена, значения и строки комментариев вместе, а затем сортирует t список шляп по именам по мере необходимости, затем повторно заполните TStringList с отсортированными группами. Например:

uses 
    ... 
    System.Classes, 
    System.SysUtils, 
    System.Generics.Defaults, 
    System.Generics.Collections, 
    System.StrUtils, 
    System.Types; 

type 
    ItemInfo = record 
    LeadingText, 
    Name, 
    Value: string; 
    end; 

    ItemInfoComparer = class(TComparer<ItemInfo>) 
    public 
    function Compare(const Left, Right: ItemInfo): Integer; override; 
    end; 

function ItemInfoComparer.Compare(const Left, Right: ItemInfo): Integer; 
begin 
    if (Left.Name <> '') and (Right.Name <> '') then 
    Result := AnsiCompareStr(Left.Name, Right.Name) 
    else if (Left.Name <> '') then 
    Result := -1 
    else 
    Result := 1; 
end; 

procedure SortMyList(List: TStringList); 
var 
    Compare: IComparer<ItemInfo>; 
    Items: TList<ItemInfo>; 
    Info: ItemInfo; 
    I: Integer; 
    InText: Boolean; 
    S: String; 
begin 
    Compare := ItemInfoComparer.Create; 
    Items := TList<ItemInfo>.Create(Compare); 
    try 
    Items.Capacity := List.Count; 
    InText := False; 

    for I := 0 to List.Count-1 do 
    begin 
     S := Trim(List[i]); 
     if (S = '') or (S[1] = ';') then 
     begin 
     if InText then 
      Info.LeadingText := Info.LeadingText + #13 + List[i] 
     else 
     begin 
      Info.LeadingText := List[i]; 
      InText := True; 
     end; 
     end else 
     begin 
     Info.Name := List.Names[I]; 
     Info.Value := List.ValueFromIndex[I]; 
     Items.Add(Info); 
     Info := Default(ItemInfo); 
     InText := False; 
     end; 
    end; 

    if InText then 
     Items.Add(Info); 

    Items.Sort; 

    List.Clear; 
    for I := 0 to Items.Count-1 do 
    begin 
     Info := Items[I]; 

     if Info.LeadingText <> '' then 
     begin 
     for S in SplitString(Info.LeadingText, #13) do 
      List.Add(S); 
     end; 

     if Info.Name <> '' then 
     List.Add(Info.Name + '=' + Info.Value); 
    end; 
    finally 
    Items.Free; 
    end; 
end; 
1

Это простая процедура, которая сортирует и обрабатывает пробелы в качестве груза. Я также добавил код для обработки комментариев в конце файла.

Это будет работать с более старыми версиями Delphi, которые не имеют дженерики или расширенные типы, как в ответ Реми (при условии, как удобство для тех, кто использует более старые версии)

function SortKeys(List: TStringList; Index1, Index2: Integer): Integer; 
begin 
    result := CompareText(List.Names[Index1], List.Names[Index2]); 
end; 


Procedure SortStringListWithComments(AStrings: TStrings); 
var 
LCargoText: TStringList; 
LSortedText : TStringList; 
s: string; 
i : integer; 
begin 
    LCargoText := nil; 
    LSortedText := TStringList.Create; 
    try 
    for i := 0 to AStrings.count-1 do 
    begin 
     s := Trim(AStrings[i]); 
     if (s='') or (s[1] = ';') then //LCargoText and blank lines attached to sorted strings (Boolean short circuit assumed here) 
     begin 
     if LCargoText = nil then 
      LCargoText := TStringList.Create; 
     LCargoText.Add(AStrings[i]); 
     end 
     else 
     begin 
     LSortedText.AddObject(AStrings[i], LCargoText); 
     LCargoText := nil; //set nil to deal with cases where we have no comments for a following key value pair 
     end; 

    end; 

    LSortedText.CustomSort(SortKeys); 

    // LSortedText.sort - will cause a1=x to be sorted before a=x 

    AStrings.clear; 

    for i := 0 to LSortedText.count-1 do 
    begin 
     if LSortedText.objects[i] <> nil then 
     begin 
     AStrings.AddStrings(TStringList(LSortedText.Objects[i])); 
     LSortedText.Objects[i].Free; 
     end; 
     AStrings.Add(LSortedText[i]); 
    end; 

    if LCargoText <> nil then 
    begin 
     AStrings.AddStrings(LCargoText) ; //comments orphaned at the end of the file 
     LCargoText.Free; 
    end; 
    finally 
    LSortedText.Free; 
    end; 
end; 
+0

я использовал дженерики, так как я знал, что они были доступны, как вопрос помечен XE8. В любом случае единственными изменениями, которые я предлагаю, является добавление обработки исключений, чтобы обеспечить правильную очистку вещей в случае ошибки и изменить 'Sort()' на 'CustomSort()', чтобы вы могли сортировать только по фактическим именам (как и ОП), а не целыми линиями. Ваш обработчик сортировки может использовать 'TStringList.Names []' для извлечения имен каждой строки. –

+0

Предоставляется в качестве удобства для других. Я согласен, что вам понадобится обычная сортировка, так как числа имеют более низкое порядковое значение, чем =. Нет никаких известных исключений, которые мне нужно обработать, но я сделаю, наконец, бесплатно –

+1

Что касается исключений, я больше думал об ошибках выделения памяти, а не о других ошибках во время выполнения. Вы всегда должны использовать 'try/finally' как минимум при работе с локальными парами' Create/Free'. И не забывайте, что 'TStringList' не владеет указателями' Objects [] ', поэтому вы должны' Free' их вручную, прежде чем освобождать 'TStringList', если вы используете' finally', чтобы освободить его. –

Смежные вопросы