সি # তে প্রাকৃতিক বাছাই অর্ডার


129

যে কারওরও ভাল সংস্থান আছে বা কোনও FileInfoঅ্যারের জন্য সি # তে প্রাকৃতিক অর্ডার সাজানোর নমুনা সরবরাহ করেন ? আমি IComparerআমার প্রকারে ইন্টারফেসটি বাস্তবায়ন করছি ।

উত্তর:


148

সবচেয়ে সহজ কাজটি হ'ল উইন্ডোজে অন্তর্নির্মিত ফাংশনটি কেবল পি / ইনভোক করা এবং এটি আপনার তুলনায় ফাংশন হিসাবে ব্যবহার করুন IComparer:

[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
private static extern int StrCmpLogicalW(string psz1, string psz2);

মাইকেল কাপলানের এখানে এই ফাংশনটি কীভাবে কাজ করে তার কয়েকটি উদাহরণ রয়েছে এবং ভিস্তার পক্ষে আরও স্বজ্ঞাতভাবে কাজ করার জন্য যে পরিবর্তনগুলি করা হয়েছিল। এই ফাংশনের প্লাস পাশটি হ'ল এটির উইন্ডোজের সংস্করণ যেমন চলবে তেমন আচরণ করবে তবে এর অর্থ এই যে এটি উইন্ডোজের সংস্করণগুলির মধ্যে পৃথক হয় সুতরাং আপনার এটি আপনার সমস্যা কিনা তা বিবেচনা করা উচিত।

সুতরাং একটি সম্পূর্ণ বাস্তবায়ন কিছু হবে:

[SuppressUnmanagedCodeSecurity]
internal static class SafeNativeMethods
{
    [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
    public static extern int StrCmpLogicalW(string psz1, string psz2);
}

public sealed class NaturalStringComparer : IComparer<string>
{
    public int Compare(string a, string b)
    {
        return SafeNativeMethods.StrCmpLogicalW(a, b);
    }
}

public sealed class NaturalFileInfoNameComparer : IComparer<FileInfo>
{
    public int Compare(FileInfo a, FileInfo b)
    {
        return SafeNativeMethods.StrCmpLogicalW(a.Name, b.Name);
    }
}

8
দুর্দান্ত উত্তর। ক্যাভেট: এটি উইন 2000 সহ কাজ করবে না, যারা এই কয়েকজন লোক এখনও অপারেটিং সিস্টেমে জিনিস চালাচ্ছেন। অন্যদিকে, কাপলানের ব্লগ এবং এমএসডিএন ডকুমেন্টেশনের মধ্যে একটি অনুরূপ ফাংশন তৈরি করার জন্য পর্যাপ্ত ইঙ্গিত রয়েছে।
ক্রিস চরবারুক

9
এই পোর্টেবল নয়, শুধুমাত্র Win32 কাজ করে, কিন্তু লিনাক্স / MacOS এর / সিলভারলাইট / উইন্ডোজ ফোন / মেট্রো কাজ করে না
linquize

20
@ লিঙ্কাইজ - তিনি বলেছিলেন .মনো নয়, তাই লিনাক্স / ওএসএক্স আসলেই উদ্বেগের বিষয় নয়। এই উত্তরটি পোস্ট করার সময় উইন্ডোজ ফোন / মেট্রোটির অস্তিত্ব ছিল না ২০০৮ সালে। এবং সিলভারলাইটে আপনি কতবার ফাইল অপারেশন করেন? সুতরাং ওপি এবং সম্ভবত অন্যান্য বেশিরভাগ লোকের পক্ষে এটি একটি উপযুক্ত উত্তর ছিল। যাই হোক না কেন, আপনি একটি ভাল উত্তর প্রদান করতে নির্দ্বিধায়; এই সাইটটি কিভাবে কাজ করে।
গ্রেগ বিচ

6
এর অর্থ এই নয় যে আসল উত্তরটি ভুল ছিল। আমি কেবলমাত্র আপ-টু-ডেট তথ্য দিয়ে অতিরিক্ত তথ্য যুক্ত করেছি
15-10 টায় লিকুইজ করুন

2
এফওয়াইআই, যদি আপনি Comparer<T>প্রয়োগের পরিবর্তে উত্তরাধিকার সূত্রে পান, তবে আপনি (অ-জেনেরিক) ইন্টারফেসের IComparer<T>একটি অন্তর্নির্মিত বাস্তবায়ন পাবেন IComparerযা আপনার জেনেরিক পদ্ধতিটিকে কল করে, পরিবর্তে এটির ব্যবহার করে এমন এপিআইগুলিতে ব্যবহার করার জন্য। এটি মূলত এটিও বিনামূল্যে: কেবলমাত্র "আমি" মুছুন এবং এতে পরিবর্তন public int Compare(...)করুন public override int Compare(...)। একই জন্য IEqualityComparer<T>এবং EqualityComparer<T>
জো অ্যামেন্টা

75

ভেবেছি আমি এটিতে যুক্ত করব (সবচেয়ে সংক্ষিপ্ত সমাধানের সাথে আমি খুঁজে পেতে পারি):

public static IOrderedEnumerable<T> OrderByAlphaNumeric<T>(this IEnumerable<T> source, Func<T, string> selector)
{
    int max = source
        .SelectMany(i => Regex.Matches(selector(i), @"\d+").Cast<Match>().Select(m => (int?)m.Value.Length))
        .Max() ?? 0;

    return source.OrderBy(i => Regex.Replace(selector(i), @"\d+", m => m.Value.PadLeft(max, '0')));
}

উপরের স্ট্রিংয়ের কোনও সংখ্যাকে সমস্ত স্ট্রিংয়ের সর্বাধিক দৈর্ঘ্যের প্যাড দেয় এবং ফলস্বরূপ স্ট্রিংটি সাজানোর জন্য ব্যবহার করে।

( int?) এ castালাই হ'ল কোনও সংখ্যা ছাড়াই স্ট্রিং সংগ্রহের অনুমতি দেওয়ার জন্য ( .Max()একটি খালি গণনার ছোঁড়া একটিতে InvalidOperationException)।


1
+1 কেবল এটিই সর্বাধিক সংক্ষিপ্ত নয় যা আমি দেখেছি। গৃহীত উত্তর ব্যতীত তবে আমি সেইটিকে মেশিনের নির্ভরতার কারণে ব্যবহার করতে পারি না। এটি প্রায় 35 সেকেন্ডে 4 মিলিয়নেরও বেশি মান বাছাই করে।
জিন এস

4
এটি উভয়ই সুন্দর এবং পড়া অসম্ভব। আমি ধরে নিয়েছি যে লিনাকের সুবিধাগুলির অর্থ হবে (কমপক্ষে) সেরা গড় এবং সেরা ক্ষেত্রে পারফরম্যান্স, সুতরাং আমি মনে করি আমি এটি নিয়ে যাচ্ছি go স্বচ্ছতার অভাব থাকা সত্ত্বেও। অনেক ধন্যবাদ @ ম্যাথিউ হার্সলে
ইয়ান গ্রেনার

1
এটি খুব ভাল, তবে নির্দিষ্ট দশমিক সংখ্যার জন্য একটি বাগ রয়েছে, আমার উদাহরণটি ছিল কে 8.11 বনাম কে 8.2 বাছাই করা। এটির সমাধানের জন্য আমি নিম্নলিখিত রেজেক্স প্রয়োগ করেছি: \ d + ([\।,]] D)?
ডেভাজেরো

2
আপনি এই কোডটি প্যাড করার সময় দ্বিতীয় গ্রুপের (দশমিক পয়েন্ট + দশমিক) দৈর্ঘ্যও বিবেচনায় নিতে হবে এম.ভালিউ.প্যাডলফ্ট (সর্বাধিক, '0')
ডিভজারো

3
আমি মনে করি আপনি .DefaultIfEmpty().Max()কাস্টিংয়ের পরিবর্তে ব্যবহার করতে পারেন int?। এছাড়াও এটি source.ToList()গণনাকারীদের পুনরায় গণনা এড়াতে একটি করার উপযুক্ত।
তেজয়

30

বিদ্যমান বাস্তবায়নগুলির কোনওটিই দুর্দান্ত লাগেনি তাই আমি নিজের লেখা। ফলাফলগুলি উইন্ডোজ এক্সপ্লোরার (উইন্ডোজ 7/8) এর আধুনিক সংস্করণগুলির দ্বারা ব্যবহৃত বাছাইয়ের মতো প্রায় একই are আমি কেবলমাত্র পার্থক্যগুলি দেখেছি 1) যদিও উইন্ডোজ যে কোনও দৈর্ঘ্যের সংখ্যা হ্যান্ডেল করত (যেমন এক্সপি) এখন এটি 19 সংখ্যায় সীমাবদ্ধ - খনি সীমাহীন, ২) উইন্ডোজ ইউনিকোড ডিজিটের নির্দিষ্ট সেটগুলির সাথে অসামঞ্জস্যপূর্ণ ফলাফল দেয় - খনি কাজ করে জরিমানা (যদিও এটি সারোগেট জোড়গুলির সাথে সংখ্যার সাথে তুলনা করে না; উইন্ডোজও করে না) এবং 3) খনি বিভিন্ন ধরণের অ প্রাইমারি সাজানো ওজনের বিভিন্ন ধরণের পার্থক্য করতে পারে না (যেমন "e-1é" বনাম " é1e- "- সংখ্যার আগে এবং পরে বিভাগগুলিতে ডায়াক্রিটিক এবং বিরামচিহ্ন ওজনের পার্থক্য রয়েছে)।

