概述
string類型是c++的字符串類型,其繼承自basic_string類。
使用string需要導入頭文件#include <string>,并且在命名空間std下。
c++string是否是寫時復制? (像Qt的string一樣)?經過自己的測試,推斷,c++的方式不是寫時復制,其在賦值的階段就會開辟新的空間。
?1.? 構造string類對象
?? ?std::string str1("Hello World");
?? ?std::string str2;? ? std::string str3(20, '-');? ? ? ? ? ? ? ? ? ? ? ? ? ?// str3包含20個中劃線
?? ?std::string str4({ "Hello" }); ? ? ? ? ? ? ? ? ? ?// 使用初始化列表?? ?std::string str5(str1, 5);? ? ? ? ? ? ? ? ? ? ? ? ?//? 拷貝從str1第5個字符開始后面的字符
?? ?std::cout << str5 << std::endl;? ? ? ? ? ? ??// 輸出: World(注意W前面有個空格字符)? ? const char* str = "Jack"; ? ? ? ? ? ? ? ? ? ?
?? ?std::string str6(str, 2);? ? ? ? ? ? ? ? ? ? ? ? ? ?// 拷貝str字符串的前兩個字符
?? ?std::cout << str6 << std::endl;? ? ? ? ? ? ? // 輸出:Ja
2. ?string的訪問
- ?使用下標
std::cout << str1[0] ? ?<< std::endl; ? ? ? ? ? ? ? ? ? ? ? ? ?// 輸出: H ?使用下標運算符訪問第一? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 個字符,注意越界
std::cout << str1.at(0) << std::endl; ? ? ? ? ? ? ? ? ? ? ? ?// 和下標運算符類似- 使用back()和front()
std::cout << str1.back() << std::endl;? ? ? ? ? ? ? ? ? ?// 輸出d,返回最后一個字符
str1.back() = 'e';? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 可以通過back()返回修改對應位置? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?字符
std::cout << str1 << std::endl;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // Hello Worlefront()的使用方式類似。
- 使用迭代器? ?--? ?其它迭代器的使用和前面容器是類似的
std::string str1("Hello World");
for (auto i : str1) {
?? ?std::cout << i << " "; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// H e l l o ? W o r l d
}這是c++11新增的for循環,可以對擁有迭代器的類方便遍歷,當然使用迭代器還可以像之前容器那樣去遍歷(不了解可以去容器章節查看),當然那樣寫起來比較麻煩。
3. string的長度?
- ??? ?std::string str1("Hello World");
? ??
?? ?std::cout << str1.length()?? ??? ? ? << std::endl; ? ? // 11
?? ?std::cout << str1.size() ? ? ? ? ? << std::endl; ? ? // 11
?? ?std::cout << strlen(str1.c_str()) ?<< std::endl; ? ? // 11??使用strlen需要導入頭文件#include??<string.h> 或者 #include <cstring>? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
注意:? ? ? ? ? ? ? ? ?
c++所有計算字符串長度的函數,計算出的都是字符串的字節數。也就是所如果我們去計算"我愛你"這樣的中文字符串的長度,上面的函數都會返回6。因為在程序中一個中文一般占兩個字節。
4. c++字符串轉化為C語言字符串?
?在使用一些函數的時候,尤其是c的函數,我們需要傳入的字符串是C語言的字符串類型:char*,或者是const char*。
這時候我們可以通過c_str()函數將c++字符串轉化為C語言字符串。str.c_str();? 其會返回一個const char*的字符串指針。
為什么呢??string是c++關于處理字符串封裝的一個類,簡單來看,其內部就是存在一個char*的指針,然后內部會為它動態開辟空間。并且類中包含了一系列處理字符串的函數。
你使用c_str()它只是將這個指針給你返回回來了。
5. 字符串的容量?
std::string str1;?
str1.reserve(20);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 開辟20個空間
std::cout << str1.capacity() << std::endl;? ? ? ? ? ? ? ?// 打印string目前的容量
容量是指當前字符串對應的空間有多大,它并不等于字符串的長度。
它會根據內部的算法,進行空間的動態擴充,reserve是指定string開辟20個空間,但是實際打印出來并不是,因為它會根據內部的算法進行擴充。(感覺和vector容器是一樣的)
6. 字符串的查找?
- find()?
此函數可以用來查找string中存放字符串的特定字符和字串,并且可以指定查找位置。- 成功找到:? ?返回找到的第一個字符或者首字符的下標
沒有找到:? ?返回npos
如果指定查找的范圍超出字符串的范圍:? 結果未定義std::string str1("Hello World");
using size = std::string::size_type;? ? ? ? ? ? ?// 給strng內置的類型取一個別名,方便使用size ret = str1.find("He");? ? ? ? ? ? ? ? ? ? ? ? ? ? // 查找子串"He",找到返回字段首字符的下? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 標,沒有找到返回npos
std::cout << ret << std::endl;size ret1 = str1.find("He", 5);? ? ? ? ? ? ? ? ? ? ? // 從下標為5的位置開始查找子串"He"找到返? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 回字段首字符的下標,沒有找到返回npos
size ret2 = str1.find('c');? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 查找字符c是否存在,存在返回下標找到返? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 回字段首字符的下標,沒有找到返回npos
std::cout << ret2 << std::endl;??size ret3 = str1.find("W", 5);? ? ? ? ? ? ? ? ? ? ? ?// 從下標為5的位置開始,查找字符‘w’是? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 否存在,找到返回字段首字符的下標,沒? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 有找到返回npos
?std::cout << ret3 << std::endl;
rfind()就是從反方向查找,用法和find()類似
find_first_of() 查找第一次出現的子串或者字符
std::string str1("Hello World");
str1.find_first_of('H');
str1.find_first_of('H',6);? ? ? ? ? ? ? ? ? ? ? ? ? // 從下標為6的位置開始查找
str1.find_first_of("He",6);? ? ? ? ? ? ? ? ? ? ? // 從下標為6的位置開始查找
str1.find_first_of("He");?
- find_last_of() 查找最后一個出現的子串或者字符? --? 用法和上面的一樣
- find_first_not_of() 查找第一個出現的與查找子串或者字符不匹配的下標
size_t ret = str1.find_first_not_of('s');? ?// 第一個與s不匹配的下標為0
- find_last_not_off() 和first相反
注意:? 1.? ? 如果查找的是子串,那么必須整個串都一樣,否則就是不匹配
? ? ? ? ? ?2.? ? 后面的四種方法也可以使用npos來判斷是否查找成功
6.1 如何判斷查找成功和失敗
?我們上面說到,查找成功返回對應子串或者字符的首字符下標,查找失敗返回npos。npos最開始被定義為-1,但是其并不是一直是-1,所以find()查找失敗不一定都返回-1。
看下面代碼:?std::string str1("Hello World");
using size = std::string::size_type;size ret = str1.find('s'); ? ? ? ? ? ? ? ? ? ? ? ? // 's'字符在字符串中沒有,所以其肯定找不到
if (ret == str1.npos) { ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 輸出: ?沒有找到
?? ?std::cout << "沒有找到" << std::endl;
}
else {
?? ?std::cout << str1[ret] << std::endl;
}
所以判斷是否查找失敗,我們可以使用其返回值(返回值類型可以是string內置的size_type也可以直接使用size_t)與npos進行比較,如果等于npos就是沒有找到,找到就返回所在下標。
7. 判斷string是否為空
- ?str1.empty();? ? ? ? ? ? ? ? //? 為空返回true,不為空返回false;
8. string的比較?
- ?直接使用比較運算符進行比較? ?--? 比較規則和C語言字符串類似
std::string str1("Hello World");
std::string str2("Hello");if (str2 < str1) {
?? ?std::cout << "str2小于str1" << std::endl;
}- 使用compare()函數進行比較
std::string str1("Hello World");
std::string str2("Hello");
// str1和str2整體進行比較,相同返回0,str1小返回-1,str1大返回+1
str1.compare(str2); ? ? ? ? ? ? ? ? ?
// str1下標為[0,5)字符組成的字符串與str2進行比較,返回值和上面一樣
int ret = str1.compare(0,5,str2);? ??
// str1下標為[0,5)字符組成的字符串與str2下標為[0,5)的字符組成的字符串進行比較
int ret2 = str1.compare(0, 5, str2, 0, 4);注意:? 使用compare()可以指定比較兩個字符串對應區間內的字符串,但是如果我們指定的區間大于了原來字符串的長度,那么就默認使用字符串的size(),也就是字符串的長度。
舉例:?int ret = str1.compare(0,20,str2);? ? ? ?// 我們這里指定的是str1的[0,20)的字符組成的字符串與str2進行比較,但是我們str1的長度是11,明顯20超出了str1的長度,這時候compare()函數就會使用str1.size()也就是str1的長度范圍的字符串,也就是[0,11),也就是整個字符串。
9. string的添加?
- insert()函數
std::string str1("Hello World");
std::string str2("編程");
?str1.insert(5, "開心");?? ??? ??? ??? ??? ??? ?// 在下標為5的位置插入"開心"
str1.insert(0, "美麗");?? ??? ??? ??? ??? ? ? ?// 在下標為0的位置插入"美麗"
str1.insert(str1.size(), "漂亮"); ? ? ? ? ?// 在字符串的尾部添加"漂亮",因為尾部下標就是? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 字符串的長度str1.insert(str1.begin(),'z');? ? ? ? ? ? ? ?// 在字符串開頭的位置插入字符'z'
str1.insert(str1.end(), 'z');? ? ? ? ? ? ? ? // 在字符串尾部的位置插入字符'z'str1.insert(str1.begin(), 5, 'a');? ? ? ? ?// 在尾部添加5個a,首部也類似
str1.insert(0, str2);? ? ? ? ? ? ? ? ? ? ? ? ? // 在下標為0的位置添加str2std::cout << str1 << std::endl;
?使用append()函數對字符串進行尾部添加
?str1.append("123"); ? ? ? ? ? ? ? ? ? ?// 尾部添加字符串"123"
?str1.append(str2);? ? ? ? ? ? ? ? ? ? ? // 尾部添加字符串str2直接使用+運算符進行拼接
str1 += "123";? ? ? ? ? ? ? ? ? ? ? ? ? ? // 尾部拼接
str1 = "123" + str1;? ? ? ? ? ? ? ? ? ?// 首部拼接
注意:? 使用+直接拼接必須有一個string類對象,不能直接對C語言的字符串進行+,因為那都是地址。
10. string的刪除?
使用下標? --? 刪除對應下標以及之后的所有數據或者相應數量的數據
? ? ? ? ? ? ? ? ? ? 返回值:? ? 返回刪除后的string對象
std::string str1("Hello World");
std::string str2;str2 = str1.erase(2);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 刪除下標為2開始的所有字符
std::cout << str1 << std::endl;
std::cout << str1 << std::endl;? ? ? ? ? ? ? ? ? ? // 輸出和str1是一樣的
str1.erase(2,3);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??// 刪除從下標為2開始的三個字符,同理也是? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?返回刪除后的string對象(和上面是一樣的)
std::cout << str1 << std::endl;
?使用迭代器? ?--? ?刪除迭代器指向數據,或者迭代器范圍的數據
str1.erase(str1.begin()); ? ? ? ? ? ? ? ? ?// 刪除第一個字符,迭代器end()用于刪除最后一個
? ? ? ? ? ? ? ? ? ? ? ? ? 返回值:? ?返回指向刪除字符或者字符串的后一個位置字符的迭代器,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?如果刪除的字符后面已經沒有字符了,返回end()迭代器(就是? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?指向最后一個元素的下一個位置的迭代器)
std::cout << str1 << std::endl;
?str1.erase(str1.begin(),str1.begin()+1); ? ? // 刪除迭代器范圍的字符[beg,end)
std::cout << str1 << std::endl;注意:? 還是和之前容器所說的一樣,使用erase結合循環刪除特定元素的時候,應該注意下一個迭代器的位置是erase()的返回值。(具體可以看之前容器部分)
11. string字符串的替換
- ?std::string str1("Hello World");
?std::string str2("Good Good Study");
??str1.replace(1, 2, "123"); ? ? ? ? ? // 從下標為1的字符開始,共計兩個字節替換成"123"
?str1.replace(1, 2, str2);
?std::cout << str1 << std::endl;
??str1.replace(str1.begin(), ++str1.begin(), "你好"); ? ? ? // 將迭代器范圍[beg,end)內的字? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?符替換成"你好"
str1.replace(str1.begin(), ++str1.begin(), str2);
std::cout << str1 << std::endl;
?str1.replace(0, 2, str2, 0, 3); ? ? ? ? ? ?// 將str1從下標為0的字符開始,共計兩個字符替換? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?成str2從下標為0開始,共計3個字符的字符串
std::cout << str1 << std::endl;
?str1.replace(3, 2, "Day Day Up", 2);
std::cout << str1 << std::endl; ? ? ? ? ? ?// 將str1下標為3的字符開始,共計兩個字符替換成? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 后面字符串的前2個字符
?str1.replace(str1.begin(), ++str1.begin(), "我們", 2);
std::cout << str1 << std::endl;返回值:? 其返回的就是替換之后的string對象
12. 使用assign()函數進行賦值?
使用方法和前面的方法是類似的,每次使用assign給字符串賦值的時候,都會覆蓋原來字符串的值。?
std::string str1;
std::string str2("ch");
str1.assign(5, 'a');? ? ? ? ? ? ? ?// aaaaa
str1.assign("123");? ? ? ? ? ? ?// 123
str1.assign("123",2); ? ? ? ? ?// 賦值字符串的前兩個字符? 12
str1.assign(str2);? ? ? ? ? ? ? ? // ch
str1.assign(str2,1);? ? ? ? ? ? ?// 使用str2下標為1的字符賦值str1? h
13. resize()重新設置字符串長度?
和容器中resize()的用法是類似的。
接收兩個參數,第一個參數是一個整數,表示長度變成多少
? ? ? ? ? ? ? ? ? ? ? ? ?第二個參數是一個字符,用于指定長度變長的時候填充的字符,默認以空格? ? ? ? ? ? ? ? ? ? ? ? ? ?填充?
我們通過第一個參數可以指定擴充或者縮短字符串的長度,第二個參數只有擴充長度的時候才有效。
?std::string str1;
std::string str2("ch");
??
str1.resize(5,'a');
std::cout << str1 << std::endl;? ? // aaaaastr2.resize(1);
std::cout << str2 << std::endl;? ?// c
?14. string類型轉換為其它類型
- ?std::stoi();? ? 將字符串轉換為int類型
?std::stol();? ? 將字符串轉換為long類型
?std::stoll();? ?將字符串轉換為long long類型- std::stoul();? ?將字符串轉換為unsigned long類型
std::stoull();? 將字符串轉換為unsigned long long類型- std::stof();? ? 將字符串轉換為float類型
std::stod();? ?將字符串轉換為double類型
std::stold();? 將字符串轉換為long double類型例子:? 用法都類似
??? ?std::string str1("123");
?? ?std::cout << std::stoi(str1) << std::endl; ? ? // 123?? ?std::string str2("123abc");
?? ?std::cout << std::stoi(str2) << std::endl; ? ? ?// 123
15. to_string()?
返回一個string類型,將別的類型轉換為字符串類型。
int a = 5;
std::string str = std::to_string(a);?
16.substr()字符串分割
只是簡單的字符串分割。如果要指定分割符分割的話,可以使用前面說的strtok()函數
std::string str("Hello World");
std::string ret = str.substr(0,5); ? ?// 從下標0開始分割5個字符
std::cout << ret << std::endl; ? ? ? ?// Hello
std::cout << str << std::endl; ? ? ? ?// Hello World
?
注意:? 關于string的操作其實都是以字節為準的?(可以跳過)
我們在上面的計算字符串長度,從指定位置查找字符或者子串,刪除指定位置的字符,刪除指定個數的字符,以及使用replace()替換指定位置的字符,這寫都是針對于字節來說的。
指不過上面的操作,都是以英文字符的形式去展示的,英文字符一個字符占用一個字節,所以上面我們都使用字符數表示了,其實不是這樣的,實際那些函數操作的是字節。
如果,在例子中使用中文字符(一個字符占兩個字節):?1. 那么計算的長度就是字節數: 字符數*2
2. str1.erase(1);? ? ?// 對于中文字符會出現亂碼,因為此處的1表示,刪除字符串1字節之后的所有字節,中文字符一個占用兩個字節,如果你將1字節后面的字節都刪除了,表示中文字符的二進制就被截斷了,就會出現亂碼
str.erase(2);? ? // 這樣表示刪除字符串2字節之后的所有字符,對于中文字符,這樣才是真正的保留下第一個字符。
3. 對于迭代器也是同理,begin()和end()都是表示一個字節。
4. str1.replace(0, 2, str2, 0, 3);? ? //? 這個取代語句,是將str1第一個位置開始兩個字節的元素替換成str2第一個位置開始三個字節的元素。
如果我們操作的是中文,那么替換的字節數必須是2的倍數,否則就會出現亂碼。
等等,相關的操作其實都是對于字節數的操作。其實這些你不知道也無所謂,只是如果你去嘗試操作放有中文的或者其它多字節字符的字符串的時候,可能會感到蒙。