跟我學C++中級篇——STL中的字符串

2021-03-02 太平洋工作室

一、字符串

在傳統的C/C++語言中,對字符串的處理比較麻煩,基本都是用char*來操作,而指針又往往是一個初學者的噩夢。STL為了解決這個問題,提供了std::string這個數據結構,其實它就是一個類,不過其提供了常見的對字符串的
操作符的重載,實現在實際工程中經常遇到的字符串的長度計算,拼接和裁剪以及和C類型字符串的轉換。它不算是STL的容器,它只是一個類。

二、字符串的實現

在面試時經常有這種題,讓自己實現一個簡單的字符串類,下面是比較常見的實現的方式:

class mystring
{
    public:
         mystring() : str_(new char[1])
         {
            * str_ = '\0';
         }

         mystring(const char* str): str_(new char[strlen(str) + 1])
         {
            strcpy(str_, str);
         }
     //C++11的右值語義支持
         mystring(mystring&& mstr) noexcept: str_(mstr.str_)
         {
             mstr.str_ = nullptr;
         }
         mystring(const mystring& mstr): str_(new char[mstr.str_size() + 1])
         {
            strcpy(str_, mstr.to_char());
         }
     //C++11可以代替上面函數
     //mystring(const mystring& mstr): mystring(mstr.str_)
     //{}

    //c++11異常控制
         ~mystring() noexcept
         {
            delete[] str_;
         }

         mystring & operator=(mystring mstr)
         {
       //這裡如果使用直接拷貝可能更容易理解
             swap(mstr);
             return *this;
         }


         size_t str_size() const
         {
            return strlen(str_);
         }

         const char* to_char() const
         {
            return str_;
         }

         void swap(mystring& mstr)
         {
            std::swap(str_, mstr.str_);
         }

 private:
     char* str_;
};

這個是參考《Effect c++》和網上c++大牛陳碩的相關資料完善的。基本的方法都類似,主要就在於對內存空間的利用和優化。這個在網上的爭論很多,如果有興趣可以去網上搜索一下。這裡就不再展開了。
庫中的實際實現:

template <class _Elem, class _Traits = char_traits<_Elem>, class _Alloc = allocator<_Elem>>
class basic_string { // null-terminated transparent array of elements
private:
    friend _Tidy_deallocate_guard<basic_string>;

    using _Alty        = _Rebind_alloc_t<_Alloc, _Elem>;
    using _Alty_traits = allocator_traits<_Alty>;

    using _Scary_val = _String_val<conditional_t<_Is_simple_alloc_v<_Alty>, _Simple_types<_Elem>,
        _String_iter_types<_Elem, typename _Alty_traits::size_type, typename _Alty_traits::difference_type,
            typename _Alty_traits::pointer, typename _Alty_traits::const_pointer, _Elem&, const _Elem&>>>;
.
public:
    basic_string(initializer_list<_Elem> _Ilist, const _Alloc& _Al = allocator_type())
        : _Mypair(_One_then_variadic_args_t{}, _Al) {
        auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal());
        _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2);
        _Tidy_init();
        assign(_Ilist.begin(), _Convert_size<size_type>(_Ilist.size()));
        _Proxy._Release();
    }

    basic_string& operator=(initializer_list<_Elem> _Ilist) {
        return assign(_Ilist.begin(), _Convert_size<size_type>(_Ilist.size()));
    }

    basic_string& operator+=(initializer_list<_Elem> _Ilist) {
        return append(_Ilist.begin(), _Convert_size<size_type>(_Ilist.size()));
    }

    basic_string& assign(initializer_list<_Elem> _Ilist) {
        return assign(_Ilist.begin(), _Convert_size<size_type>(_Ilist.size()));
    }

    basic_string& append(initializer_list<_Elem> _Ilist) {
        return append(_Ilist.begin(), _Convert_size<size_type>(_Ilist.size()));
    }

    basic_string& assign(const basic_string& _Right) {
    *this = _Right;
    return *this;
}

basic_string& assign(const basic_string& _Right, const size_type _Roff, size_type _Count = npos) {
    // assign _Right [_Roff, _Roff + _Count)
    _Right._Mypair._Myval2._Check_offset(_Roff);
    _Count = _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count);
    return assign(_Right._Mypair._Myval2._Myptr() + _Roff, _Count);
}
.
}

你會發現庫的實現要複雜很多,但仔細一看,除了各種安全機制和內存控制機制,其實和上面的常見的編寫原理完全一致。這恐怕也是面試者出這個題目的一個主要原因,只要掌握了基本原理,再搞其它的基本都是觸類旁通。

三、字符串的操作

1、插入和連接

void TestString()
{
    std::string s1 = "abcd";
    std::string s2 = "efghi";
    std::string s_add = s1 + s2;
    std::cout << "string content:" << s_add<< std::endl;
    s_add += "123";
    std::cout << "string content:" << s_add << std::endl;
    s_add.insert(3, "0");
    std::cout << "string content:" << s_add << std::endl;
    std::cout << "string size:" << s1.size()<<"  "<<s2.size() << std::endl;

}

