2012-04-03 51 views
7

可能重複:
Natural Sort Order in C#排序串號

我有很多在這號碼列表。 但由於一些額外的字母,它們被保存爲字符串。

我的名單看起來是這樣的:

1 
10 
11 
11a 
11b 
12 
2 
20 
21a 
21c 
A1 
A2 
... 

,但它應該是這樣的

1 
2 
10 
11a 
11b 
... 
A1 
A2 
... 

我怎麼排序我的名單得到這樣的結果?

+0

打破 '數量' 爲組件,然後排序由。 – leppie 2012-04-03 07:34:22

+1

是的,自然排序是你之後的東西。這是Jon所說的重複。在http://zootfroot.blogspot.com.au/2009/09/natural-sort-compare-with-linq-orderby.html – 2012-04-03 07:38:52

+0

[C#中的自然分類](http://www.interact -sw.co.uk/iangblog/2007/12/13/natural-sorting) – 2012-04-03 08:13:10

回答

12

由先前的評論去,我還要實現一個自定義IComparer<T>類。從我能收集到的信息來看,這些項目的結構可以是一個數字,也可以是一個數字和一個字母組合。如果是這種情況,則應執行以下IComparer<T>實施。

public class CustomComparer : IComparer<string> 
{ 
    public int Compare(string x, string y) 
    { 
     var regex = new Regex("^(d+)"); 

     // run the regex on both strings 
     var xRegexResult = regex.Match(x); 
     var yRegexResult = regex.Match(y); 

     // check if they are both numbers 
     if (xRegexResult.Success && yRegexResult.Success) 
     { 
      return int.Parse(xRegexResult.Groups[1].Value).CompareTo(int.Parse(yRegexResult.Groups[1].Value)); 
     } 

     // otherwise return as string comparison 
     return x.CompareTo(y); 
    } 
} 

有了這個IComparer<T>,你就可以做

var myComparer = new CustomComparer(); 
myListOfStrings.Sort(myComparer); 

這已經過測試與下列項目進行排序您的string列表:

2, 1, 4d, 4e, 4c, 4a, 4b, A1, 20, B2, A2, a3, 5, 6, 4f, 1a

和給出結果:

1, 1a, 2, 20, 4a, 4b, 4c, 4d, 4e, 4f, 5, 6, A1, A2, a3, B2

+0

這很酷,但它不能正確處理像版本號的東西... 1.5.2,1.9.9,1.10.17 奇怪的是,Windows資源管理器正確地排序這些東西......但顯然,比較器不能用於任何其他C#代碼,以便重新生成與文件資源管理器按文件名進行排序時顯示的相同文件名順序。令人沮喪。 – pmbAustin 2016-07-19 21:06:57

+0

我相信正則表達式應該是'^(\\ d +)'。更好的是,正則表達式可能只是'(\\ d +)'來處理「區域1,區域10,區域2」以及「1,10,2」。 – stack247 2016-12-08 02:06:12

2

那麼,你需要從每個字符串中提取數字,然後根據數字列表中的鍵列表對字符串列表進行排序。分兩步做。

要從每個字符串中提取數字,我認爲最簡單的方法是使用正則表達式 - 查找(\d+)的匹配項(如果您有負數或十進制數,則必須使用不同的正則表達式) 。比方說,你這樣做,在一個函數調用ExtractNumber

現在你可以使用一些有創意的LINQ排序,像這樣:

strings.Select(s=>new { key=ExtractNumber(s), value=s }) // Create a key-value pair 
     .OrderBy(p=>p.key)        // Sort by key 
     .Select(p=>p.Value);        // Extract the values 
+0

這看起來像是一個使用LINQ的優雅解決方案,但當迭代到達項目'A1'和'A2'時會發生什麼? – Richard 2012-04-03 08:11:34

+0

int.Parse((new regex(@「(?<= pdf _)\ d *?(?= _ \ .bmp $)」))。Match(file).Value); // matches pdf_123456890_.bmp – 2017-02-09 18:29:00

0

我是相當新的C#,但這裏是一個解決方案,我在欣賞Java:您需要分兩步進行操作,首先定義一個自定義的IComparer,然後在調用sort方法時使用它。所以,你應該能夠做這樣的事情:

public class MyListSorter : IComparer<MyObject> 
{ 
    public int Compare(MyObject obj1, MyObject obj2) 
    { 
    if (!Char.IsNumber(obj1) && Char.IsNumber(obj2)) 
    { 
     return 0; 
    } 
    else if (Char.IsNumber(obj1) && !Char.IsNumber(obj2)) 
    { 
     return 1; 
    } 
    else 
    { 
     return obj2.CompareTo(obj1); 
    } 
    } 
} 

,然後IComparer的

myObjectList.Sort(new MyListSorter()); 

更多的相關信息:http://support.microsoft.com/kb/320727

1

由於這包括許多字符串操作,正則表達式等,我不認爲這是一個有效的算法,但它似乎工作。

List<string> list1 = new List<string>() { "11c22", "1", "10", "11", "11a", "11b", "12", "2", "20", "21a", "21c", "A1", "A2" }; 
List<string> list2 = new List<string>() { "File (5).txt", "File (1).txt", "File (10).txt", "File (100).txt", "File (2).txt" }; 
var sortedList1 = NaturalSort(list1).ToArray(); 
var sortedList2 = NaturalSort(list2).ToArray(); 

public static IEnumerable<string> NaturalSort(IEnumerable<string> list) 
{ 
    int maxLen = list.Select(s => s.Length).Max(); 
    Func<string, char> PaddingChar = s => char.IsDigit(s[0]) ? ' ' : char.MaxValue; 

    return list 
      .Select(s => 
       new 
       { 
        OrgStr = s, 
        SortStr = Regex.Replace(s, @"(\d+)|(\D+)", m => m.Value.PadLeft(maxLen, PaddingChar(m.Value))) 
       }) 
      .OrderBy(x => x.SortStr) 
      .Select(x => x.OrgStr); 
} 
+0

很想看到一個版本,它實際上處理版本號...有多個數字,以保持秩序:2.5.7,10.3.2,2.18.3等 – pmbAustin 2016-07-19 21:09:47

+1

這個作品太棒了!謝謝! – 2016-10-24 08:14:11