總結字串比較函數

來源:互聯網
上載者:User

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

1.Compare和CompareTo大PK

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

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

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

A. 文化資訊

B. CompareOptions

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

對於文化資訊,我們可以看下Compare的反編譯結果:

對於Compare來說,他會通過傳遞進來的文化資訊來調用對應的比較。

而CompareTo則是:

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

對於文化資訊來說,還有著這樣一個枚舉選項:StringComparison:

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

對於該枚舉,共有如上六個枚舉值。該枚舉主要對應著當前的文化資訊,大小寫,以及定序。

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

下面來看下CompareOptions:

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

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

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

因此,當我們每次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執行個體方法:

方法會首先進行合法性判斷,然後比較兩者是否指向同一區塊引述,接下來調用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*)(chPtr2 + 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靜態方法:

(關於==的運算子多載之前有誤,下文會解釋清楚)

4. 總結

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

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.