C++字符串分割(一) 個人總結向,基於strtok()的實作,面向少打一點代碼

《前言》

<cstring>中有一函式

char * strtok ( char * str, const char * delimiters ); 

常用來處理C風格陣列的分割問題。其中str是待分割字符串,delimiters 是分割符字符串,舉列說現在有

str[] = "ab,cd,efg,.hi,zyx";

delimiters[] = ",.!?";

strtok 可以把它分割成

"ab", "cd", "efg", "zyx"。

 

其實這個算是剛入門C語言必學的一個函式,為了上實驗課可以快速解決簡單的分割問題,用迴圈手寫太耗時間,複雜一點的可以用sscanf, sprintf  (這篇不討論這個)。但學了C++知道了一堆更強大的工具,很少去用原來C的函式了(變成用find()來分割字符串),直到最近寫代碼時被同學提醒了一下,才發現strtok在c++也許更好用(其實就是可以少打一點代碼)(逃)。

現在我們來討論它的用法。

 

《正文》

  一.  作者本人的一般使用方法

1 char *p = strtok(str, delim);
2 while (p != NULL) {
3   printf("%s\n", p);  //cout << p << endl; 用cout輸出也不成問題
4   p = strtok(NULL, delim);         
5 }
/*參考了cppreference*/

每一次迴圈都會把第一個分割符變成"\0",所以%s輸出不成問題,輸出結果就是

"ab\n", "cd\n", "efg\n", "zyx\n" 跟上面一樣

不過我們通常都希望把它存下來,我們可以用一個char的二維陣列,如

char strArr[100][10];

把它存起來,但缺點也很明顯,你必須保證分割出來的子串一定少於10。或者用char指針的一維陣列直接指到原字串中各子串的開頭,如

char *strArr[100];

感覺不錯,但畢竟是淺拷貝,原字串一定要保留不能改動,想要深拷貝的話要new / malloc 一個空間,感覺更麻煩了,要先判斷子串長度,這種時候我都直接放棄用strtok, 轉用 sscanf 和 sprintf 的混合雙打 (結果我還是拿出來講了)

 1 #include <iostream>
 2 #include <cstdlib>
 3 using namespace std;
 4 int main() {
 5     char context[] = "abc,de,????fgh,ifkl,.ef,f"; //如果開頭或最後出現分割字符,會出BUG,可能有辦法解決,歡迎提出
 6     char delim[] = "%[^,.!?]%*[,.!?]%n";      //%[^,.!?] 碰到分割符就停, %*[,.!?] 之後把分割符吃掉, %n 目前經過的字符數
 7     char temp[sizeof(context)];
 8     char *strArr[100];
 9     char *p = context;
10     int cnt = 0;
11     for (int bit = 0; ~sscanf(p += bit, delim, temp, &bit); cnt++){  //相當於sscanf(p += bit, "%[^,.!?]%*[,.!?]%n", temp, &bit);  bit的值是子串長加分割符長
12         strArr[cnt] = (char*) malloc(bit + 1);  //因為bit的值還會加上分割符數,所以size會比子串長一點,有辦法解決,這邊交給讀者
13         sprintf(strArr[cnt], "%s", temp);
14     }
15     for (int i = 0; i < cnt; i++) {
16         printf("%s\n", strArr[i]);
17     }
18 }

 不是我想秀什麼,這代碼是比較難理解,而且用起來非常不安全,一不小心就會出bug。現在也不想討論 %[] %*[] 的用法,若覺得不太優的請跳過。

 

二.  用C++ string 類直接save

 1 int main() {
 2     char context[] = "abc,de,fgh,ifkl,.ef,f";
 3     char delim[] = ",.!?";
 4     
 5     char *typeOne[100];
 6     vector<string> typeTwo;
 7     
 8     int cnt = 0;
 9     char *p = strtok(context, delim);
10     while (p != NULL) {
11         typeOne[cnt++] = p;  //typeOne 會被下面的p[0] = toupper(p[0])改掉。
12         typeTwo.push_back(p);  //typeTwo 是深拷貝,不會被改掉,而且很簡單。
13         p[0] = toupper(p[0]);
14         p = strtok(NULL, delim);
15     }  
16 }

typeTwo 並不會因p[0] = toupper(p[0]); 而被改掉,實現了看着更舒爽的深拷貝。(逃)

其實這篇文章的意義就是這個vector<string>::push_back(char*); 一直都沒有發現還有這種操作。把上面的所有問題都解決了(我猜其實速度會很慢)。c++ stl 萬歲~~

 

三.  利用模版,分割不同的型別

 1 template <class T>
 2 vector<T> split(char *str, char *delim) {
 3     vector<T> data;
 4     T temp;
 5     stringstream ss;  //#include <sstream>
 6     char *p = strtok(str, delim);
 7     while (p != NULL) {
 8         ss << p;
 9         ss >> temp;
10         ss.clear();
11         data.push_back(temp);
12         p = strtok(NULL, delim);
13     }
14     return data;
15 }

        實測過int 和 string 都能用。

《後記,總結》

string 可以直接 用 c風格字串賦值可能大家都知道,但還真沒想過把strtok混在一起用,雖然是比較簡單的東西,但我真的嚇到了。真接讓我萌生出打Blog的念頭。

第一次打博客,重新看一遍感覺新手會難理解(因為沒圖又沒有過多解釋),一般人會嫌太簡單(因為重點就只有vector<string>::push_back(char*))。這篇文章純粹拿來試水用, 那就歡迎大家來吐嘈吧。。。

代碼都是用Dev C++ (TDM-GCC 4.9.2)測的。 畢竟在visual studio 上用 <cstring> 感覺就是在給自己找麻煩一樣。

全文只參考了cppreference, 其他都是自己(或憑記憶)打出來的,

沒有本人同意,不得以任何形式轉載。雖然很爛:)

猜你喜欢

转载自www.cnblogs.com/lfcpp/p/8965057.html