Come risolvere le stringhe numeriche come numeri?

Se hai delle stringhe come:

"file_0" "file_1" "file_2" "file_3" "file_4" "file_5" "file_6" "file_11" 

come puoi ordinarli in modo che “file_11” non venga dopo “file_1”, ma venga dopo “file_6”, poiché 11> 6.

Devo analizzare la stringa e convertirla in un numero per questo?

Windows Explorer in Win7 ordina i file come volevo.

È ansible importare la funzione StrCmpLogicalW e utilizzarla per ordinare le stringhe. Questa è la stessa funzione utilizzata da Explorer per i nomi dei file.

Non ti aiuterà se non vuoi P / Invoke o rimanere compatibile su altri sistemi, però.

Devo analizzare la stringa e convertirla in un numero per questo?

Essenzialmente, sì; ma LINQ può aiutare:

 var sorted = arr.OrderBy(s => int.Parse(s.Substring(5))); foreach (string s in sorted) { Console.WriteLine(s); } 

Per gestire l’ordinamento di stringhe e numeri intermixati per qualsiasi tipo di formato, è ansible utilizzare una class come questa per dividere le stringhe in componenti stringa e numero e confrontarle:

 public class StringNum : IComparable { private List _strings; private List _numbers; public StringNum(string value) { _strings = new List(); _numbers = new List(); int pos = 0; bool number = false; while (pos < value.Length) { int len = 0; while (pos + len < value.Length && Char.IsDigit(value[pos+len]) == number) { len++; } if (number) { _numbers.Add(int.Parse(value.Substring(pos, len))); } else { _strings.Add(value.Substring(pos, len)); } pos += len; number = !number; } } public int CompareTo(StringNum other) { int index = 0; while (index < _strings.Count && index < other._strings.Count) { int result = _strings[index].CompareTo(other._strings[index]); if (result != 0) return result; if (index < _numbers.Count && index < other._numbers.Count) { result = _numbers[index].CompareTo(other._numbers[index]); if (result != 0) return result; } else { return index == _numbers.Count && index == other._numbers.Count ? 0 : index == _numbers.Count ? -1 : 1; } index++; } return index == _strings.Count && index == other._strings.Count ? 0 : index == _strings.Count ? -1 : 1; } } 

Esempio:

 List items = new List { "item_66b", "999", "item_5", "14", "file_14", "26", "file_2", "item_66a", "9", "file_10", "item_1", "file_1" }; items.Sort((a,b)=>new StringNum(a).CompareTo(new StringNum(b))); foreach (string s in items) Console.WriteLine(s); 

Produzione:

 9 14 26 999 file_1 file_2 file_10 file_14 item_1 item_5 item_66a item_66b 

Il seguente codice basato sul suggerimento di Joey funziona per me (metodo di estensione a string []):

 public static void SortLogical(this string[] files) { Array.Sort(files, new Comparison(StrCmpLogicalW)); } [DllImport("shlwapi.dll", CharSet=CharSet.Unicode, ExactSpelling=true)] private static extern int StrCmpLogicalW(String x, String y); 

Un modo semplice è di riempire la porzione numerica in questo modo:

 file_00001 file_00002 file_00010 file_00011 

eccetera.

Ma questo si basa sul conoscere il valore massimo che la porzione numerica può assumere.

Ho usato il seguente approccio in un progetto qualche tempo fa. Non è particolarmente efficiente, ma se il numero di elementi da ordinare non è enorme, si è comportato abbastanza bene per quell’uso. Quello che fa è che divide le stringhe per confrontarle in matrici sul carattere '_' e quindi confronta ogni elemento degli array. Viene effettuato un tentativo di analizzare l’ultimo elemento come un int e fare un confronto numerico lì.

Ha anche un’uscita anticipata se le stringhe di input contengono un numero diverso di elementi (quindi se confronti “file_nbr_1” a “file_23”, non andrà a confrontare ciascuna parte delle stringhe, ma piuttosto a un confronto di stringhe regolari sulle stringhe piene):

 char[] splitChars = new char[] { '_' }; string[] strings = new[] { "file_1", "file_8", "file_11", "file_2" }; Array.Sort(strings, delegate(string x, string y) { // split the strings into arrays on each '_' character string[] xValues = x.Split(splitChars); string[] yValues = y.Split(splitChars); // if the arrays are of different lengths, just //make a regular string comparison on the full values if (xValues.Length != yValues.Length) { return x.CompareTo(y); } // So, the arrays are of equal length, compare each element for (int i = 0; i < xValues.Length; i++) { if (i == xValues.Length - 1) { // we are looking at the last element of the arrays // first, try to parse the values as ints int xInt = 0; int yInt = 0; if (int.TryParse(xValues[i], out xInt) && int.TryParse(yValues[i], out yInt)) { // if parsing the values as ints was successful // for both values, make a numeric comparison // and return the result return xInt.CompareTo(yInt); } } if (string.Compare(xValues[i], yValues[i], StringComparison.InvariantCultureIgnoreCase) != 0) { break; } } return x.CompareTo(y); });