public static int CompareNatural(string strA, string strB) {
    return CompareNatural(strA, strB, CultureInfo.CurrentCulture, CompareOptions.IgnoreCase);
}

public static int CompareNatural(string strA, string strB, CultureInfo culture, CompareOptions options) {
    CompareInfo cmp = culture.CompareInfo;
    int iA = 0;
    int iB = 0;
    int softResult = 0;
    int softResultWeight = 0;
    while (iA < strA.Length && iB < strB.Length) {
        bool isDigitA = Char.IsDigit(strA[iA]);
        bool isDigitB = Char.IsDigit(strB[iB]);
        if (isDigitA != isDigitB) {
            return cmp.Compare(strA, iA, strB, iB, options);
        }
        else if (!isDigitA && !isDigitB) {
            int jA = iA + 1;
            int jB = iB + 1;
            while (jA < strA.Length && !Char.IsDigit(strA[jA])) jA++;
            while (jB < strB.Length && !Char.IsDigit(strB[jB])) jB++;
            int cmpResult = cmp.Compare(strA, iA, jA - iA, strB, iB, jB - iB, options);
            if (cmpResult != 0) {
                // Certain strings may be considered different due to "soft" differences that are
                // ignored if more significant differences follow, e.g. a hyphen only affects the
                // comparison if no other differences follow
                string sectionA = strA.Substring(iA, jA - iA);
                string sectionB = strB.Substring(iB, jB - iB);
                if (cmp.Compare(sectionA + "1", sectionB + "2", options) ==
                    cmp.Compare(sectionA + "2", sectionB + "1", options))
                {
                    return cmp.Compare(strA, iA, strB, iB, options);
                }
                else if (softResultWeight < 1) {
                    softResult = cmpResult;
                    softResultWeight = 1;
                }
            }
            iA = jA;
            iB = jB;
        }
        else {
            char zeroA = (char)(strA[iA] - (int)Char.GetNumericValue(strA[iA]));
            char zeroB = (char)(strB[iB] - (int)Char.GetNumericValue(strB[iB]));
            int jA = iA;
            int jB = iB;
            while (jA < strA.Length && strA[jA] == zeroA) jA++;
            while (jB < strB.Length && strB[jB] == zeroB) jB++;
            int resultIfSameLength = 0;
            do {
                isDigitA = jA < strA.Length && Char.IsDigit(strA[jA]);
                isDigitB = jB < strB.Length && Char.IsDigit(strB[jB]);
                int numA = isDigitA ? (int)Char.GetNumericValue(strA[jA]) : 0;
                int numB = isDigitB ? (int)Char.GetNumericValue(strB[jB]) : 0;
                if (isDigitA && (char)(strA[jA] - numA) != zeroA) isDigitA = false;
                if (isDigitB && (char)(strB[jB] - numB) != zeroB) isDigitB = false;
                if (isDigitA && isDigitB) {
                    if (numA != numB && resultIfSameLength == 0) {
                        resultIfSameLength = numA < numB ? -1 : 1;
                    }
                    jA++;
                    jB++;
                }
            }
            while (isDigitA && isDigitB);
            if (isDigitA != isDigitB) {
                // One number has more digits than the other (ignoring leading zeros) - the longer
                // number must be larger
                return isDigitA ? 1 : -1;
            }
            else if (resultIfSameLength != 0) {
                // Both numbers are the same length (ignoring leading zeros) and at least one of
                // the digits differed - the first difference determines the result
                return resultIfSameLength;
            }
            int lA = jA - iA;
            int lB = jB - iB;
            if (lA != lB) {
                // Both numbers are equivalent but one has more leading zeros
                return lA > lB ? -1 : 1;
            }
            else if (zeroA != zeroB && softResultWeight < 2) {
                softResult = cmp.Compare(strA, iA, 1, strB, iB, 1, options);
                softResultWeight = 2;
            }
            iA = jA;
            iB = jB;
        }
    }
    if (iA < strA.Length || iB < strB.Length) {
        return iA < strA.Length ? 1 : -1;
    }
    else if (softResult != 0) {
        return softResult;
    }
    return 0;
}

স্বাক্ষরটি ডেলিগেটের সাথে মেলে Comparison<string>:

