.Photo by João Victor Xavier on Unsplash

C++ STL - basic_string

Gary Yeh
14 min readJul 26, 2019

--

這邊整理介紹關於 string 的 member function

  • 建構函數與解構函數
  • 字串長度
  • 字串元素
  • 字串比較
  • 字串修改
  • 字串尋找

建構函數與解構函數

先從建構函數開始說起,要初始化一個 String,一般常見的有這幾種用法:

string(); 
string(const string& str);
string(const string& str, size_t pos, size_t len = npos);
string(const char* s);
string(const char* s, size_t n);
string(size_t n, char c);
string(InputIterator first, InputIterator last);

舉例來說:

string s; //宣告一個空的字串
string t(s); //宣告字串t,值為複製字串 s
string t(s, 0, 5); //值為複製字串 s,從索引位置0開始,指定複製的長度5
string t(cstr); //值為複製 C-String cstr
string t(cstr, 5); //值為複製 C-String cstr,從索引位置0開始,複製長度5
string t(5, 'c'); //值為5個字元 c
string t(s.begin(), s.end()) //值為複製字串 s,參數為Iterator的開始至結束(包含InputIterator first所指的字元至InputIterator last所指的前一個字元)

這邊的 C-String 是指以 null 結束的字元陣列。如果要將 string 轉成 C-String,可以使用 c_str()。

string s = "stringsvalue";
char c_arr[s.length() + 1];
strcpy(c_arr, s.c_str());

如果要宣告一個只含一個字元的字串:

string s('c');

這樣是錯誤的,因為引數的型態並不是 string,也不是 C-String。

可以使用下面的形式:

string s(1, 'x'); //method 1
string s("x"); //method 2

解構函數使用:

~string();

字串長度

跟長度有關的 member function 常見的有以下幾個:

  • size()
  • length()
  • max_size()
  • capacity()
  • reserve(size_t n = 0)
  • resize(size_t n), resize (size_t n, char c)
  • shrink_to_fit()

size() 跟 length() 都是回傳 string 的長度,兩個方法基本上是完全一樣,差別只在於在英文文法上,在形容字或句子的長度時,會使用 length。另外在其他的 STL containers,例如 vector 或 map,也是使用 size() 來取得長度。要使用哪一個方法就由大家自己選擇囉。

max_size() 會回傳 string 可以容納的最大長度。要注意的是,如果你宣告了合法的長度,也是有可能會因為 memory allocate 錯誤而無法使用。

capacity() 會回傳已經 allocated 的空間。

reserve(size_t n = 0) 則是分配空間給字串,例如:

string s;
s.reserve(100); //預先分配長度100的空間
s.size(); //0
s.capacity(); //100或大於100

若 reserve 的參數 n 比目前字串的 capacity 還要大,則呼叫 reserve 後,字串的 capacity 會大於或等於 n,視系統實作而定;若 reserve 的參數n 比目前字串的 capacity 還要小,則呼叫 reserve 後,會發出 shrink 的請求,將 capacity 減少至 size 大小。這邊的請求是一個 non-binding request,也就是雖然你想將多餘的空間還回去,但系統不一定會照做;若 reserve 的參數 n 比 max_size 還要大,則會拋出 length_error 的異常。

resize(size_t n) 是將字串的長度直接改變,與 reserve 改變 capacity 不同。若n 小於目前字串的長度,則會將字串截斷,取前 n 個字元;若 n 大於目前字串的長度,則多出來的空間會填入 value-initialized characters(也就是 null characters)。resize (size_t n, char c)可以傳入第二個引數,多出來的空間都會填入你指定的字元 c。

shrink_to_fit()從c++11開始加入,將 capacity 減少至 size 大小,如同前面提到的一樣,是一個 non-binding request。

字串元素

有兩個方式可以得到字串元素,一個是使用運算子[],另外一個是使用 at() 函數。使用運算子[]的情況,不會檢查索引的有效性。當索引小於或等於字串的長度,是有效的;當索引大於字串長度時,則為未定義行為。當使用 at() 函數時,會檢查傳入的引數,若大於或等於字串長度時,會拋出 out_of_range 的異常。

string s("test");
cout << s[1]; //output: e
cout << s.at(4); //out_of_range exception

字串比較

字串提供了 compare 函數,可以用來比較兩個字串是否相同,或是其字典順序。函數原型如下:

int compare(const string& str) const;

可以傳入參數 string str。

int compare(size_t pos, size_t len, const string& str) const;
int compare(size_t pos, size_t len, const string& str,
size_t subpos, size_t sublen) const;

可以指定被比較字串的起始位置與長度,也可以再傳入比較字串的起始位置與長度。

int compare(const char* s) const;
int compare(size_t pos, size_t len, const char* s) const;

可以傳入參數 C-String,也可以指定被比較字串的起始位置與長度。

int compare(size_t pos, size_t len, const char* s, size_t n) const;

可以傳入字元陣列,因為有指定長度 n,所以不需要 null-terminated。

回傳值如果是0,表示兩者比較結果是相同的;如果回傳值是負數,表示被比較字串的字典順序要在前面,或是所有比較字元都相同,但是被比較字串的長度較短;如果回傳值是正數,表示被比較字串的字典順序要在後面,但是被比較字串的長度較長。例如:

string s = “abc”;
string t = “abcd”;
cout << s.compare(0, 3, t, 0, 3) << endl; // 0
cout << s.compare(0, 3, t, 0, 2) << endl; // 1
cout << s.compare(0, 3, t, 0, 4) << endl; // -1

