2009-04-24 23 views
11

Sto affrontando uno strano problema durante l'ordinamento di un elenco di stringhe con valori interi. Tuttavia alcuni valori potrebbero essere preceduti da alcuni caratteri.Come ordinare le stringhe dei numeri interi?

ad es.

// B1, 5, 50, A10, 7, 72, B3, A1, A2 

ci sono fondamentalmente i numeri di pagina e deve essere ordinato come:

// A1, A2, A10, B1, B3, 5, 7, 50, 72 

Ma se io uso stringa di default l'ordinamento allora questi saranno ordinati come

// A1, A10, A2, B1, B3, 5, 50, 7, 72 

Qualsiasi soluzione per questo in C#?

+0

È possibile utilizzare questo 'NaturalStringComparer' che ho messo insieme e ripulito un po '(non ricordo dove ho avuto la base per esso) . Usa la funzione Win32 StrCmpLogicalW menzionata da Skizz. http://my.opera.com/Svishy/blog/2009/03/02/natural-sorting – Svish

risposta

17

Stai cercando il Alphanum algorithm. Fortunatamente per te, esistono già un certo numero di implementazioni. Vedi here.

+0

Alphanum restituirà // 5, 7, 50, 72, A1, A2, A10, B1, B3 anziché // A1 ... 5 – Carra

+2

Se analizzi alcuni esempi di codice, è delineato il modo in cui lo cambierai per adattarsi a scenari leggermente diversi. –

0

Bene, è possibile sempre richiamare la funzione API Win32 StrCmpLogicalW che fa esattamente ciò che si desidera (è ciò che Explorer utilizza per ordinare i nomi dei file). L'unico svantaggio possibile è che l'ordinamento non fa distinzione tra maiuscole e minuscole.

5

Ecco come ho risolto per la nostra applicazione, l'ordine sarà come in una directory di Windows:

public class NaturalSortComparer : IComparer<string> 
{ 
    public int Compare(string x, string y) 
    { 
     return StrCmpLogicalW(x, y); 
    } 

    [DllImport("shlwapi.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] 
    public static extern int StrCmpLogicalW(string x, string y); 
} 

Usage:

NaturalSortComparer comparer = new NaturalSortComparer(); 
    return comparer.Compare(string1, string2); 

Ma probabilmente non è esattamente quello che vuoi:

// A1, A2, A10, B1, B3, 5, 7, 50, 72

Questo darà

// 5, 7, 50, 72, A1, A2, A10, B1, B3

0

Non sono sicuro prestazioni, e che questo può essere ottimizzato, ma fa il lavoro:

string[] sort(string[] data) 
{ 
    return data 
     .OrderBy(s => Regex.Match(s, @"^\D").Length == 0) 
     .ThenBy(s => Regex.Match(s, @"\D*").Value) 
     .ThenBy(s => Int32.Parse(Regex.Match(s, @"\d+").Value)).ToArray(); 
} 

var result = sort(new string[] { "B1", "5", "50", "A10", "7", "72", "B3", "A1", "A2" }); 
3

Quello che stai cercando è un ordinamento naturale.

Jeff Atwood ha fatto un bel post sul suo blog una volta, spiegando il concetto e il collegamento a varie altre fonti con algoritmi che potreste prendere come esempio.

Sorting for Humans : Natural Sort Order

1

Ecco un operatore di confronto personalizzato che ordinare nel vostro ordine richiesto. Si noti che non ci sono controlli di errore/integrità in questo codice: presuppone che tutte le stringhe saranno nel formato corretto.

public class MyComparer : IComparer<string> 
{ 
    public int Compare(string x, string y) 
    { 
     Match xMatch = Regex.Match(x, @"^(\D*)(\d+)$"); 
     Match yMatch = Regex.Match(y, @"^(\D*)(\d+)$"); 

     string xChars = xMatch.Groups[1].Value; 
     string yChars = yMatch.Groups[1].Value; 

     if ((xChars.Length == 0) && (yChars.Length > 0)) 
     { 
      return 1; 
     } 
     else if ((xChars.Length > 0) && (yChars.Length == 0)) 
     { 
      return -1; 
     } 
     else 
     { 
      int charsResult = xChars.CompareTo(yChars); 

      return (charsResult != 0) 
       ? charsResult 
       : int.Parse(xMatch.Groups[2].Value) 
        .CompareTo(int.Parse(yMatch.Groups[2].Value)); 
     } 
    } 
} 

Si può usare in questo modo:

List<string> testList = 
    new List<string>() { "B1","5","50","A10","7","72","B3","A1","A2" }; 

testList.Sort(new MyComparer()); // A1, A2, A10, B1, B3, 5, 7, 50, 72 
Problemi correlati