[程式設計] C++ 的字串切割
簡介
C++的字串切割,我看過解釋最詳細的是這篇。推薦大家看看。字串切割是撰寫程式中常有的需求,舉例來說
- 將一個字串依空白切割成數個子字串。如 "15 19 12" 切成 "15","19" 以及 "12"。
- 將字串依規定的符號切割成數個子字串。如 "15,19,12",切成 "15","19" 以及 "12",這是切割符號為逗點。
- 將字串依規定的符號切割成數個子字串,但這個符號可以有多種。例如 "15,19!12",切成 "15","19" 以及 "12",這是切割符號為 , 以及 !。
這篇文章,要談的,是最後一種的切割方式。
如果只有一種切割符號,那直接用 stringstream 以及 getline 就可以解決。
但多個切割符號,就很麻煩了。函式庫內建的 stringstream 以及 getline 並沒有法子達到這個功能,所以只能自己動手來搞定了。
好在 C++ 的字串有提供 find_first_of 這個字串的尋找函式,可以幫我們省下很多麻煩。
題外話,由於字串切割真的是很重要,而且常見的任務。這種自幹的作法也是會讓程式設計師增加很多麻煩,所以著名的 Boost 函式庫就提供了功能強大的 split 函式。祈禱這個功能可以加入下一版的 C++ 中囉 (例如 C++ 20 中)。
好了,要開始進入正文了,但先說明,底下版本會略掉「空字串」。
這什麼意思呢?看一下底下的例子。
"12,|15 19"
分割符號為 逗點,空白,以及 |。
可以看到 12 和 15 間夾了 ,|
這兩個符號間,夾了一個空字串,這個字串在底下的程式中,是會被忽略的。
find_first_of
find_first_of 是 C++ 字串的一個成員函式,它最常用的形式為
find_first_of("切割字元", 位置);
舉例來說
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include<string> | |
#include<algorithm> | |
using namespace std; | |
int main() | |
{ | |
string s = "15,|12 19"; | |
int pos = s.find_first_of(" ,|", 0); | |
cout << pos << endl; | |
} |
在上面的例子中, 我們要在 s 中,找到 " ,|" 中第一個出現的字元。
" ,|" 包含三個字元,即 空白,逗點,以及 |。 C++ 只要遇到其中一個,就會中斷尋找,並回傳所在的位置。
所以這個例子中, pos 為 2,因為第一個出現的分割字元為 逗點,它出現在字串的第 2 個位置。
substr 常用的形式為
substr( 位置, 長度 )
它會在字串中,由位置指定處,切出某個長度的子字串。
還是搭配個例子吧。
在上面的例子中, current 代表開始搜尋的位置。它的初始值為 0。
pos 為第一個分割字元的位值,其值為 2。
所以 pos - current 便為 2。也就是第一個子字串的長度。
因此第12行便印出 15 了。
" ,|" 包含三個字元,即 空白,逗點,以及 |。 C++ 只要遇到其中一個,就會中斷尋找,並回傳所在的位置。
所以這個例子中, pos 為 2,因為第一個出現的分割字元為 逗點,它出現在字串的第 2 個位置。
搭配 substr 來切出子字串
接著我們就可以用 substr 來把字串中的子字串找出來。substr 常用的形式為
substr( 位置, 長度 )
它會在字串中,由位置指定處,切出某個長度的子字串。
還是搭配個例子吧。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include<string> | |
#include<algorithm> | |
using namespace std; | |
int main() | |
{ | |
string s = "15,|12 19"; | |
int current = 0; | |
int pos = s.find_first_of(" ,|", current); | |
cout << pos << endl; | |
cout << s.substr(current, pos - current)<<endl; | |
} |
pos 為第一個分割字元的位值,其值為 2。
所以 pos - current 便為 2。也就是第一個子字串的長度。
因此第12行便印出 15 了。
把 while 加上去,切出所有的子字串吧!
瞭解這些後,我們就可以把前述的程式碼放到 loop 中,不斷的切割,就可以切出所有的子字串了。
在底下的例子中,會把所有的子字串都放到一個 vector 中,並且忽略空字串。
上面的程式碼中,我把字串切割的功能寫成了函式。
由 16 行開始,splitStr2Vec 會把字串中的子字串切割出來,並放入 vector 中。
current 變數代表搜尋的起點。
它的邏輯蠻簡單的,只要切出一個子字串後,若它不是空字串,就放到 vector 中。
然後更新 current 的位置,也就是改變搜尋的位置,由目前分割字元的「下一個」字元開始搜尋。
這個函式還有可以改善的地方。
例如我把 「分割字元」 寫死在函式中了,但其實分割字元可以當成參數傳給函式。
在底下的例子中,會把所有的子字串都放到一個 vector 中,並且忽略空字串。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include<string> | |
#include<algorithm> | |
#include<vector> | |
using namespace std; | |
void splitStr2Vec(string s, vector<string>& buf); | |
int main() | |
{ | |
string s = "15,|12 19"; | |
vector<string> buf; | |
splitStr2Vec(s, buf); | |
for (string tmp : buf) | |
cout << tmp << endl; | |
} | |
void splitStr2Vec(string s, vector<string>& buf) | |
{ | |
int current = 0; //最初由 0 的位置開始找 | |
int next; | |
while (1) | |
{ | |
next = s.find_first_of(" ,|", current); | |
if (next != current) | |
{ | |
string tmp = s.substr(current, next - current); | |
if (tmp.size() != 0) //忽略空字串 | |
buf.push_back(tmp); | |
} | |
if (next == string::npos) break; | |
current = next + 1; //下次由 next + 1 的位置開始找起。 | |
} | |
} |
由 16 行開始,splitStr2Vec 會把字串中的子字串切割出來,並放入 vector 中。
current 變數代表搜尋的起點。
它的邏輯蠻簡單的,只要切出一個子字串後,若它不是空字串,就放到 vector 中。
然後更新 current 的位置,也就是改變搜尋的位置,由目前分割字元的「下一個」字元開始搜尋。
結論
這篇文章談到了如何依照多個「分割字元」來切割子串。並且把切割字串的功能寫成了函式。這個函式還有可以改善的地方。
例如我把 「分割字元」 寫死在函式中了,但其實分割字元可以當成參數傳給函式。
可以解釋一下這段的作用嗎?
回覆刪除for (string tmp : buf)
cout << tmp << endl;
作者已經移除這則留言。
刪除這是一個C++比較新版的方法,可用冒號走訪vector各元素。
刪除感謝
回覆刪除謝謝分享!
回覆刪除不過這一段似乎有BUG哦
next = s.find_first_of(" ,|", current);
if (next != current)
{
string tmp = s.substr(current, next - current);
if (tmp.size() != 0) //忽略空字串
buf.push_back(tmp);
}
當 find() return -1 的時候,您的 substr 會是 s.substr(current, -1-current)
所以應該另外判斷!
Learn and apply all the latest language code with us here codeprozone.
回覆刪除