在 .NET 中撰寫 String.Contains 方法時容易犯的錯誤

分享到噗浪!

.NET Framework 中的 String.Contains 方法從 .NET 2.0 時就開始出現,但你可能從未想過這個問題,這個簡單的 String.Contains 方法到底有什麼該注意的地方呢。先考各位一段簡單的程式碼,並先仔細想想如果以下程式碼執行完後,變數 b 的值應該是 True 或 False 呢?

string s1 = "The quick brown fox jumps over the lazy dog";
string s2 = "";
bool b;
b = s1.Contains(s2);

不蠻各位,連我在寫類似這段程式時,大多從心裡直覺的認為答案是 False,直到上週才發現一個幾個月前完成的專案竟然開始出現一些不合理的結果,原本不應該出現的資料全部都出現了。

我會想說,s1 有一堆字串,而 s2 卻是空字串,當判斷 s1 是否包含「空字串」時,怎麼可能會是 True 呢?

我也好奇的問了多位有在寫 .NET 的朋友,不少都是 .NET 撰寫多年且實力不差的開發人員,大家都一致的回答 False

我都說成這樣了,還有人認為答案是 False 嗎?除了我親自跑程式驗證過答案是 True 外,也轉貼 MSDN 上關於 String.Contains 方法的說明,如下圖示:

.NET Framework 類別庫 - String.Contains 方法

這樣的結果著實讓我匪夷所思,像這種簡單到爆的方法(Method)誰會真的認真到仔細的看這種 Method 的 MSDN 文件,而這種違反直覺判斷的程式行為實在是需要「經驗」,遇到了才知道,即便是如此簡單的字串用法。

※ 2009-06-26 補充說明

我多列出幾個跟 String 類別有關的字串比對 Method,供各位參考:

Console.WriteLine("123".Contains(""));    // True
Console.WriteLine("123".StartsWith(""));  // True
Console.WriteLine("123".EndsWith(""));    // True
Console.WriteLine("123".IndexOf(""));     // 0
Console.WriteLine("123".LastIndexOf("")); // 2

總之「空字串」代表的不是「字串」,而擁有一個特殊的意義,你可以想像著每一個字串開頭結尾都會包括一個「空字串」,這個「空字串」本身不佔空間,但卻存在這樣一個虛擬字元,緊緊黏在第一個字元最後一個字元上。

我不知道這樣說明會不會對某些人來說太複雜,但我自己是這樣想像的,可以幫助我記憶這個現象,這樣想以後寫程式的時候就比較不會出錯了。

相關連結

  

此文章由 will 發表於 2009/6/25 下午 08:17:25

永久連結 | 評論 (10) | 此文章的RSSRSS comment feed |

分類: .Net

標籤: ,

評論

六月 25. 2009 22:12

kennyshu

如果只是判別空字串的話(不論是針對s1或是s2),這邊還有其他更好的方法喔
msdn.microsoft.com/.../ms182279(VS.80).aspx

kennyshu 台灣

六月 25. 2009 22:44

Neil

FYI
用Python來判斷,也是一樣的結果哦~

Neil 台灣

六月 26. 2009 09:59

tomexou

.NET字串操作相關method,在傳入的字串常要小心null及empty
因為我們在撰寫String Helper函式時,
不也是都會對這些值過敏嗎?

String.Contains()而言,empty代表無比對之值,理論要略過此函式。
通常函式不執行return要傳回false,
但呼叫這函式通常會再執行false時要do something,這就違反了「略過」的原意
因此method創造人深思之後,才決定要傳回true。

tomexou 台灣

六月 26. 2009 10:16

Johnny.Net

因為 String.Contains 的回傳方法是

return (this.IndexOf(value, StringComparison.Ordinal) >=0);

邏輯很簡單; 如果你傳 value = "", 它能不傳回 true 嗎?

Johnny.Net 台灣

六月 26. 2009 10:56

will

我的原意並非判別「空字串」,而是判斷該字串是否包含每些字串,這個字串是設定在 web.config 的 appSettings 中。
這次是因為 appSettings 中的參數被設為 Empty,本以為不會出現某些資料,但卻通通出現了。

will 台灣

六月 26. 2009 11:27

kennyshu

所以算是例外處理,加個判斷空字串還是保險些~
我之前也發生過user把web.config裡的的值移掉變成"",從那之後我都會記得檢查一下,不然又變成bug...

kennyshu 美國

六月 26. 2009 12:50

Johnny.Net

所以問題是出在 IndexOf... 為什麼空字串傳進去其回傳值是 0。但反過來想, 它能不是 0 嗎? 如果要被比較的字串本身也是空字串, 它若傳回 -1 而不是 0, 代誌就大條了。

Johnny.Net 台灣

六月 26. 2009 13:32

will

總之「空字串」代表的不是「字串」,而擁有一個特殊的意義,你可以想像著每一個字串在開頭與結尾都會包括一個「空字串」,這個「空字串」本身不佔空間,但卻存在這樣一個虛擬字元,緊緊黏在第一個字元與最後一個字元上。

我不知道這樣說明會不會對某些人來說太複雜,但我自己是這樣想像的,可以幫助我記憶這個現象,這樣想以後寫程式的時候就比較不會出錯了。

will 台灣

六月 26. 2009 15:59

Johnny.Net

一般來講, 字串這種資料結構的實作方式常見的有兩種, 一種是明確定義長度的, 另一種則是以 Null 做終結的 (雖然一般也會設定最大長度)。在 .Net 中採用的是後者 (我應該沒記錯吧?)。一個空字串其實就是一個只有一個 Null 字元 (0X00) 的實體 (它還是有實體, 不像一個被指定為 Null 的字串變數, 因為其 Pointer 也是 Null, 所以連實體都沒有)。也因為如此, 所以 Null 字串和空字串相比起來是不相等的。但是一個空字串和一個空字串比起來必須相等; 如果它們竟然不相等, 我前面說代誌大條就是指這個。

那麼, 由於一個空字串並不真的是空無一物 (它至少還有一個終結字元), 所以由 IndexOf 運算出來, 不管什麼字串 (除了 Null 字串以外), 它一定會傳回一個大於等於 0 的結果。所以說如果從這個角度來看, 那麼為什麼什麼字串都 Contain 空字串, 這就沒有什麼奇怪的了。

Johnny.Net 台灣

六月 29. 2009 14:18

Peter

應該是每一個character的開頭跟結尾各有一個空字串("")
所以每一個string應該有number of characters + 1個空字串

Peter 台灣

新增評論


( 您輸入的Email不會顯示於網站上 )

  Country flag

biuquote
  • 評論
  • 線上預覽
Loading