總結字符串比較函數

作者: 飛林沙  來源: 博客園  發布時間: 2010-01-25 11:38  閱讀: 2830 次  推薦: 0   原文鏈接   [收藏]  
摘要:字符串

  最近一段時間一直在重看CLR via C# , 這次把字符串比較的函數總結下。

  1.Compare和CompareTo大PK

  首先是我們最常用的String.Compare和CompareTo實例方法,先來看看這兩個方法:

 

image

  我們通過這個可以直觀地看到,String的靜態方法要比CompareTo多出好多的方法重載,其實這也是兩者的最大區別,也就是說String.Compare有著更多的功能選項供我們控制。

  其中主要包含著三個方面:

  A. 文化信息

  B. CompareOptions

  C. 比較的開始和結束位置

  對于文化信息,我們可以看下Compare的反編譯結果:

 

image

  對于Compare來說,他會通過傳遞進來的文化信息來調用對應的比較。

  而CompareTo則是:

 

image

  CompareTo則會調用與當前線程相關聯的文化信息。

  對于文化信息來說,還有著這樣一個枚舉選項:StringComparison:

  下面讓我們來看下StringComparison枚舉:

image

  對于該枚舉,共有如上六個枚舉值。該枚舉主要對應著當前的文化信息,大小寫,以及排序規則。

 

image

  這就意味著,如果我們進行國際化的時候,字符串比較必須使用String.Compare靜態方法。

  下面來看下CompareOptions:

 

image

  最后看下含開始和結束位置的String.Compare方法:

 

image

  方法本身很簡單,而方法鏈的最末端使用的是:

 

image

  一個內部比較字符串的非托管方法,而方法的具體內容,我無從而知,但是可以明確的是,這一定是一個高效的比較算法。

  因此,當我們每次SubString的時候,當我們ToLower的時候,我們不妨都在這里使用String.Compare,是不是為我們節省了很多空間,提高了很大效率呢?

  因此,我在這里建議,如果可能,我們盡可能地使用String.Compare方法來代替CompareTo方法!

  2. 被遺忘的CompareOrdinal

  讓我們先來看下CompareOrdinal的源碼:

private static unsafe int CompareOrdinalHelper(string strA, string strB)
{
    int num = Math.Min(strA.Length, strB.Length);
    int num2 = -1;
    fixed (char* chRef = &strA.m_firstChar)
    {
        fixed (char* chRef2 = &strB.m_firstChar)
        {
            char* chPtr = chRef;
            char* chPtr2 = chRef2;
            while (num >= 10)
            {
                if (*(((int*)chPtr)) != *(((int*)chPtr2)))
                {
                    num2 = 0;
                    break;
                }
                if (*(((int*)(chPtr + 2))) != *(((int*)(chPtr2 + 2))))
                {
                    num2 = 2;
                    break;
                }
                if (*(((int*)(chPtr + 4))) != *(((int*)(chPtr2 + 4))))
                {
                    num2 = 4;
                    break;
                }
                if (*(((int*)(chPtr + 6))) != *(((int*)(chPtr2 + 6))))
                {
                    num2 = 6;
                    break;
                }
                if (*(((int*)(chPtr + 8))) != *(((int*)(chPtr2 + 8))))
                {
                    num2 = 8;
                    break;
                }
                chPtr += 10;
                chPtr2 += 10;
                num -= 10;
            }
            if (num2 == -1)
            {
                goto Label_00F1;
            }
            chPtr += num2;
            chPtr2 += num2;
            int num3 = chPtr[0] - chPtr2[0];
            if (num3 != 0)
            {
                return num3;
            }
            return (chPtr[1] - chPtr2[1]);
        Label_00D7:
            if (*(((int*)chPtr)) != *(((int*)chPtr2)))
            {
                goto Label_00F5;
            }
            chPtr += 2;
            chPtr2 += 2;
            num -= 2;
        Label_00F1:
            if (num > 0)
            {
                goto Label_00D7;
            }
        Label_00F5:
            if (num > 0)
            {
                int num4 = chPtr[0] - chPtr2[0];
                if (num4 != 0)
                {
                    return num4;
                }
                return (chPtr[1] - chPtr2[1]);
            }
            return (strA.Length - strB.Length);
        }
    }
}

  方法很長,但是很簡單,即使是Reflector 出來的變量名很BT,但是我們也可以大致看個究竟。

  他是將整個字符串每5個字符(10個字節)分成一組,然后逐個比較,找到第一個不相同的ASCII碼后退出循環。并且求出兩者的ASCII碼的差。不過我很費解的是微軟為什么要把這個實現的如此麻煩。只能等到周一再求解了。

  但是在CLR via C#上有這樣的話:這個方法比其他方法都要快。我想應該是有一定道理的吧。

  所以當我們比較大小的時候,盡量使用CompareOrdinal方法。

  3. 常用的Equals方法

  先來看Equals實例方法:

 

image

  方法會首先進行合法性判斷,然后比較兩者是否指向同一塊引用,接下來調用EqualsHelper方法(不清楚微軟為什么很沉迷于XXXHelper這個命名,難道XXXHelper這個名詞不應該是一個類名么?)

private static unsafe bool EqualsHelper(string strA, string strB)
{
    int length = strA.Length;
    if (length != strB.Length)
    {
        return false;
    }
    fixed (char* chRef = &strA.m_firstChar)
    {
        fixed (char* chRef2 = &strB.m_firstChar)
        {
            char* chPtr = chRef;
            char* chPtr2 = chRef2;
            while (length >= 10)
            {
                if ((((*(((int*)chPtr)) != *(((int*)chPtr2))) || (*(((int*)(chPtr + 2))) != *((
(int*)(chPtr2 + 2))))) || ((*(((int*)(chPtr + 4))) != *(((int*)(chPtr2 + 4)))) || (*(((int
*)(chPtr + 6))) != *(((int*)(chPtr2 + 6)))))) || (*(((int*)(chPtr + 8))) != *(((int*)(chPt
r2 + 8)))))
                {
                    break;
                }
                chPtr += 10;
                chPtr2 += 10;
                length -= 10;
            }
            while (length > 0)
            {
                if (*(((int*)chPtr)) != *(((int*)chPtr2)))
                {
                    break;
                }
                chPtr += 2;
                chPtr2 += 2;
                length -= 2;
            }
            return (length <= 0);
        }
    }
}

  迷糊了,又是這樣的算法,我實在不了解10字節究竟有什么奧秘,周一如果問到答案會對其進行解釋。

  然而,值得肯定的是,由于是非安全代碼的比較,所以效率要比我們用安全代碼高得多。

  接下來看看Equals靜態方法:

 

image

  (關于==的運算符重載之前有誤,下文會解釋清楚)

  4. 總結

  本文主要介紹了String類型的比較方法,也留下了一些疑問,也希望可以得到各位的解答。

0
0
 
標簽:字符串
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()