2、查找和替換

void FindR()
{
    std::string s = "are you ok!";
    std::cout << "y pos:" << s.find('y') << std::endl;
    s.replace(2,3,"tt");//從索引2,替換三個字符長度為tt
    std::cout << "s content:" << s << std::endl;
    std::cout << s.substr(2, 5) << std::endl;
}
int main()
{
    TestString();
    FindR();
    return 0;
}

運行結果是:

string content:abcdefghi
string content:abcdefghi123
string content:abc0defghi123
string size:4  5
y pos:4
s content:arttou ok!
ttou

這個類的功能有很多,除了上面的這些,還包括追加、擦除、流操作、比較、複製等等。但是相比於其它高級語言缺少太多的相關功能如trim,upper,lower,strip以及向json、xml等轉換的函數。
這裡仍然提醒注意的是,新標準在快速迭代,要注意更新標準的知識。比如在c++11中增加了大量的數字和字符串的直接轉換操作,增加類似棧操作的POP和相關的front等。具體的細節這裡就不再一一展開,用到哪個後,直接查詢一下相關的幫助或者c++開發網站,即可正確使用。

四、總結

總體來說,string這個類在c++的開發應用中是非常普遍的,不管是小白和老鳥,都會經常用到。不過這個類的功能還是有些簡單,比如對字符串的一些分割split,仍然需要自己手動處理。對正則的支持也是一點點開始有的,所以網上有很多個人和公司開發的強大的string類,用來在c++開發中替換標準庫的std::string,這個在實際使用中要注意區別。
另外這個類有一個比較致命的缺點,它沒有區別寬字符集和ASCII字符集等,所以在實際應用中,一定要看清楚應用的場景,小心對待。這個在實際開發中,很多人進過坑。總之,這個類功能不算強大,但是很常用,能在這個類的基礎上用好,並能自己進行二次擴展,就說明你基本掌握了這個類,不要急,慢慢來。