除了 compare 函數之外,也可以用關係運算子 ==、!=、<、<=、>、>=。大小就是字典順序,在前者為小。若是要比較字元陣列,則必須是 null-terminated,否則為未定義行為。

字串修改

字串修改常用的方法有很多,例如 assign、operator=、swap、erase、insert、append、replace。

  • assign 常用的用法有這些:
assign(const basic_string& str);
assign(const basic_string& str, size_type subpos, size_type sublen = npos);
assign(const charT* s);
assign(const charT* s, size_type n);
assign(size_type n, charT c);
assign(InputIterator first, InputIterator last);

可以看到跟建構函式的參數型態一樣,這邊就不多撰述。使用 assign 會直接把原本的字串替換掉,就是重新賦值的意思。

  • operator= 跟 assign 一樣是重新賦值的功能。
  • swap 可以交換兩個字串,就是彼此替換的意思。
string s = "foo";
string t = "bar";
s.swap(t);
  • erase 的用法有這些:
erase(size_type pos = 0, size_type len = npos);
erase(const_iterator p);
erase(const_iterator first, const_iterator last);

舉例來說:

string str ("The quick brown fox jumps over the lazy dog");
str.erase(10,4); //The quick n fox jumps over the lazy dog
//刪除指定位置開始的長度
str.erase(str.begin()+7);//The quik n fox jumps over the lazy dog
//刪除指定位置的字元
str.erase(str.begin()+8, str.end()-9);//The quik lazy dog
//刪除指定位置開始至結束
  • insert的用法有這些:
insert(size_type pos, const basic_string& str);
insert(size_type pos, const basic_string& str,
size_type subpos, size_type sublen = npos);
insert(size_type pos, const charT* s);
insert(size_type pos, const charT* s, size_type n);
insert(size_type pos, size_type n, charT c);
insert(const_iterator p, size_type n, charT c);
insert(const_iterator p, charT c);
insert(iterator p, InputIterator first, InputIterator last);

舉例來說

string str = “brown fox”;
string str2 = “The “;
string str3 = “quick “;
string str4 = “the lazy dog”;
str.insert(0, str2); //The brown fox
str.insert(4, str3, 0, 6); //The quick brown fox
str.insert(19, “jumps “); //The quick brown foxjumps
str.insert(25, “over my dead body”, 4);
//The quick brown foxjumps over
str.insert(19, 1, ‘ ‘); //The quick brown fox jumps over
str.insert(str.end(), 1, ‘ ‘); //The quick brown fox jumps over
str.insert(str.end(), ‘t’); //The quick brown fox jumps over t
str.insert(str.end(), str4.begin() + 1, str4.end());
//The quick brown fox jumps over the lazy dog
  • append 常見的用法有這些:
append(const basic_string& str);
append(const basic_string& str, size_type subpos,
size_type sublen = npos);
append(const charT* s);
append(const charT* s, size_type n);
append(size_type n, charT c);
append(InputIterator first, InputIterator last);

可以看成是 insert 在最後面的用法。

  • replace 常見的用法有這些:
replace(size_type pos, size_type len, const basic_string& str);
replace(const_iterator i1, const_iterator i2,
const basic_string& str);
replace(size_type pos, size_type len, const basic_string& str,
size_type subpos, size_type sublen = npos);
replace(size_type pos, size_type len, const charT* s);
replace(const_iterator i1, const_iterator i2, const charT* s);
replace(size_type pos, size_type len, const charT* s, size_type n);
replace(const_iterator i1, const_iterator i2,
const charT* s, size_type n);
replace(size_type pos, size_type len, size_type n, charT c);
replace(const_iterator i1, const_iterator i2, size_type n, charT c);
replace(const_iterator i1, const_iterator i2,
InputIterator first, InputIterator last);
replace(const_iterator i1, const_iterator i2,
initializer_list<charT> il);

可以看成是 erase 加上 insert 的用法。

字串尋找

跟字串有關的函式有 find、rfind、find_first_of、find_first_not_of、find_last_of、find_last_not_of。

  • find 可以提供目標字串,在原始字串當中尋找相等於目標字串,若有找到第一個符合的,回傳其在原始字串中的位置;若沒有找到,則回傳 npos。find 的函式原型有這些:
size_type find (const basic_string& str, size_type pos = 0) const noexcept;
size_type find (const charT* s, size_type pos = 0) const;
size_type find (const charT* s, size_type pos, size_type n) const;
size_type find (charT c, size_type pos = 0) const noexcept;

pos 指的是從哪裡開始尋找。

當尋找目標是 const charT* s 時,若 n 有指定,則需相等於目標的前 n 個字元;若 n 沒有指定,則 s 必須是 null-terminated。

舉例來看:

string str = “abcdefg”;
string t = “cde”;
string::size_type found;
found = str.find(t, 1); //2
found = str.find(“fg”, 1); //5
found = str.find(“bcd”, 0, 3); //1
found = str.find(‘c’, 0); //2
  • rfind 與 find 相反,是從指定的位置往前尋找(沒有指定則是從原始字串的尾端開始)。可以想成是有一個 sliding window 由前往後與由後往前的差別。
  • find_first_of 的用法是尋找原始字串當中,含有任一個目標字串當中的字元時,回傳其位置。與 find 不同,find 需要整個目標字串符合,而find_first_of 只需要其中一個字元符合。
string str (“The quick brown fox jumps over the lazy dog”);
string::size_type found = str.find_first_of(“aeiou”); //2
  • find_first_not_of 就跟 find_first_of 相反,是尋找第一個沒有含有目標字串當中的字元。
  • find_last_of、find_last_not_of 就是往前尋找的用法。

--

--