سردرگمی در ترتیب مرتب‌سازی طبیعی

اکثر مدیران فایل گرافیکی صرفاً بر اساس واژگان مرتب‌سازی نمی‌کنند، بلکه از مرتب‌سازی «طبیعی» استفاده می‌کنند. بلوک‌های اعداد در نام‌ها به عنوان اعداد تفسیر می‌شوند - بلوک بزرگتر اعداد برنده می‌شود، حتی اگر عکس آن به صورت الفبایی صادق باشد. ایده پشت مرتب‌سازی طبیعی: چیزی که مردم معمولاً می‌خواهند «۹ قبل از ۱۰»، «فصل ۲ قبل از فصل ۱۰» است - بدون اینکه مجبور به اضافه کردن صفرهای ابتدایی باشند.


جفت فایل‌های زیر به طور طبیعی به ترتیب صعودی به صورت زیر مرتب شده‌اند:

  • build-9e2.log
  • build-950.log

شگفت‌انگیز، اما قابل توضیح: رقم اول \(9\) از اولین مجموعه ارقام \(950\) کوچک‌تر است.

  • IMG_12113419_90.jpg
  • IMG_0554363070_90.jpg

عدد \(12113419\) کمتر از \(554363070\) است (عدد \(0\) از ابتدای آن حذف شده است).

  • temp_0C.txt
  • temp_2C.txt
  • temp_-3C.txt
  • temp_10C.txt
  • temp_-12C.txt

اعداد مقایسه شده عبارتند از \(0\) ، \(2\) ، \(3\) ، \(10\) ، \(12\) - علامت "-" بخشی از عدد محسوب نمی‌شود.

حتی «الفبایی» هم در سطح جهانی بدون ابهام نیست: حروف بزرگ، حروف ترکیبی مانند ä (آلمانی) یا حروف چند کاراکتری مانند ch (چکی) منجر به انواع مشروع می‌شوند. بنابراین «صرفاً الفبایی» وابسته به متن است. ویندوز اکسپلورر این را در تابع StrCmpLogicalW پیاده‌سازی می‌کند. در حالی که کد منبع آن (shlwapi.dll) اختصاصی است و عمومی نیست، پیاده‌سازی‌های مجددی، به عنوان مثال، از ReactOS وجود دارد.:

{
    TRACE("%s, %s\n", wine_dbgstr_w(str), wine_dbgstr_w(comp));
 
    if (!str || !comp)
        return 0;
 
    while (*str)
    {
        if (!*comp)
            return 1;
        else if (*str >= '0' && *str <= '9')
        {
            int str_value, comp_value;
 
            if (*comp < '0' || *comp > '9')
                return -1;
 
            /* Compare the numbers */
            StrToIntExW(str, 0, &str_value);
            StrToIntExW(comp, 0, &comp_value);
 
            if (str_value < comp_value)
                return -1;
            else if (str_value > comp_value)
                return 1;
 
            /* Skip */
            while (*str >= '0' && *str <= '9') str++;
            while (*comp >= '0' && *comp <= '9') comp++;
        }
        else if (*comp >= '0' && *comp <= '9')
            return 1;
        else
        {
            int diff = ChrCmpIW(*str, *comp);
            if (diff > 0)
                return 1;
            else if (diff < 0)
                return -1;
 
            str++;
            comp++;
        }
    }
 
    if (*comp)
      return -1;
 
    return 0;
}

گوگل درایو، وان درایو، کی‌دی‌ای و سایرین رفتار مرتب‌سازی مشابهی را نشان می‌دهند. ابزارهای رابط خط فرمان مانند ls و find با این حال، آنها به طور متفاوتی نسبت به مدیران فایل GUI مرتب‌سازی می‌کنند. معانی در نام فایل‌ها هستند، نه در API. اگر می‌خواهید نتایج بدون غافلگیری باشند، قراردادها را تعریف کنید: جداکننده‌های ثابت، اعداد پر شده و مدیریت واضح واحدها. سپس "الفبایی" دوباره قابل پیش‌بینی می‌شود.

بازگشت