字串 String
字串在程式語言中是其中一個非常重要的資料型態。如果沒有字串,那我們如果想要表達一句英文的話就會變得非常麻煩。
在這一章,我們就是要來介紹字串這個觀念。包括應該如何使用、有分什麼種類等等!
什麼是字串
所以到底什麼是字串?還記得我們在 什麼是IDE 那章的第一個程式碼嗎?
int main() { std::cout << "Hello World!" << std::endl; return 0; }
這裡的 "Hello World!"
就是一個字串,英文叫做 string。
字串顧名思義就是將一個一個字符串起來,我們常常用字串來表示名字或各種單字。
在 C++ 中,我們有兩種方式可以用來表示字串:
- C-style string
- std::string
C-style string
C-style string,中文翻譯叫做 C 風格字串。從名字上就可以知道這種表達方式源自於 C 語言,而 C++ 則繼續採用了這種表達方式。
這種表達方式最重要的一個特色就是每一個字串最後都是由 \0
結尾,這個符號叫做 null terminator。
因此 C++ 在做讀取的時候,只要讀到 \0
他就知道這個是一個字串的結尾,也就會停止讀取。
那麼要如何宣告一個 C-style string 呢?其實就跟前一章 陣列 在講如何宣告陣列基本上是一樣的方法,只是這個陣列的資料型態為 char
。
所以我們可以看到,其實 C-style string 本身是一個陣列,而這個鎮列中的每一個格子則儲存一個字符。
我們可以這樣宣告:
char str[]{"abc"};
在宣告時,我們並沒有加上 \0
,那是因為 編譯器 在編譯時會自動在最尾端幫我們加上去。
因此,實際上這個字串 str
的長度其實是 4。我們可以執行以下程式進行驗證。
int main() { char str[]{"abc"}; int length{std::size(str)}; std::cout << length << std::endl; return 0; }
因為 C-style string 本身其實是一個陣列,因此當我們想要更改 str
時,我們必須以更改陣列的方式進行修改。也就是說:
str = "def"; // 這是不可以的 str[0] = "d"; // 這是可以的,str會變成"dbc"
而有一點是初學者常常犯的錯,也就是如果我們想要給定這個陣列的大小,那如果我們的字串剛好等於這個陣列的大小,則程式就會出錯。
這是因為在 C-style string,我們永遠要留一個空位給 null terminator \0
。
char str[4]{"abc"}; // 正確 char str[3]{"abc"}; // 程式會報錯
std::string
C++ 提供了另一種字串類型:std::string
。
要使用這個類別我們需要引進相關的資料庫,因此需要使用 <string>
這個程式庫。
在宣告方面,std::string
也是比 C-style string 好用非常多。
std::string name{"Arthur"};
如果我們想要將這個 name
換成別的名字,也是非常容易。
name = "Simon"
更方便的是,如果我們只想更改其中一個字元,我們可以像更改陣列那樣更改特定字元。
int main() { std::string str{"Arthur"}; str[0] = 'B'; std::cout << str << std::endl; // str 的結果為 Brthur return 0; }
要使用 C-style string 還是 std::string
講了這麼多,當我們在寫程式時到底要用哪一個呢?
如果可以的話,盡量使用 std::string!std::string 最大的優點就是相比於 C-style string,std::string 更容易使用,並且也有更多樣的應用方式。
在很少數的應用中你可能會需要去嚴格控制你的記憶體空間,在這種情況下你就可以用 C-style string,因為你可以自由控制需要給每一個字串多少儲存空間 (buffer size)。
然而絕大多數情況下,我們仍然偏好使用 std::string。但是,要真正的了解 C++,知道 C-style string 是如何運作也是非常必要的!因為在許多底層應用中,C-style string 仍是非常常見的。
字串的基本操作
現在我們了解了字串的作用,以及不同種類的字串。那麼我們可以用字串來做什麼呢?
這邊我們來介紹五種常見的功能:
- 字串連接
- 長度檢視
- 查找
- 替代
- 提取
字串連接
我們可以利用 +
來完成數個字串的連接。舉例來說,"abc"
和 "def"
連接後就會是 "abcdef"
。
我們可以這樣用
int main() { std::string s1 = "abc"; std::string s2 = "def"; std::cout << "連接後:" << s1 + s2 << std::endl; std::string n1 = "123"; std::string n2 = "456"; std::cout << "連接後:" << n1 + n2 << std::endl; std::string message = "Hello" + ", " + "world!"; std::cout << "連接後:" << message << std::endl; return 0; }
這樣輸出結果就會是
連接後: abcdef 連接後: 123456 連接後: Hello, world!
值得注意的是,這裡的 "123"
依舊是一個字串而非數字。
因此 "123" + "456"
的結果並不是 "579"
!
長度檢視
有時候我們會想知道當前字串的長度,這時候我們就可以利用 .length()
或是 .size()
。
這兩個函數做的是一樣的事,所以可以隨意選擇。
之所以會有兩個函數主要是因為歷史和命名的原因。在其他的 C++ 容器比方說陣列,它也會有 .size()
這樣一個函數,以供我們得知存在容器內的元素數量。
另外,我們也可以使用 .empty()
來看看字串是否為空。
int main() { std::string s = "abc"; std::cout << "字串長度: " << s.length() << std::endl; if (s.empty() == true) { std::cout << "字串為空" << std::endl; } else { std::cout << "字串不為空字串" << std::endl; } return 0; }
這樣的輸出結果就會是
字串長度: 3 字串不為空字串
查找
有時候我們會想要查找字串中的子字串。什麼是子字串?舉個例子,"abc"
就是 "abcdef"
的子字串,"bcd"
也是。
這時候,.find()
就會非常好用。.find()
會回傳找到的第一個子字串的位置。如果沒有找到,那麼就會回傳 -1
。
比方說:
int main() { std::string sentence = "The brown fox jumps over the lazy dog"; int found = sentence.find("lazy"); // 查找 "lazy" 的位置 if (found != -1) { std::cout << "\"lazy\" found at position: " << found << std::endl; } else { std::cout << "\"lazy\" not found in the sentence." << std::endl; } return 0; }
因為 "lazy"
出現在字串中,所以輸出結果會是
"lazy" found at position: 29
替代
有時候,我們會想要替換掉一個句子裡面的某的特定詞語,這時候,.replace()
就會非常好用!
.replace()
這個函數需要三個參數:
- 開始替代的位置
- 被替代字串的長度
- 替代的字串
int main() { std::string sentence = "The string is amazing!"; sentence.replace(sentence.find("amazing"), 7, "cool"); std::cout << "新的句子: " << sentence << std::endl; return 0; }
注意我們可以搭配前面提到的 .find()
來查看想要替換的字串的位置,這樣我們就不用一個一個數啦!
輸出結果就會是
新的句子: The string is cool!
提取
我們有時候會想要提取一個字串中特定的子字串,這時候我們就可以用 .substr()
。
.substr()
則需要兩個參數:
- 開始位置
- 提取長度
int main() { std::string sentence = "The string is amazing!"; std::string sub_string = sentence.substr(sentence.find("amazing"), 7); std::cout << "提取的字串是: " << sub_string << std::endl; return 0; }
一樣我們可以搭配 .find()
使用,讓我們更方便一些。
這樣的輸出就是
提取的字串是: amazing
總結
在這一章中我們了解到字串的功能,以及兩種不同的字串種類,包括 C-style string 和 std::string。同時我們也了解了一些常用的函數。
在不同的情況下,這兩種分別有各自的優點。在大多數情況下,std::string 還是比較常見,因此剛學程式語言的新手們可以學習 std::string 就好了。
但是對於想要更近一步了解底層運作邏輯的人,以及那些真正想把程式語言學好的人,我會說 C-style string 也是一個一定要學習的知識點!
那就這樣啦我們下一章見~