相關焦點

  • 跟我學C++中級篇——STL的學習
    一、c++標準庫C++的標準庫主要包含兩大類,首先是包含C的標準庫的,當然,為了適應c++對一些C庫進行了少許的修改和增加。最重要的當然是面向對象的c++庫;而c++庫又可以分成兩大類,即面向對象的c++庫和標準模板庫,也就是題目中的STL。
  • ​跟我學C++中級篇——STL的容器vector
    向量的構造函數其實就是兩部分模板類型名稱和分配器,而分配器一般使用默認的分析器類型,這個回頭再分配器中再詳細分析。這16個鍊表中每個鍊表中的空閒空間的大小都是固定的8,16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128位元組。那麼實際分配就好說了,大於128的使用第一種,否則使用當前內存池中的鍊表遍歷,查找可用空閒找到後直接使用,否則*8,調用refill再次分配。
  • 跟我學c++中級篇——Linux下的庫相關工具和命令
    -b只顯示執行結果,不顯示文件名-c這個是顯示執行過程,在Linux命令中基本差不多-f顯示文件中文件名的文件類型-F指定分隔符號替換輸出文件名後的默認的「:」分隔符。其下命令最後都要跟目標文件地址和名稱。
  • 跟我學java編程—Java字符串類型
    字符串是有限個字符的有序集合,前面講的char類型用於表示和存儲單個字符,字符串相當於幾個或多個char類型的有序集合,用於表示和存儲多個有序的字符序列。例如:「I want to learn Java programming」、「我要學Java編程」、「3+5=8」、「abc」等等。
  • C 語言會比 C++ 快?
    因為根據以前的經驗,我預計性能不足以滿足我想要編寫的算法類型,但是有一個自定義替代方式就是使用二次探測在一個大的二維數組中實現,這類似於谷歌的 dense_hash_set 設計。它是我通常在不同的代碼庫中為不同的應用程式經常實現和使用的一種哈希表,所以我對它非常熟悉。在 simplifier.cpp 中的實現只有35行代碼 [2],可見這個方式很容易插入並適應手頭的用例。
  • C/C++中字符串與數字轉換
    ,方法一和方法二是c++中的方法,方法三和方法四是C語言庫函數的方法。方法一:c++11中string中添加了下面這些方法幫助完成字符串和數字的相互轉換stod stof stoi stol stold stoll stoul stoull函數原型:float stof (const string& str, size_t* idx = 0);to_string to_wstring
  • C++字符串操作
    庫函數中提供了大量的API函數用於讀寫字符串、拷貝字符串、比較字符串、合併字符串、查找字符串等。  #include<stdio.h> #define MSG "I am a symbolic string constant."
  • C++、java 和 C 的區別
    一、基礎類型c++:** java:** C#:1.以java為準,c++裡面的int short long 像這樣的整型 一般都有unsigned 和signed的區分 ,這個跟java和c# 的區別比較大,但c#裡面有unit ulong ushort 這三種就相當於c++的修飾詞unsigned,當c++李明的變量類型定義unsigned,就默認是整數。
  • C++之字符串類學習總結
    ,所以更加無法獲得字符串類型為了解決這個問題,在c++中,引入了自定義類型,而且可以通過類來完成對字符串類型的定義。那麼C++中的原生類型系統是否包含字符串類型呢?答案是c++中並沒有提供原生的字符串類型。
  • 一文帶你了解c++和c中字符串的使用
    說完了c,那麼對於我們的c++來說,它定義字符串就簡單多了,因為有關鍵字來定義,你一看就知道。那麼下面大家就隨著我的筆步一起來看看究竟吧!有可能有些網友還沒怎麼接觸到c++(c++它是一門面向對象的語言,而c是一門面向過程的語言,所以這裡可能沒接觸過那個面向對象的網友不習慣這個用法,不過還是建議至少要掌握一門面向對象的語言,在這個發展快速的時代,不能太固步自封了(我這裡也是簡單的介紹一下c++中的字符串,不會設計到類和對象什麼的,只是和c語言做個對比)。)1、什麼是字符串?
  • c++的輸入與輸出
    c++輸入與輸出C++ 標準庫提供了一組豐富的輸入/輸出功能,本章將討論 C++ 編程中最基本和最常見的 I/O 操作。輸入輸出並不是c++語言的正式組成成分,c和c++沒有為輸入輸出提供專門的結構。在c語言中輸入輸出是通過調用scanf和printf 實現的,在c++中是通過調用流對象cin和cout實現的。
  • 到底先學哪個才能更好的理解編程,這些你造嗎
    首先很多人覺得C語言是c++的子集,覺得直接學習c++就可以了,還要花費一個學期的時間先學C語言有必要嗎? 本身C語言和c++的編程方法不同,一個面向過程,一個面向對象。而要做出大型的、複雜的、精彩的程序,面向對象的語言就更適合。所以要學習c++這樣的語言。但是,1.c語言是好多學校的基礎課;2.c語言很容易描述算法;3.軟體開發過程中也有很多面向過程的開發,以及模塊化程序設計思想。要學習這些,比起學c++的複雜、困難程度,學c語言就可以達到上述目的。
  • scratch/python/c++,小孩學編程學哪個好?
    有的家長認為學幾天停幾天就前功盡棄了,不是的。多學一天就肯定多一天的收穫,並不會因為中間停了,以前學的就白學了。這個階段是培養興趣階段,非常忌諱強迫性學習,厭學一般都是強迫的結果。scratch和編程貓各有優缺點。scratch是麻省理工的公益軟體,完全免費,到現在位置最新版本為3.0。
  • 使用ollvm自定義簡單的字符串加密
    先貼上測試好的結果: https://github.com/dqzg12300/kOLLVM.git想要寫一個字符串加密的pass,第一步就是先實現一遍c++的算法流程,然後再看一看生成的IR文件,然後再寫對應的加密pass,下面看一個自己實現的簡單c++字符串加密。
  • C++stl簡單使用
    //1.sort函數排序/*using namespace std;int main(){ int a[] = { 2,0,3,1,8,2,4,0 }; sort(a, a + 3);//對前三個數排序 for (int i=0;i<8;++i) { cout << a[i] << " "; } return 0;}*///2.字符串處理
  • 關於Java 字符串的全部,都在這份手冊裡了
    作者 | 沉默王二責編 | 屠敏頭圖 | CSDN 下載自視覺中國String 可以說是 Java 中最常見的數據類型,用來表示一串文本,它的使用頻率非常高,為了小夥伴們著想,我怒肝了一周,把字符串能寫的全都寫了出來。
  • 驚,Java 字符串拼接竟然有這麼多玩法!| CSDN 原力計劃
    value[c++] = 'u';    value[c++] = 'l';    value[c++] = 'l';    count = c;    return this;}2)拼接後的字符數組長度是否超過當前值,如果超過,進行擴容並複製。
  • C++ 的門門道道 | 技術頭條 - CSDN
    運行過程中需要動態增刪的vector,不宜存放大的對象本身 ,因為擴容會導致所有成員拷貝構造,消耗較大,可以通過保存對象指針替代。不管您信不信,我反正是信了。二十三、了解C++新標準,關注新技術,c++11/14/17、lambda,右值引用,move語義,多線程庫等c++98/03標準到c++11標準的推出歷經13年,13年來程序設計語言的思想得到了很大的發展,c++11新標準吸收了很多其他語言的新特性,雖然c++11新標準主要是靠引入新的庫來支持新特徵
  • 在Excel中處理字符串
    ,當時我跟她說:用現有函數實現的可能性非常小,應該需要用到VBA,之後我就沒管這事,自己玩兒去了。按照她的要求,我設計了三個字符串,算是一個簡單的例子,具體見下圖。  二、與字符串相關的函數和運算  我並不想將本文的主題僅僅限定於介紹一個例子,我還有更大的目標,那就是盤點一下如何在Excel中處理字符串。
  • C/C++中字符串常量的不相等性及字符串的Copy
    )  {      if("test"=="test")      {          cout<<"相等";      }      else      {          cout<<"不相等";      }  }  上面的代碼我們測試兩個內容為test的字符串常量是否相等