Working with strings

字串

字串介紹

System.String是直接繼承System.Object,所以很明顯字串是一個參考型別,但是當操作字串的時候,會發現字串的特性,好像又跟參考型別不太一樣,感覺很像數值型別。

例如以下範例:

string a = "I'm String";

// b指向a的參考
string b = a;

// 指向同一個參考
Console.WriteLine(Object.ReferenceEquals(a, b)); //output : ture

// b 指定新字串
b = "New String";

// 結果指向不同參考
Console.WriteLine(Object.ReferenceEquals(a, b)); //output : false

Console.WriteLine("a = " + a); // output : a = I'm String

Console.WriteLine("b = " + b); // output : b = New String
以參考型別來說,當我b字串給新值的時候,應該是直接改參考的值,然後a的值會變得跟b一樣才對,結果卻不是這樣,這是為什麼呢?
這是因為字串物件不可修改的特性(immutable)的關係,字串一旦初始化後,就不能再進行任何刪除、切斷、插入等動作,是一個唯讀的物件,
b = "New String" 這段就是初始化了一個字串物件,並塞給參考b,所以才導致b的參考與a不一樣。
由於字串的唯讀特性,對任何字串物件的修改,都強制建立一個新的字串物件,這一點非常重要。

 

字串方法

因為操作字串是一件很頻繁的事情,還好字串提供了很多方法,來操作字串,能讓我們快速做字串的操作,在這邊簡單介紹幾個字串操作的方法。
string str = "Hello I'm Miles";

// 從第十個字元開始,取五個長度的字元
string value = str.Substring(10, 5); // Miles

// 將兩個字串組在一起
value = string.Concat(value, " is a boy"); // Miles is a boy

//取代指定字串
value = value.Replace("Miles", "Kevin"); // Kevin is a boy

// 轉大寫
value = value.ToUpper(); // KEVIN IS A BOY

// 將每個字元切開
var array = "Miles".ToArray();
// array = {'M','i','l','e','s'}

// ASCII編碼
byte[] bytes = Encoding.ASCII.GetBytes("Miles");
// bytes = {77,103,108,101,115}

提醒:Replace,ToUpper,Concat,都會產生一個新的字串物件。

 

效能問題

通常要產生一個字串物件的時候,很多人會用+的方式來組字串("A" + "B"),這組裝過程中,會產生很多臨時的字串物件,這些物件在累堆上分配,需要垃圾回收器來回收,
這些動作會對程式的效能產生巨大的影響,例如以下程式範例
string item = "加字串";
long startTime = DateTime.Now.Ticks; //Nanosecond
string str1 = "";
for (int i = 0; i < 10000; i++)
{
    str1 += item;
}
long endTime = DateTime.Now.Ticks; //Nanosecond

Console.WriteLine(string.Format("花費時間 {0} nanosceond", endTime - startTime)); //nanosceond = 10的-9次方秒
// output : 花費時間 490020 nanosceond

要解決這個效能問題,可以用底下介紹StringBuilder來處理。

 

StringBuilder

StringBuilder類別在最終產生String物件之前,將不會產生任何String物件,底下程式範例
string item = "加字串";
StringBuilder sb = new StringBuilder();
long startTime = DateTime.Now.Ticks; //Nanosecond            
for (int i = 0; i < 10000; i++)
{
    sb.Append(item);
}
string str = sb.ToString();
long endTime = DateTime.Now.Ticks; //Nanosecond

Console.WriteLine(string.Format("花費時間 {0} nanosceond", endTime - startTime)); //nanosceond = 10的-9次方秒
// output : 花費時間 19990 nanosceond
看到花費時間的結果,這花費時間差了16倍之多,這效能差距可說是非常的大,
這差距是因為StringBuilder類別內部保留了一個私有的String成員,當ToString方法被呼叫時,
這個String成員才會被塞值,並回傳給呼叫者。這其中字串組裝期間,字串是在記憶體上被修改的,才能有這麼快的效率。

 

正規表示式 (regular expressions)

對字串的操作,除了用內建的方法來操作以外,還可以用正規表示式的方式來操作字串,非常的靈活,以下範例
var str = @"Regular expressions are a specialized syntax 
            to find and replace patterns in strings";

// "\b" 比對空白
// "\w+" 比對多個數字或英文字母
// 意思就是比對空白之間的字串就抓出來
var pattern = @"\b\w+\b";
var matches = Regex.Matches(str, pattern);
foreach (var item in matches)
{
    Console.WriteLine(item);
}
// output:Regular
// output:expressions
// output:are
// output:a
// .......以此類推

/*---------------------------------------------*/

// "(Regular )" 比對Regular 字串
// (.+) 比對一個或多個任何字元,但換行符號不算
// ( are) 比對 are字串
// 意思就是 比對 "Regular 任何字串 are" 就成立
pattern = @"(Regular )(.+)( are)";
var groups = Regex.Match(str, pattern).Groups;
foreach (var item in groups)
{
    Console.WriteLine(item);
}
// output:Regular expressions are
// output:Regular
// output:expressions
// output: are

 

正規表示式參考資料:

https://atedev.wordpress.com/2007/11/23/%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%A4%BA%E5%BC%8F-regular-expression/

 

 

一天一分享,身體好健康。

該追究的不是過去的原因,而是現在的目的。