عنوان:

‫مرتب‌سازی عددی در نگارش‌های پیش از دات‌نت 10


نویسنده: وحید نصیری
تاریخ: ۱۴۰۳/۱۲/۱۶ ۰۸:۱۶
آدرس: www.dntips.ir
این مقاله به بررسی مرتب‌سازی عددی (Numeric Sorting) یا مرتب‌سازی طبیعی (Natural Sorting) در دات‌نت می‌پردازد. مرتب‌سازی عددی، رشته‌هایی را که شامل اعداد هستند، به ترتیبی منطقی و قابل فهم برای انسان مرتب می‌کند. به عنوان مثال، در مرتب‌سازی پیش‌فرض، رشته "v10" قبل از "v3" قرار می‌گیرد، زیرا مقایسه کاراکتر به کاراکتر انجام می‌شود. اما در مرتب‌سازی عددی، "v3" قبل از "v10" قرار می‌گیرد، زیرا عدد 3 از 10 کوچکتر است. در ادامه، سه روش برای پیاده‌سازی مرتب‌سازی عددی در دات‌نت معرفی می‌شوند:

1. استفاده از پرچم CompareOptions.NumericOrdering در دات‌نت 10

اگر از دات‌نت 10 (پس از اواسط نوامبر 2025) استفاده می‌کنید، می‌توانید از پرچم جدید CompareOptions.NumericOrdering که در این نسخه معرفی شده است، بهره ببرید. این پرچم به شما امکان می‌دهد تا یک مقایسه‌گر رشته سفارشی (StringComparer) ایجاد کنید که مرتب‌سازی عددی را انجام دهد.
var comparer = StringComparer.Create(CultureInfo.InvariantCulture, CompareOptions.NumericOrdering);

string[] tags = ["v1.0.11", "v1.0.2", "v1.0.9"];
Array.Sort(tags, comparer);

Console.WriteLine(string.Join(", ", tags)); // prints v1.0.2, v1.0.9, v1.0.11
نکته مهم این است که یک نمونه تکین عمومی (Public Singleton) از StringComparer برای مرتب‌سازی عددی وجود ندارد. دلیل این امر، امکان سفارشی‌سازی بیشتر نحوه برخورد با سایر کاراکترها (مانند حساسیت به حروف بزرگ و کوچک، نشانه‌های نگارشی و قوانین خاص فرهنگی) است. همچنین، پرچم CompareOptions.NumericOrdering با پرچم‌های CompareOptions.Ordinal و CompareOptions.OrdinalIgnoreCase سازگار نیست.

2. استفاده از بسته NaturalSort.Extension

اگر از نسخه‌های قدیمی‌تر دات‌نت استفاده می‌کنید یا دات‌نت 10 هنوز در دسترس نیست، می‌توانید از بسته NaturalSort.Extension استفاده کنید. این بسته، یک مقایسه‌گر طبیعی (NaturalSortComparer) ارائه می‌دهد که مقایسه‌گر دیگری را در بر می‌گیرد و به شما امکان می‌دهد تا مرتب‌سازی عددی را با سایر گزینه‌های مقایسه (از جمله ترتیبی) ترکیب کنید.
var comparer = new NaturalSortComparer(StringComparer.OrdinalIgnoreCase);

string[] tags = ["v1.0.11", "v1.0.2", "v1.0.9"];
Array.Sort(tags, comparer);

Console.WriteLine(string.Join(", ", tags)); // also prints v1.0.2, v1.0.9, v1.0.11

3. استفاده از رابط برنامه‌نویسی کاربردی ویندوز (Windows API)

اگر برنامه شما فقط برای ویندوز طراحی شده است و می‌خواهید رفتار مرتب‌سازی ویندوز اکسپلورر را شبیه‌سازی کنید، می‌توانید از تابع StrCmpLogicalW از طریق فراخوانی پلتفرم (P/Invoke) استفاده کنید. این تابع، مقایسه را به صورت غیر حساس به حروف بزرگ و کوچک انجام می‌دهد و سطح سفارشی‌سازی کمتری نسبت به دو روش قبلی دارد.
internal sealed class WindowsNumericStringComparer : IComparer<string?>
{
    public int Compare(string? x, string? y)
    {
        if (x == y) return 0;
        if (x == null) return -1;
        if (y == null) return 1;

        return NativeMethods.StrCmpLogical(x, y);
    }

    private static class NativeMethods
    {
        [DllImport("shlwapi.dll", EntryPoint = "StrCmpLogicalW", ExactSpelling = true, CharSet = CharSet.Unicode)]
        [DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
        public static extern int StrCmpLogical(string psz1, string psz2);
    }
}

var comparer = new WindowsNumericStringComparer();

string[] tags = ["v1.0.11", "v1.0.2", "v1.0.9"];
Array.Sort(tags, comparer);

Console.WriteLine(string.Join(", ", tags));
برای استفاده از این روش، می‌توانید از بسته Microsoft.Windows.CsWin32 برای تولید خودکار امضای فراخوانی پلتفرم یا از LibraryImportAttribute که مبتنی بر تولیدکننده‌های منبع است، استفاده کنید.


مشاهده مطلب اصلی