開啟章節選單
字串
極為常見在 C++ 內建的 STL 函式庫中,有內建一個標頭檔叫 <string>
,這個標頭檔裡面有不少跟字串相關的函式,這些函式在字串處理相關問題上十分實用,接下來會介紹幾個字串相關基本語法及常用的函式!
宣告
最簡單的宣告如下:
string 字串名稱;
在上面這種情況下,你的字串會是空字串。那如果想給予字串一個初始值呢?有幾種方法可以達成:
// 括號裡面放字串 string s1("Hello World!"); // 宣告後直接給值 string s2 = "Hello World!"; // 也可以直接複製另一個字串 string s3 = s1;
如果你有學過其他的程式語言,你可能會習慣用單引號來表示字元,但在 C++ 中,字串只能用雙引號表示。
// 不能用單引號 :( string s1 = 'Hello World!'; // 當然,什麼都不加就更慘了 string s2 = Hello World!;
單引號在後面還有其他用途,不要跟字串用的雙引號搞混!
輸入
string s1; cin >> s1;
如果你想要輸入一個字串的話,有一個比較容易被忽略的地方就是,當你輸入字串時,如果你的字串中有空白、換行或是 Tab,那麼之後的部分就會被當作下一個變數的輸入。以上面的程式來說,如果你輸入 Hello World
,那 s1 就會是 Hello
。如果想知道如何避免這個問題,請參閱前面的 整行輸入。
在 printf
中輸出
有一些新手可能會習慣用 printf
,不過當你想使用 printf
來輸出字串時,你會發現這樣寫是不行的:
string s = "APCS Guide"; printf("%s is cool", s);
這樣寫會出現錯誤,因為 printf
是 C 語言的函式,而 string
是 C++ 的類別,所以這兩者不能直接混用。如果你想要在 printf
中輸出字串,你得先把 string
透過 .c_str()
轉換成 C String(char*
):
string s = "APCS Guide"; printf("%s is cool", s.c_str());
字串長度
當想取得字串長度時,只要在字串名稱後加 .size()
就可以取得:
string s1 = "Hello"; int n = s1.size(); cout << n << endl; // 5
size()
的回傳值是 size_t
,這是一種 unsigned
的整數型態,所以如果你要將 size()
的回傳值做運算時要非常小心。如果陣列前是空的,size()
會回傳 0
,那如果你剛好將 size()
的回傳值減 1
,那結果就會是一個很大的數字,這可能會造成不可預期的錯誤。
改變長度
string s = "abcde"; s.resize(3); cout << s; //輸出結果:abc
以上面的 code 為例,如果只想保留 s 的前三個字,那就呼叫 .resize(3)
,其中 ()
裡裝的就是要保留的長度。最後輸出時,s 就變成 abc
了。舉例來說,如果想把字串砍半怎麼辦?只要結合剛剛的 .size()
就好了!
s.resize(s.size() / 2);
那如果想讓它變長呢?
s.resize(s.size() + 3); // abcde___ s.resize(s.size() + 3, 'a'); // abcdeaaa
如果純粹只是想讓它變長,原理就跟剛剛變短一樣,只是後面變長的空間就會變成空字元。如果想再指定多出來的長度要是什麼,只要在後面多打你指定的字元就好。
取得或修改字串中的某個字元
如果你想取得字串中的某個字元,只要在字串名稱後加上中括號,並在中括號中填入索引值(也就是你要取得的位置)就可以了。注意,索引值是從 0 開始的:
string s = "abcde"; cout << s[0] << endl; // a cout << s[2] << endl; // c
如果你想修改字串中的某個字元,也是差不多的方法:
string s = "abcde"; s[0] = 'z'; cout << s << endl; // zbcde
請注意,對於一個長度為 n
的字串,合法的索引值是 0
到 n - 1
,這點常常被很多初學者忽略!
如果你試圖取得或修改超出字串範圍的字元,程式可能會出現不可預期的結果,所以在取得或修改字元之前,最好先確定索引值是合法的。
比較
就像字元一樣,字串也可以用 ==
、!=
、<
、>
、<=
、>=
來比較。比較過程是從字串的第一個字元開始比較(依照 ASCII),然後比較第二個字元,一直比到有一個字元不同為止,如果都一樣,就會比到長度較短的那個字串為止。這種比較方式就叫做「字典序」。
以下是一些簡單的例子:
string s1 = "abc"; string s2 = "abcd"; string s3 = "cb"; string s4 = "abd"; cout << (s1 > s3) << endl; // false 因為第一個字元就不一樣,且 c 比 a 大 cout << (s1 < s2) << endl; // true 雖然前面都相同,但 s1 長度較短 cout << (s1 < s4) << endl; // true 比到第三個字元就會發現 d 比 c 大
加法
你沒看錯!字串可以加在一起!但是要注意兩個字串相加的前後順序,例如:
string s1 = "123", s2 = "abc"; string s3 = s1 + s2; // 123abc string s4 = s2 + s1; // abc123 s1 += s2; // 123abc
如果你嘗試過這樣的寫法:
cout << "123" + "abc" << endl;
你會發現這樣寫竟然會出現錯誤?!不是說好字串可以加在一起嗎?為什麼這樣寫會出錯呢?
這是直接透過這樣語法宣告的 "123"
和 "abc"
都是「C String」(古老 C 語言中的字串表示方法,也就是 char*
),而不是 C++ 的 string
,所以這樣寫會被當作兩個字元陣列的記憶體位置相加,而不是字串相加(C++ 中的字串才有更多的功能!)。如果你想要這樣寫,你可以這樣寫:
cout << string("123") + "abc" << endl;
或者是這樣寫:
cout << "123"s + "abc" << endl;
這個 s
是 C++11 之後的語法,可以將 char*
轉換成 string
,跟 1ll
可以將 int
轉換成 long long
一樣的概念。
子字串
子字串的定義是在一個字串 s
中,從某個位置開始連續的 i
個字元組成的字串 t
,就稱 t
為 s
的一個長度為 i
的子字串。例如 abcde
中,a
、bc
、甚至 abcde
本身都是 abcde
的子字串,但 ae
不是。
那要如何生成一個字串的子字串呢?使用 .substr
就行了!例如:
string S = "abcde"; string S1 = S.substr(2, 2); // cd string S2 = S.substr(2); // cde
其中,第一個參數是子字串的起始位置,第二個參數是子字串的長度,如果沒有指定長度,就會直接複製到字串尾。
建立重複字元的字串
這邊要來介紹一個小技巧,如果你想要建立一個重複某個字元的字串,可以使用以下的方法,雖然這很明顯可以直接用回圈來達到一樣的效果,但這樣寫起來比較簡潔:
string s(5, 'a'); cout << s << endl; // aaaaa
或者用賦值的方式:
string s = string(5, 'a'); cout << s << endl; // aaaaa
尋找子字串
如果你想要在一個字串中尋找另一個字串,可以使用 .find
函式,這個函式要傳入一個要尋找的子字串,會回傳第一個找到的子字串的起始位置,如果找不到就會回傳 string::npos
:
string s = "Hello World!"; int pos = s.find("World"); if (pos != string::npos) { cout << pos << endl; // 6 } else { cout << "Not found" << endl; }
string::npos
是一個特殊的常數,代表找不到的意思,這個常數的型態是 size_t
,值是一個很大的數字,通常是 18446744073709551615
,這個數字是 2^64 - 1
,同時也是 unsigned long long
的最大值。
判斷是否為空字串
string
有一個函式叫做 .empty()
,可以用來判斷一個字串是否為空字串:
string s1 = "Hello"; string s2; cout << s1.empty() << endl; // 0 cout << s2.empty() << endl; // 1
不過,如果你想要自己實作的話,也可以直接用 s.size() == 0
來判斷就好。
數字轉字串
如果你想要把一個數字轉成字串,C++ 提供了一個非常方便的函式叫做 to_string
,這個函式要傳入一個整數或浮點數,會回傳一個對應的字串:
int n = 123; string s = to_string(n); cout << s << endl; // 123 double d = 3.14; string s2 = to_string(d); cout << s2 << endl; // 3.14 cout << to_string(12345) << endl; // 12345
字串轉數字
如果你想要把一個字串轉成數字,C++ 也提供了一個函式叫做 stoi
,這個函式要傳入一個字串,會回傳一個對應的 int
數字:
string s = "123"; int n = stoi(s); cout << n << endl; // 123 cout << n + 1 << endl; // 124 cout << stoi("12345") * 2 << endl; // 24690
那如果是想轉換成 float
、double
或者是其他的型態呢?C++ 對每個型態都提供了對應的函式,如下表所示:
型態 | 函式 |
---|---|
int | stoi |
long | stol |
long long | stoll |
unsigned long | stoul |
unsigned long long | stoull |
float | stof |
double | stod |
long double | stold |
這邊要注意的是,如果你的字串不是合法的數字,那這些函式會拋出一個 invalid_argument
的錯誤,所以在使用之前,最好先確定你的字串是合法的數字。
但不合法的數字不只包括非數字字元,也包括超出範圍的數字,例如 stoi("12345678901234567890")
這樣的數字就會超出 int
的範圍,所以也會拋出錯誤,因此如果數字過大的話,要記得改用 stoll
或 stoull
。
replace
函式
如果你有學過其他程式語言的話,你可能會習慣用 replace
來取代字串中的某個子字串,但在 C++ 中,replace
的功能是取代字串中的某個範圍的字元,而不是取代子字串。
無論如何,這個函式要傳入三個參數,分別是要取代的起始位置、要取代的長度以及要取代的字串:
string s = "Hello World!"; s.replace(6, 5, "APCS"); cout << s << endl; // Hello APCS!
小測驗
字串是根據什麼來比較大小的?