Ceci est appelé un « ordre de tri naturel », et est généralement utilisé pour trier les éléments comme ceux que vous avez, comme les noms de fichiers et autres .
est ici un naïf (dans le sens où il y a probablement beaucoup de UNICODE problèmes avec elle) la mise en œuvre qui semble faire l'affaire:
Vous pouvez copier le code ci-dessous dans LINQPad pour l'exécuter et le tester.
Fondamentalement, l'algorithme de comparaison identifiera les numéros dans les chaînes, et gérer ceux de rembourrage le plus court avec des zéros non significatifs, donc par exemple les deux chaînes "Test123Abc"
et "Test7X"
devraient être comparés comme si elles étaient "Test123Abc"
et "Test007X"
, qui devrait produire ce que tu veux.
Cependant, quand je dit « naïf », je veux dire que j'ai probablement des tonnes de problèmes unicode réel ici, comme la manipulation diacritiques et des caractères multi-codepoint. Si quelqu'un peut donner une meilleure mise en œuvre, j'aimerais le voir.
Notes:
- La mise en œuvre n'a pas analyser réellement les nombres, donc arbitrairement longs nombres devrait fonctionner parfaitement
- Comme il ne parse pas réellement les nombres comme « chiffres », les nombres à virgule flottante "123.45" contre "123.789" sera comparé à "123.045" contre "123.789", ce qui est faux.
code:
void Main()
{
List<string> input = new List<string>
{
"1", "5", "3", "6", "11", "9", "A1", "A0"
};
var output = input.NaturalSort();
output.Dump();
}
public static class Extensions
{
public static IEnumerable<string> NaturalSort(
this IEnumerable<string> collection)
{
return NaturalSort(collection, CultureInfo.CurrentCulture);
}
public static IEnumerable<string> NaturalSort(
this IEnumerable<string> collection, CultureInfo cultureInfo)
{
return collection.OrderBy(s => s, new NaturalComparer(cultureInfo));
}
private class NaturalComparer : IComparer<string>
{
private readonly CultureInfo _CultureInfo;
public NaturalComparer(CultureInfo cultureInfo)
{
_CultureInfo = cultureInfo;
}
public int Compare(string x, string y)
{
// simple cases
if (x == y) // also handles null
return 0;
if (x == null)
return -1;
if (y == null)
return +1;
int ix = 0;
int iy = 0;
while (ix < x.Length && iy < y.Length)
{
if (Char.IsDigit(x[ix]) && Char.IsDigit(y[iy]))
{
// We found numbers, so grab both numbers
int ix1 = ix++;
int iy1 = iy++;
while (ix < x.Length && Char.IsDigit(x[ix]))
ix++;
while (iy < y.Length && Char.IsDigit(y[iy]))
iy++;
string numberFromX = x.Substring(ix1, ix - ix1);
string numberFromY = y.Substring(iy1, iy - iy1);
// Pad them with 0's to have the same length
int maxLength = Math.Max(
numberFromX.Length,
numberFromY.Length);
numberFromX = numberFromX.PadLeft(maxLength, '0');
numberFromY = numberFromY.PadLeft(maxLength, '0');
int comparison = _CultureInfo
.CompareInfo.Compare(numberFromX, numberFromY);
if (comparison != 0)
return comparison;
}
else
{
int comparison = _CultureInfo
.CompareInfo.Compare(x, ix, 1, y, iy, 1);
if (comparison != 0)
return comparison;
ix++;
iy++;
}
}
// we should not be here with no parts left, they're equal
Debug.Assert(ix < x.Length || iy < y.Length);
// we still got parts of x left, y comes first
if (ix < x.Length)
return +1;
// we still got parts of y left, x comes first
return -1;
}
}
}
Est-ce que ces valeurs de chaîne représentent des nombres hexadécimaux? Ou serait-il possible que "S2" apparaisse dans la liste? –
@El Ronnoco: Non hexadécimal non. Il peut être "S2" etc ... (Edité dans) –