string[] files = Directory.GetFiles(@"C:\");
Array.Sort(files, CompareNatural);

ব্যবহারের জন্য এখানে একটি মোড়কের ক্লাস রয়েছে IComparer<string>:

public class CustomComparer<T> : IComparer<T> {
    private Comparison<T> _comparison;

    public CustomComparer(Comparison<T> comparison) {
        _comparison = comparison;
    }

    public int Compare(T x, T y) {
        return _comparison(x, y);
    }
}

উদাহরণ:

string[] files = Directory.EnumerateFiles(@"C:\")
    .OrderBy(f => f, new CustomComparer<string>(CompareNatural))
    .ToArray();

আমি পরীক্ষার জন্য ব্যবহার করি এমন ফাইলের নামগুলির একটি ভাল সেট এখানে দেওয়া হয়েছে:

Func<string, string> expand = (s) => { int o; while ((o = s.IndexOf('\\')) != -1) { int p = o + 1;
    int z = 1; while (s[p] == '0') { z++; p++; } int c = Int32.Parse(s.Substring(p, z));
    s = s.Substring(0, o) + new string(s[o - 1], c) + s.Substring(p + z); } return s; };
string encodedFileNames =
    "KDEqLW4xMiotbjEzKjAwMDFcMDY2KjAwMlwwMTcqMDA5XDAxNyowMlwwMTcqMDlcMDE3KjEhKjEtISox" +
    "LWEqMS4yNT8xLjI1KjEuNT8xLjUqMSoxXDAxNyoxXDAxOCoxXDAxOSoxXDA2NioxXDA2NyoxYSoyXDAx" +
    "NyoyXDAxOCo5XDAxNyo5XDAxOCo5XDA2Nio9MSphMDAxdGVzdDAxKmEwMDF0ZXN0aW5nYTBcMzEqYTAw" +
    "Mj9hMDAyIGE/YTAwMiBhKmEwMDIqYTAwMmE/YTAwMmEqYTAxdGVzdGluZ2EwMDEqYTAxdnNmcyphMSph" +
    "MWEqYTF6KmEyKmIwMDAzcTYqYjAwM3E0KmIwM3E1KmMtZSpjZCpjZipmIDEqZipnP2cgMT9oLW4qaG8t" +
    "bipJKmljZS1jcmVhbT9pY2VjcmVhbT9pY2VjcmVhbS0/ajBcNDE/ajAwMWE/ajAxP2shKmsnKmstKmsx" +
    "KmthKmxpc3QqbTAwMDNhMDA1YSptMDAzYTAwMDVhKm0wMDNhMDA1Km0wMDNhMDA1YSpuMTIqbjEzKm8t" +
    "bjAxMypvLW4xMipvLW40P28tbjQhP28tbjR6P28tbjlhLWI1Km8tbjlhYjUqb24wMTMqb24xMipvbjQ/" +
    "b240IT9vbjR6P29uOWEtYjUqb245YWI1Km/CrW4wMTMqb8KtbjEyKnAwMCpwMDEqcDAxwr0hKnAwMcK9" +
    "KnAwMcK9YSpwMDHCvcK+KnAwMipwMMK9KnEtbjAxMypxLW4xMipxbjAxMypxbjEyKnItMDAhKnItMDAh" +
    "NSpyLTAwIe+8lSpyLTAwYSpyLe+8kFwxIS01KnIt77yQXDEhLe+8lSpyLe+8kFwxISpyLe+8kFwxITUq" +
    "ci3vvJBcMSHvvJUqci3vvJBcMWEqci3vvJBcMyE1KnIwMCEqcjAwLTUqcjAwLjUqcjAwNSpyMDBhKnIw" +
    "NSpyMDYqcjQqcjUqctmg2aYqctmkKnLZpSpy27Dbtipy27Qqctu1KnLfgN+GKnLfhCpy34UqcuClpuCl" +
    "rCpy4KWqKnLgpasqcuCnpuCnrCpy4KeqKnLgp6sqcuCppuCprCpy4KmqKnLgqasqcuCrpuCrrCpy4Kuq" +
    "KnLgq6sqcuCtpuCtrCpy4K2qKnLgrasqcuCvpuCvrCpy4K+qKnLgr6sqcuCxpuCxrCpy4LGqKnLgsasq" +
    "cuCzpuCzrCpy4LOqKnLgs6sqcuC1puC1rCpy4LWqKnLgtasqcuC5kOC5lipy4LmUKnLguZUqcuC7kOC7" +
    "lipy4LuUKnLgu5UqcuC8oOC8pipy4LykKnLgvKUqcuGBgOGBhipy4YGEKnLhgYUqcuGCkOGClipy4YKU" +
    "KnLhgpUqcuGfoOGfpipy4Z+kKnLhn6UqcuGgkOGglipy4aCUKnLhoJUqcuGlhuGljCpy4aWKKnLhpYsq" +
    "cuGnkOGnlipy4aeUKnLhp5UqcuGtkOGtlipy4a2UKnLhrZUqcuGusOGutipy4a60KnLhrrUqcuGxgOGx" +
    "hipy4bGEKnLhsYUqcuGxkOGxlipy4bGUKnLhsZUqcuqYoFwx6pilKnLqmKDqmKUqcuqYoOqYpipy6pik" +
    "KnLqmKUqcuqjkOqjlipy6qOUKnLqo5UqcuqkgOqkhipy6qSEKnLqpIUqcuqpkOqplipy6qmUKnLqqZUq" +
    "cvCQkqAqcvCQkqUqcvCdn5gqcvCdn50qcu+8kFwxISpy77yQXDEt77yVKnLvvJBcMS7vvJUqcu+8kFwx" +
    "YSpy77yQXDHqmKUqcu+8kFwx77yO77yVKnLvvJBcMe+8lSpy77yQ77yVKnLvvJDvvJYqcu+8lCpy77yV" +
    "KnNpKnPEsSp0ZXN02aIqdGVzdNmi2aAqdGVzdNmjKnVBZS0qdWFlKnViZS0qdUJlKnVjZS0xw6kqdWNl" +
    "McOpLSp1Y2Uxw6kqdWPDqS0xZSp1Y8OpMWUtKnVjw6kxZSp3ZWlhMSp3ZWlhMip3ZWlzczEqd2Vpc3My" +
    "KndlaXoxKndlaXoyKndlacOfMSp3ZWnDnzIqeSBhMyp5IGE0KnknYTMqeSdhNCp5K2EzKnkrYTQqeS1h" +
    "Myp5LWE0KnlhMyp5YTQqej96IDA1MD96IDIxP3ohMjE/ejIwP3oyMj96YTIxP3rCqTIxP1sxKl8xKsKt" +
    "bjEyKsKtbjEzKsSwKg==";
string[] fileNames = Encoding.UTF8.GetString(Convert.FromBase64String(encodedFileNames))
    .Replace("*", ".txt?").Split(new[] { "?" }, StringSplitOptions.RemoveEmptyEntries)
    .Select(n => expand(n)).ToArray();

অঙ্ক বিভাগগুলি বিভাগ-ভিত্তিক তুলনা করা দরকার, অর্থাত্ 'abc12b' 'abc123' এর চেয়ে কম হওয়া উচিত।
সাউদার

আপনি নিম্নলিখিত ডেটা চেষ্টা করতে পারেন: পাবলিক স্ট্রিং [] ফাইলের নামগুলি = {"-abc12.txt", " abc12.txt", "1abc_2.txt", "a0000012.txt", "a0000012c.txt", "a000012.txt" , "a000012b.txt", "a012.txt", "a0000102.txt", "abc1_2.txt", "abc12 .txt", "abc12b.txt", "abc123.txt", "abccde.txt", " b0000.txt "," b00001.txt "," b0001.txt "," b001.txt "," c0000.txt "," c0000c.txt "," c00001.txt "," c000b.txt "," d0 " 20.2b.txt "," d0.1000c.txt "," d0.2000y.txt "," d0.20000.2b.txt ","
সাউসার

@ XichenLi ভাল পরীক্ষার ক্ষেত্রে ধন্যবাদ। আপনি যদি উইন্ডোজ এক্সপ্লোরারকে এই ফাইলগুলি বাছাই করতে দেন তবে আপনি উইন্ডোজের কোন সংস্করণ ব্যবহার করছেন তার উপর নির্ভর করে আপনি বিভিন্ন ফলাফল পাবেন। আমার কোডগুলি সেই নামগুলি সার্ভার 2003 (এবং সম্ভবত এক্সপি) অনুসারে বাছাই করে তবে উইন্ডোজ ৮ এর চেয়ে পৃথক I আমি যদি কোনও সুযোগ পান তবে আমি কীভাবে উইন্ডোজ 8 এটি করছে তা নির্ধারণ করার চেষ্টা করব এবং আমার কোডটি আপডেট করব।
জেডি

2
বাগ আছে।
সীমার

3
দুর্দান্ত সমাধান! আমি যখন প্রায় 10,000 টি ফাইল সহ একটি সাধারণ দৃশ্যে এটি বেঞ্চমার্ক করেছিলাম, তখন এটি ম্যাথিউয়ের রেজেক্স উদাহরণের চেয়ে দ্রুত এবং স্ট্রিম্পলজিকাল ডাব্লু () এর মতোই পারফরম্যান্সের তুলনায় দ্রুত ছিল। উপরের কোডটিতে একটি ছোট্ট বাগ রয়েছে: "যখন (টিআরএ [জেএ] == জিরোএ) জেএ ++;" এবং "যখন (টিআরবি [জেবি] == শূন্যবি) জেবি ++;" "যখন (জে <স্ট্রিয়া। দৈর্ঘ্য && strA [জেএ] == শূন্যআর) জেএ ++ হওয়া উচিত" এবং "যখন (jB <strB.Length && strB [jB] == জিরোবি) জেবি ++;"। অন্যথায়, কেবল শূন্যযুক্ত স্ট্রিং একটি ব্যতিক্রম ছুঁড়ে দেবে।
কুরোকি

22

লিনাক অর্ডারবাইয়ের জন্য খাঁটি সি # দ্রবণ:

http://zootfroot.blogspot.com/2009/09/natural-sort-compare-with-linq-orderby.html

public class NaturalSortComparer<T> : IComparer<string>, IDisposable
{
    private bool isAscending;

    public NaturalSortComparer(bool inAscendingOrder = true)
    {
        this.isAscending = inAscendingOrder;
    }

    #region IComparer<string> Members

    public int Compare(string x, string y)
    {
        throw new NotImplementedException();
    }

    #endregion

    #region IComparer<string> Members

    int IComparer<string>.Compare(string x, string y)
    {
        if (x == y)
            return 0;

        string[] x1, y1;

        if (!table.TryGetValue(x, out x1))
        {
            x1 = Regex.Split(x.Replace(" ", ""), "([0-9]+)");
            table.Add(x, x1);
        }

        if (!table.TryGetValue(y, out y1))
        {
            y1 = Regex.Split(y.Replace(" ", ""), "([0-9]+)");
            table.Add(y, y1);
        }

        int returnVal;

        for (int i = 0; i < x1.Length && i < y1.Length; i++)
        {
            if (x1[i] != y1[i])
            {
                returnVal = PartCompare(x1[i], y1[i]);
                return isAscending ? returnVal : -returnVal;
            }
        }

        if (y1.Length > x1.Length)
        {
            returnVal = 1;
        }
        else if (x1.Length > y1.Length)
        { 
            returnVal = -1; 
        }
        else
        {
            returnVal = 0;
        }

        return isAscending ? returnVal : -returnVal;
    }

    private static int PartCompare(string left, string right)
    {
        int x, y;
        if (!int.TryParse(left, out x))
            return left.CompareTo(right);

        if (!int.TryParse(right, out y))
            return left.CompareTo(right);

        return x.CompareTo(y);
    }

    #endregion

    private Dictionary<string, string[]> table = new Dictionary<string, string[]>();

    public void Dispose()
    {
        table.Clear();
        table = null;
    }
}

2
এই কোডটি শেষ পর্যন্ত কোডেপ্রজেক্ট / কেবি / রেসিপেস / ন্যাচারালকম্পের.এএসপিএক্স (যা লিনকিউ -ভিত্তিক নয়) থেকে।
mhenry1384

2
ব্লগ পোস্টটি জাস্টিন জোন্সকে ( কডেপ্রজেক্ট / কেবি / স্ট্রিং / ন্যাচারালসোর্টকম্পার.অএসপিএক্স ) কৃতিত্ব আইসকম্পারের জন্য, পাস্কাল গণায়কে নয়।
জেমস ম্যাককর্ম্যাক

1
মাইনর নোট, এই সমাধানটি এমন জায়গাগুলি উপেক্ষা করে যা উইন্ডোজগুলির মতো নয় এবং নীচে ম্যাথু হর্সলির কোডের মতো ভাল নয়। সুতরাং আপনি উদাহরণস্বরূপ 'স্ট্রিং 01' 'স্ট্রিং 01' 'স্ট্রিং 02' 'স্ট্রিং02' পেতে পারেন (যা দেখতে কুৎসিত দেখাচ্ছে)। আপনি যদি স্পেসগুলি বাদ দিয়ে সরিয়ে ফেলেন তবে এটি স্ট্রিংগুলি পিছনের দিকে অর্ডার করে অর্থাৎ 'স্ট্রিং 01' 'স্ট্রিং 01' এর আগে আসে যা গ্রহণযোগ্য বা নাও হতে পারে।
মাইকেল পার্কার

এটি ঠিকানাগুলির জন্য কাজ করেছে, যেমন "1 স্মিথ আরডি", "10 স্মিথ আরডি", "2 স্মিথ আরডি" ইত্যাদি - প্রাকৃতিকভাবে সাজানো orted হ্যাঁ! সুন্দর!
পাইওটর কুলা

যাইহোক, আমি লক্ষ্য করেছি (এবং সেই লিঙ্কযুক্ত পৃষ্ঠায় মন্তব্যগুলিও ইঙ্গিত করে বলে মনে হচ্ছে) টাইপ আর্গুমেন্ট <T> সম্পূর্ণ অপ্রয়োজনীয়।
jv-dev

18

ম্যাথিউস হর্সলেইসের উত্তর হ'ল দ্রুততম পদ্ধতি যা আপনার প্রোগ্রামটির উইন্ডোর কোন সংস্করণ চলছে তার উপর নির্ভর করে আচরণ পরিবর্তন করে না। যাইহোক, একবার রিজেক্স তৈরি করে এবং রিজেক্সপশন ব্যবহার করে এটি আরও দ্রুত হতে পারে omp সংকলিত। আমি একটি স্ট্রিং তুলক সন্নিবেশ করার বিকল্পটিও যুক্ত করেছি যাতে প্রয়োজনে আপনি কেসটিকে উপেক্ষা করতে পারেন, এবং পাঠ্যতা কিছুটা উন্নত করতে পারেন।

    public static IEnumerable<T> OrderByNatural<T>(this IEnumerable<T> items, Func<T, string> selector, StringComparer stringComparer = null)
    {
        var regex = new Regex(@"\d+", RegexOptions.Compiled);

        int maxDigits = items
                      .SelectMany(i => regex.Matches(selector(i)).Cast<Match>().Select(digitChunk => (int?)digitChunk.Value.Length))
                      .Max() ?? 0;

        return items.OrderBy(i => regex.Replace(selector(i), match => match.Value.PadLeft(maxDigits, '0')), stringComparer ?? StringComparer.CurrentCulture);
    }

দ্বারা ব্যবহার করুন

var sortedEmployees = employees.OrderByNatural(emp => emp.Name);

ডিফল্ট। নেট স্ট্রিং তুলনার জন্য 300ms এর তুলনায় 100,000 স্ট্রিংগুলি সাজানোর জন্য 450 মিমি লাগে pretty বেশ দ্রুত!


2
এটি উপরের
রিটটি পড়ার মতো

16

আমার সমাধান:

void Main()
{
    new[] {"a4","a3","a2","a10","b5","b4","b400","1","C1d","c1d2"}.OrderBy(x => x, new NaturalStringComparer()).Dump();
}

public class NaturalStringComparer : IComparer<string>
{
    private static readonly Regex _re = new Regex(@"(?<=\D)(?=\d)|(?<=\d)(?=\D)", RegexOptions.Compiled);

    public int Compare(string x, string y)
    {
        x = x.ToLower();
        y = y.ToLower();
        if(string.Compare(x, 0, y, 0, Math.Min(x.Length, y.Length)) == 0)
        {
            if(x.Length == y.Length) return 0;
            return x.Length < y.Length ? -1 : 1;
        }
        var a = _re.Split(x);
        var b = _re.Split(y);
        int i = 0;
        while(true)
        {
            int r = PartCompare(a[i], b[i]);
            if(r != 0) return r;
            ++i;
        }
    }

    private static int PartCompare(string x, string y)
    {
        int a, b;
        if(int.TryParse(x, out a) && int.TryParse(y, out b))
            return a.CompareTo(b);
        return x.CompareTo(y);
    }
}

ফলাফল:

1
a2
a3
a4
a10
b4
b5
b400
C1d
c1d2

আমি এটা পছন্দ করি. এটি বোঝা সহজ এবং লিনকের প্রয়োজন নেই।

11

আপনার সতর্কতা অবলম্বন করা দরকার - আমি অস্পষ্টভাবে মনে করি স্ট্রিম্পলজিক্যাল ডাব্লু, বা এর মতো অন্য কিছু পড়ার বিষয়টি কঠোরভাবে অস্থির ছিল না এবং আমি লক্ষ্য করেছি যে তুলনা ফাংশনটি যদি এই নিয়মটি ভেঙে দেয় তবে নেট কখনও কখনও অসীম লুপগুলিতে আটকে যায় ET

একটি অস্থায়ী তুলনা সর্বদা জানায় যে একটি <সি যদি <বি এবং বি <সি হয়। একটি ক্রিয়াকলাপ রয়েছে যা প্রাকৃতিক সাজানোর অর্ডার তুলনা করে যা সর্বদা সেই মানদণ্ডকে মেটায় না, তবে আমি এটি স্মরণ করতে পারি না এটি স্ট্রিম্পলজিক্যাল ডাব্লু বা অন্য কিছু।


আপনার কাছে এই বক্তব্যের কোনও প্রমাণ আছে? আশেপাশে গুগল করার পরে, আমি এটির সত্য যে কোনও ইঙ্গিত পাই না।
mhenry1384

1
আমি StrCmpLogicalW এর সাথে সেই অসীম লুপগুলি অনুভব করেছি।
THD


ভিজ্যুয়াল স্টুডিও প্রতিক্রিয়া আইটেমটি 236900 আর বিদ্যমান নেই, তবে এখানে আরও একটি আধুনিক তথ্য রয়েছে যা সমস্যার বিষয়টি নিশ্চিত করে: সংযুক্ত.মাইক্রোসফট / ভিজ্যুয়াল স্টুডিও / ফেডব্যাক / ডেটেল / 774540/… এটি একটি কাজের আশপাশ দেয়: CultureInfoএকটি সম্পত্তি আছে CompareInfo, এবং যে বস্তুটি এটি প্রত্যাবর্তন করে তা আপনাকে SortKeyবস্তু সরবরাহ করতে পারে। এগুলি, পরিবর্তে, তুলনা করা যায় এবং ট্রানজিটিভিটির গ্যারান্টি দেয়।
জোনাথন গিলবার্ট

9

এটি আলফা এবং সংখ্যাসূচক উভয় অক্ষরযুক্ত একটি স্ট্রিং সাজানোর জন্য আমার কোড।

প্রথমত, এই এক্সটেনশন পদ্ধতি:

public static IEnumerable<string> AlphanumericSort(this IEnumerable<string> me)
{
    return me.OrderBy(x => Regex.Replace(x, @"\d+", m => m.Value.PadLeft(50, '0')));
}

তারপরে, এটি আপনার কোডের যে কোনও জায়গায় এটি ব্যবহার করুন:

List<string> test = new List<string>() { "The 1st", "The 12th", "The 2nd" };
test = test.AlphanumericSort();

এটি কিভাবে কাজ করে ? শূন্যগুলির সাথে প্রতিস্থাপন করে:

  Original  | Regex Replace |      The      |   Returned
    List    | Apply PadLeft |    Sorting    |     List
            |               |               |
 "The 1st"  |  "The 001st"  |  "The 001st"  |  "The 1st"
 "The 12th" |  "The 012th"  |  "The 002nd"  |  "The 2nd"
 "The 2nd"  |  "The 002nd"  |  "The 012th"  |  "The 12th"

একক সংখ্যা সহ কাজ করে:

 Alphabetical Sorting | Alphanumeric Sorting
                      |
 "Page 21, Line 42"   | "Page 3, Line 7"
 "Page 21, Line 5"    | "Page 3, Line 32"
 "Page 3, Line 32"    | "Page 21, Line 5"
 "Page 3, Line 7"     | "Page 21, Line 42"

আশা করি এটি সাহায্য করবে।


6

যোগ করা হচ্ছে গ্রেগ মধ্যে Beech এর উত্তর (কারণ আমি শুধু যে অনুসন্ধানের জন্য করে থাকেন), আপনি Linq থেকে এই ব্যবহার করতে আপনি ব্যবহার করতে পারেন চান তাহলে OrderByযে সময় লাগে IComparer। উদাহরণ:

var items = new List<MyItem>();

// fill items

var sorted = items.OrderBy(item => item.Name, new NaturalStringComparer());

2

এখানে অপেক্ষাকৃত সহজ উদাহরণ যা পি / ইনভোক ব্যবহার করে না এবং কার্যকর করার সময় কোনও বরাদ্দ এড়ানো যায়।

internal sealed class NumericStringComparer : IComparer<string>
{
    public static NumericStringComparer Instance { get; } = new NumericStringComparer();

    public int Compare(string x, string y)
    {
        // sort nulls to the start
        if (x == null)
            return y == null ? 0 : -1;
        if (y == null)
            return 1;

        var ix = 0;
        var iy = 0;

        while (true)
        {
            // sort shorter strings to the start
            if (ix >= x.Length)
                return iy >= y.Length ? 0 : -1;
            if (iy >= y.Length)
                return 1;

            var cx = x[ix];
            var cy = y[iy];

            int result;
            if (char.IsDigit(cx) && char.IsDigit(cy))
                result = CompareInteger(x, y, ref ix, ref iy);
            else
                result = cx.CompareTo(y[iy]);

            if (result != 0)
                return result;

            ix++;
            iy++;
        }
    }

    private static int CompareInteger(string x, string y, ref int ix, ref int iy)
    {
        var lx = GetNumLength(x, ix);
        var ly = GetNumLength(y, iy);

        // shorter number first (note, doesn't handle leading zeroes)
        if (lx != ly)
            return lx.CompareTo(ly);

        for (var i = 0; i < lx; i++)
        {
            var result = x[ix++].CompareTo(y[iy++]);
            if (result != 0)
                return result;
        }

        return 0;
    }

    private static int GetNumLength(string s, int i)
    {
        var length = 0;
        while (i < s.Length && char.IsDigit(s[i++]))
            length++;
        return length;
    }
}

এটি নেতৃস্থানীয় শূন্যগুলি উপেক্ষা করে না, তাই 01পরে আসে 2

সংশ্লিষ্ট ইউনিট পরীক্ষা:

public class NumericStringComparerTests
{
    [Fact]
    public void OrdersCorrectly()
    {
        AssertEqual("", "");
        AssertEqual(null, null);
        AssertEqual("Hello", "Hello");
        AssertEqual("Hello123", "Hello123");
        AssertEqual("123", "123");
        AssertEqual("123Hello", "123Hello");

        AssertOrdered("", "Hello");
        AssertOrdered(null, "Hello");
        AssertOrdered("Hello", "Hello1");
        AssertOrdered("Hello123", "Hello124");
        AssertOrdered("Hello123", "Hello133");
        AssertOrdered("Hello123", "Hello223");
        AssertOrdered("123", "124");
        AssertOrdered("123", "133");
        AssertOrdered("123", "223");
        AssertOrdered("123", "1234");
        AssertOrdered("123", "2345");
        AssertOrdered("0", "1");
        AssertOrdered("123Hello", "124Hello");
        AssertOrdered("123Hello", "133Hello");
        AssertOrdered("123Hello", "223Hello");
        AssertOrdered("123Hello", "1234Hello");
    }

    private static void AssertEqual(string x, string y)
    {
        Assert.Equal(0, NumericStringComparer.Instance.Compare(x, y));
        Assert.Equal(0, NumericStringComparer.Instance.Compare(y, x));
    }

    private static void AssertOrdered(string x, string y)
    {
        Assert.Equal(-1, NumericStringComparer.Instance.Compare(x, y));
        Assert.Equal( 1, NumericStringComparer.Instance.Compare(y, x));
    }
}

2

আমি আসলে এটি একটি এক্সটেনশন পদ্ধতি হিসাবে প্রয়োগ করেছি StringComparerযাতে আপনি উদাহরণস্বরূপ করতে পারেন:

  • StringComparer.CurrentCulture.WithNaturalSort() অথবা
  • StringComparer.OrdinalIgnoreCase.WithNaturalSort()

ফল IComparer<string>পছন্দ সব জায়গায় ব্যবহার করা যেতে পারে OrderBy, OrderByDescending, ThenBy, ThenByDescending, SortedSet<string>, ইত্যাদি আর আপনি যা করতে পারেন এখনও সহজেই বদলাতে ক্ষেত্রে সংবেদনশীলতা, সংস্কৃতি, ইত্যাদি

বাস্তবায়ন মোটামুটি তুচ্ছ এবং এটি বড় সিকোয়েন্সগুলিতেও বেশ ভাল সঞ্চালন করা উচিত।


আমি এটিকে একটি ক্ষুদ্র নুগেট প্যাকেজ হিসাবে প্রকাশ করেছি , সুতরাং আপনি কেবল এটি করতে পারেন:

Install-Package NaturalSort.Extension

এক্সএমএল ডকুমেন্টেশন মন্তব্য সহ কোড পরীক্ষাগুলির স্যুট ন্যাচারালসোর্ট E এক্সটেনশন গিটহাব সংগ্রহস্থলীতে উপলভ্য ।


পুরো কোডটি হ'ল (আপনি যদি এখনও সি # 7 ব্যবহার করতে না পারেন তবে কেবল নিউগেট প্যাকেজটি ইনস্টল করুন):

public static class StringComparerNaturalSortExtension
{
    public static IComparer<string> WithNaturalSort(this StringComparer stringComparer) => new NaturalSortComparer(stringComparer);

    private class NaturalSortComparer : IComparer<string>
    {
        public NaturalSortComparer(StringComparer stringComparer)
        {
            _stringComparer = stringComparer;
        }

        private readonly StringComparer _stringComparer;
        private static readonly Regex NumberSequenceRegex = new Regex(@"(\d+)", RegexOptions.Compiled | RegexOptions.CultureInvariant);
        private static string[] Tokenize(string s) => s == null ? new string[] { } : NumberSequenceRegex.Split(s);
        private static ulong ParseNumberOrZero(string s) => ulong.TryParse(s, NumberStyles.None, CultureInfo.InvariantCulture, out var result) ? result : 0;

        public int Compare(string s1, string s2)
        {
            var tokens1 = Tokenize(s1);
            var tokens2 = Tokenize(s2);

            var zipCompare = tokens1.Zip(tokens2, TokenCompare).FirstOrDefault(x => x != 0);
            if (zipCompare != 0)
                return zipCompare;

            var lengthCompare = tokens1.Length.CompareTo(tokens2.Length);
            return lengthCompare;
        }
        
        private int TokenCompare(string token1, string token2)
        {
            var number1 = ParseNumberOrZero(token1);
            var number2 = ParseNumberOrZero(token2);

            var numberCompare = number1.CompareTo(number2);
            if (numberCompare != 0)
                return numberCompare;

            var stringCompare = _stringComparer.Compare(token1, token2);
            return stringCompare;
        }
    }
}

2

এখানে একটি নিষ্কলুষ এক-লাইন রেজেক্স-কম লিনকিউ উপায় (পাইথন থেকে ধার করা):

var alphaStrings = new List<string>() { "10","2","3","4","50","11","100","a12","b12" };
var orderedString = alphaStrings.OrderBy(g => new Tuple<int, string>(g.ToCharArray().All(char.IsDigit)? int.Parse(g) : int.MaxValue, g));
// Order Now: ["2","3","4","10","11","50","100","a12","b12"]

মুছে ফেলা ডাম্প () এবং ভারে নির্ধারিত এবং এটি একটি কবজির মতো কাজ করে!
আর্ন এস

@ আর্নস: এটি লিঙ্কউপ্যাডে লেখা হয়েছিল; এবং আমি অপসারণ করতে ভুলে গেছি Dump()। নির্দেশ করার জন্য ধন্যবাদ।
mshsayem

1

পূর্ববর্তী উত্তরগুলির কয়েকটি এবং সম্প্রসারণের পদ্ধতিগুলি ব্যবহার করে, আমি নিম্নলিখিতগুলি নিয়ে এসেছি যার মধ্যে একাধিক রেজেক্স অবজেক্ট ব্যবহার করার বা রিজেক্সকে অকারণে কল করার সাথে সম্পর্কিত সম্ভাব্য একাধিক গণনাযুক্ত গণনা, বা পারফরম্যান্স সম্পর্কিত সমস্যা নেই that বলা হচ্ছে, এটি টোলিস্ট () ব্যবহার করে, যা বৃহত্তর সংগ্রহগুলিতে সুবিধা উপেক্ষা করতে পারে।

নির্বাচক জেনেরিক টাইপিং সমর্থন করে যাতে কোনও প্রতিনিধি নির্ধারিত হতে দেওয়া হয়, উত্স সংগ্রহের উপাদানগুলি নির্বাচক দ্বারা পরিবর্তিত হয়, তারপরে টোস্ট্রিং () দিয়ে স্ট্রিংগুলিতে রূপান্তরিত হয়।

    private static readonly Regex _NaturalOrderExpr = new Regex(@"\d+", RegexOptions.Compiled);

    public static IEnumerable<TSource> OrderByNatural<TSource, TKey>(
        this IEnumerable<TSource> source, Func<TSource, TKey> selector)
    {
        int max = 0;

        var selection = source.Select(
            o =>
            {
                var v = selector(o);
                var s = v != null ? v.ToString() : String.Empty;

                if (!String.IsNullOrWhiteSpace(s))
                {
                    var mc = _NaturalOrderExpr.Matches(s);

                    if (mc.Count > 0)
                    {
                        max = Math.Max(max, mc.Cast<Match>().Max(m => m.Value.Length));
                    }
                }

                return new
                {
                    Key = o,
                    Value = s
                };
            }).ToList();

        return
            selection.OrderBy(
                o =>
                String.IsNullOrWhiteSpace(o.Value) ? o.Value : _NaturalOrderExpr.Replace(o.Value, m => m.Value.PadLeft(max, '0')))
                     .Select(o => o.Key);
    }

    public static IEnumerable<TSource> OrderByDescendingNatural<TSource, TKey>(
        this IEnumerable<TSource> source, Func<TSource, TKey> selector)
    {
        int max = 0;

        var selection = source.Select(
            o =>
            {
                var v = selector(o);
                var s = v != null ? v.ToString() : String.Empty;

                if (!String.IsNullOrWhiteSpace(s))
                {
                    var mc = _NaturalOrderExpr.Matches(s);

                    if (mc.Count > 0)
                    {
                        max = Math.Max(max, mc.Cast<Match>().Max(m => m.Value.Length));
                    }
                }

                return new
                {
                    Key = o,
                    Value = s
                };
            }).ToList();

        return
            selection.OrderByDescending(
                o =>
                String.IsNullOrWhiteSpace(o.Value) ? o.Value : _NaturalOrderExpr.Replace(o.Value, m => m.Value.PadLeft(max, '0')))
                     .Select(o => o.Key);
    }

1

মাইকেল পার্কারের সমাধান দ্বারা অনুপ্রাণিত, এখানে এমন একটি IComparerবাস্তবায়ন রয়েছে যা আপনি যে কোনও লিনাক অর্ডার পদ্ধতিতে ফেলে যেতে পারেন:

private class NaturalStringComparer : IComparer<string>
{
    public int Compare(string left, string right)
    {
        int max = new[] { left, right }
            .SelectMany(x => Regex.Matches(x, @"\d+").Cast<Match>().Select(y => (int?)y.Value.Length))
            .Max() ?? 0;

        var leftPadded = Regex.Replace(left, @"\d+", m => m.Value.PadLeft(max, '0'));
        var rightPadded = Regex.Replace(right, @"\d+", m => m.Value.PadLeft(max, '0'));

        return string.Compare(leftPadded, rightPadded);
    }
}

0

নিম্নলিখিত প্যাটার্নটির সাথে পাঠ্যকে মোকাবেলা করার জন্য আমাদের একটি প্রাকৃতিক ধরণের প্রয়োজন ছিল:

"Test 1-1-1 something"
"Test 1-2-3 something"
...

কোনও কারণে যখন আমি প্রথম এসও-তে নজর রেখেছিলাম তখন আমি এই পোস্টটি খুঁজে পেলাম না এবং আমাদের নিজস্ব প্রয়োগ করেছিলাম। এখানে উপস্থাপিত কয়েকটি সমাধানের সাথে তুলনা করা হয়েছে, একইরকম ধারণা অনুসারে, এটি সম্ভবত সহজ এবং সহজে বোঝার সুবিধা হতে পারে। যাইহোক, আমি পারফরম্যান্সের বাধাগুলি দেখার চেষ্টা করার সময় এটি ডিফল্টের তুলনায় এখনও অনেক ধীর গতিতে কার্যকর implementationOrderBy()

আমি প্রয়োগ করি এমন এক্সটেনশন পদ্ধতিটি এখানে:

public static class EnumerableExtensions
{
    // set up the regex parser once and for all
    private static readonly Regex Regex = new Regex(@"\d+|\D+", RegexOptions.Compiled | RegexOptions.Singleline);

    // stateless comparer can be built once
    private static readonly AggregateComparer Comparer = new AggregateComparer();

    public static IEnumerable<T> OrderByNatural<T>(this IEnumerable<T> source, Func<T, string> selector)
    {
        // first extract string from object using selector
        // then extract digit and non-digit groups
        Func<T, IEnumerable<IComparable>> splitter =
            s => Regex.Matches(selector(s))
                      .Cast<Match>()
                      .Select(m => Char.IsDigit(m.Value[0]) ? (IComparable) int.Parse(m.Value) : m.Value);
        return source.OrderBy(splitter, Comparer);
    }

    /// <summary>
    /// This comparer will compare two lists of objects against each other
    /// </summary>
    /// <remarks>Objects in each list are compare to their corresponding elements in the other
    /// list until a difference is found.</remarks>
    private class AggregateComparer : IComparer<IEnumerable<IComparable>>
    {
        public int Compare(IEnumerable<IComparable> x, IEnumerable<IComparable> y)
        {
            return
                x.Zip(y, (a, b) => new {a, b})              // walk both lists
                 .Select(pair => pair.a.CompareTo(pair.b))  // compare each object
                 .FirstOrDefault(result => result != 0);    // until a difference is found
        }
    }
}

ধারণাটি হ'ল মূল স্ট্রিংগুলি অঙ্ক এবং অ-অঙ্কের ব্লকগুলিতে ভাগ করা ("\d+|\D+" )। যেহেতু এটি একটি সম্ভাব্য ব্যয়বহুল কাজ, এটি এন্ট্রি প্রতি মাত্র একবার করা হয়। এরপরে আমরা তুলনামূলক বস্তুর তুলক ব্যবহার করি (দুঃখিত, আমি এটি বলার জন্য আরও সঠিক উপায় খুঁজে পাচ্ছি না)। এটি প্রতিটি ব্লকে অন্য স্ট্রিংয়ের সাথে সম্পর্কিত ব্লকের সাথে তুলনা করে।

এটি কীভাবে উন্নত করা যায় এবং প্রধান ত্রুটিগুলি কী তা সম্পর্কে আমি প্রতিক্রিয়া চাই। নোট করুন যে এই মুহুর্তে রক্ষণাবেক্ষণযোগ্যতা আমাদের কাছে গুরুত্বপূর্ণ এবং আমরা বর্তমানে এটি অত্যন্ত বড় ডেটা সেটগুলিতে ব্যবহার করছি না।


1
কাঠামোগতভাবে পৃথক স্ট্রিংগুলির তুলনা করার চেষ্টা করলে এটি ক্র্যাশ হয় - যেমন "a-1" "" a-2 "এর সাথে তুলনা করা ভাল কাজ করে, তবে" a "সাথে" 1 "তুলনা করা হয় না, কারণ" a "। কমপ্যাক্টটো (1) একটি ব্যতিক্রম নিক্ষেপ
jimrandomh

@ জিমরান্দোম, আপনি সঠিক বলেছেন এই পদ্ধতির আমাদের নিদর্শন নির্দিষ্ট ছিল।
এরিক লিপ্রান্দি

0

এমন সংস্করণ যা পড়া / বজায় রাখা সহজ।

public class NaturalStringComparer : IComparer<string>
{
    public static NaturalStringComparer Instance { get; } = new NaturalStringComparer();

    public int Compare(string x, string y) {
        const int LeftIsSmaller = -1;
        const int RightIsSmaller = 1;
        const int Equal = 0;

        var leftString = x;
        var rightString = y;

        var stringComparer = CultureInfo.CurrentCulture.CompareInfo;

        int rightIndex;
        int leftIndex;

        for (leftIndex = 0, rightIndex = 0;
             leftIndex < leftString.Length && rightIndex < rightString.Length;
             leftIndex++, rightIndex++) {
            var leftChar = leftString[leftIndex];
            var rightChar = rightString[leftIndex];

            var leftIsNumber = char.IsNumber(leftChar);
            var rightIsNumber = char.IsNumber(rightChar);

            if (!leftIsNumber && !rightIsNumber) {
                var result = stringComparer.Compare(leftString, leftIndex, 1, rightString, leftIndex, 1);
                if (result != 0) return result;
            } else if (leftIsNumber && !rightIsNumber) {
                return LeftIsSmaller;
            } else if (!leftIsNumber && rightIsNumber) {
                return RightIsSmaller;
            } else {
                var leftNumberLength = NumberLength(leftString, leftIndex, out var leftNumber);
                var rightNumberLength = NumberLength(rightString, rightIndex, out var rightNumber);

                if (leftNumberLength < rightNumberLength) {
                    return LeftIsSmaller;
                } else if (leftNumberLength > rightNumberLength) {
                    return RightIsSmaller;
                } else {
                    if(leftNumber < rightNumber) {
                        return LeftIsSmaller;
                    } else if(leftNumber > rightNumber) {
                        return RightIsSmaller;
                    }
                }
            }
        }

        if (leftString.Length < rightString.Length) {
            return LeftIsSmaller;
        } else if(leftString.Length > rightString.Length) {
            return RightIsSmaller;
        }

        return Equal;
    }

    public int NumberLength(string str, int offset, out int number) {
        if (string.IsNullOrWhiteSpace(str)) throw new ArgumentNullException(nameof(str));
        if (offset >= str.Length) throw new ArgumentOutOfRangeException(nameof(offset), offset, "Offset must be less than the length of the string.");

        var currentOffset = offset;

        var curChar = str[currentOffset];

        if (!char.IsNumber(curChar))
            throw new ArgumentException($"'{curChar}' is not a number.", nameof(offset));

        int length = 1;

        var numberString = string.Empty;

        for (currentOffset = offset + 1;
            currentOffset < str.Length;
            currentOffset++, length++) {

            curChar = str[currentOffset];
            numberString += curChar;

            if (!char.IsNumber(curChar)) {
                number = int.Parse(numberString);

                return length;
            }
        }

        number = int.Parse(numberString);

        return length;
    }
}

-2

আমার সমস্যা এবং আমি কীভাবে এটি সমাধান করতে সক্ষম হয়েছিল তা আমাকে ব্যাখ্যা করতে দিন।

সমস্যা: - ফাইল-ইনফো অবজেক্ট থেকে ফাইলনামের ভিত্তিতে ফাইলগুলি বাছাই করুন যা একটি ডিরেক্টরি থেকে পুনরুদ্ধার করা হয়।

সমাধান: - আমি ফাইলআইএনফো থেকে ফাইলের নামগুলি নির্বাচন করেছি এবং ফাইলটির ".png" অংশটি ছাঁটাই করেছি। এখন কেবল তালিকাগুলি করুন (বাছাই করুন), যা প্রাকৃতিক বাছাই ক্রমে ফাইলের নামগুলি সাজায়। আমার পরীক্ষার উপর ভিত্তি করে আমি দেখতে পেয়েছি যে .png থাকার সাথে বাছাইয়ের ক্রমটি মিস হয়। নীচের কোডটি দেখুন

var imageNameList = new DirectoryInfo(@"C:\Temp\Images").GetFiles("*.png").Select(x =>x.Name.Substring(0, x.Name.Length - 4)).ToList();
imageNameList.Sort();

আমি কি এই উত্তর -1 এর কারণ জানতে পারি?
girishkatta9
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.