81 lines
2.0 KiB
C#
81 lines
2.0 KiB
C#
namespace PlotLine.Services;
|
|
|
|
public sealed class NaturalStringComparer : IComparer<string?>
|
|
{
|
|
public static readonly NaturalStringComparer OrdinalIgnoreCase = new();
|
|
|
|
private NaturalStringComparer()
|
|
{
|
|
}
|
|
|
|
public int Compare(string? x, string? y)
|
|
{
|
|
if (ReferenceEquals(x, y))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (x is null)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (y is null)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
var xIndex = 0;
|
|
var yIndex = 0;
|
|
while (xIndex < x.Length && yIndex < y.Length)
|
|
{
|
|
if (char.IsDigit(x[xIndex]) && char.IsDigit(y[yIndex]))
|
|
{
|
|
var numberCompare = CompareNumberRuns(x, ref xIndex, y, ref yIndex);
|
|
if (numberCompare != 0)
|
|
{
|
|
return numberCompare;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
var charCompare = char.ToUpperInvariant(x[xIndex]).CompareTo(char.ToUpperInvariant(y[yIndex]));
|
|
if (charCompare != 0)
|
|
{
|
|
return charCompare;
|
|
}
|
|
|
|
xIndex++;
|
|
yIndex++;
|
|
}
|
|
|
|
return x.Length.CompareTo(y.Length);
|
|
}
|
|
|
|
private static int CompareNumberRuns(string x, ref int xIndex, string y, ref int yIndex)
|
|
{
|
|
var xStart = xIndex;
|
|
var yStart = yIndex;
|
|
while (xIndex < x.Length && char.IsDigit(x[xIndex]))
|
|
{
|
|
xIndex++;
|
|
}
|
|
|
|
while (yIndex < y.Length && char.IsDigit(y[yIndex]))
|
|
{
|
|
yIndex++;
|
|
}
|
|
|
|
var xRun = x[xStart..xIndex].TrimStart('0');
|
|
var yRun = y[yStart..yIndex].TrimStart('0');
|
|
if (xRun.Length != yRun.Length)
|
|
{
|
|
return xRun.Length.CompareTo(yRun.Length);
|
|
}
|
|
|
|
var valueCompare = string.Compare(xRun, yRun, StringComparison.Ordinal);
|
|
return valueCompare != 0 ? valueCompare : (xIndex - xStart).CompareTo(yIndex - yStart);
|
|
}
|
|
}
|