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

来源:https://www.cnblogs.com/lfcpp/archive/2018/04/27/8965057.html
-Advertisement-
Play Games

《前言》 <cstring>中有一函式 char * strtok ( char * str, const char * delimiters ); 常用來處理C風格陣列的分割問題。其中str是待分割字元串,delimiters 是分割符字元串,舉列說現在有 str[] = "ab,cd,efg,. ...


《前言》

<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的函式了(變成用string::find()或string::find_first_of()來分割字元串),直到最近寫代碼時被同學提醒了一下,結果發現strtok在c++也許更好用(其實就是可以少打一點代碼)(逃)。

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

 

《正文》

  一.  作者本人平時分割字元串的方法

直接用istream& getline (istream& is, string& str, char delim);

 1 stringstream ss("abc,de,fgh,ifkl,.ef,f");  //  #include <sstream>
 2 char delim = ',';
 3 vector<string> typeZero;
 4 string temp;
 5 while (getline(ss, temp, delim)) {
 6     typeZero.push_back(temp);         //  分割不完全,第五個子串是".ef"
 7 }
 8 for (auto &it: typeZero) {
 9     cout << it << endl;      //輸出
10 }

其實std::getline() 是有第三個參數的,指定分隔符,但要註意的是,它是char 不是char* ,只能應付單個分隔符的情況。這時可以考慮用C風格的strtok

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輸出不成問題,若  

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

輸出結果就是

"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);
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 }

 核心代碼只有11到14三行,但這代碼段比較難理解,而且用起來非常不安全,一不小心就會出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 都能用。

 

《後記,總結》

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

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

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

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

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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • http://https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3102410964,4206562405&fm=27&gp=0.jpg ...
  • 題目描述 如圖:有n個重物,每個重物系在一條足夠長的繩子上。每條繩子自上而下穿過桌面上的洞,然後系在一起。圖中X處就是公共的繩結。假設繩子是完全彈性的(不會造成能量損失),桌子足夠高(因而重物不會垂到地上),且忽略所有的摩擦。 問繩結X最終平衡於何處。 註意:桌面上的洞都比繩結X小得多,所以即使某個 ...
  • 1、VO:value object值對象。通常用於業務層之間的數據傳遞,和PO一樣也是僅僅包含數據而已。但應是抽象出的業務對象,可以和表對應,也可以不,這根據業務的需要. 2、 String[]類型列印tosring時顯示為一塊記憶體空間,這時候根據鍵可以判斷值已經成功接收 3、 在對條件進行判斷的時 ...
  • 1、信號和槽 2、自定義信號槽 3、自定義信號槽需要註意的事項 4、信號槽的更多用法 5、案例代碼........ ...
  • 面向對象學習目錄 1 面向對象介紹 2 類、實例、屬性、方法詳解 3 面向過程與面向對象進一步比較 4 類與對象 5 屬性查找與綁定方法 6 小結 7 繼承與派生 8 組合 9 抽象類 10 多態 11 封裝 12 綁定方法與非綁定方法 13 內置方法(上) 14 內置方法(中)之描述符 15 內置 ...
  • 1 class A(object): 2 x = 1 3 4 class B(A): 5 pass 6 7 class C(A): 8 pass 9 10 # 通過父類修改類屬性,子類繼承的類屬性也改變 11 A.x = 3 12 print(A.x, B.x, C.x) 13 # 通過子類修改繼承... ...
  • Python,Python,web,爬蟲,開發,機器學習,人工智慧,數據分析,資料庫,編程,基礎入門 先用電影評論來做情感分析,要看到最後哦 ...
  • 第一種:原生方式 註意點:button標簽的style為submit 第二種:Jquery校驗方式 註意點:button標簽的style為button 流程:點擊提交,首先觸發submitForm()方法,執行校驗及id選擇器,最後提交form表單。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...