前言:
STL是C++的標(biāo)準(zhǔn)模板庫,里面包含了許多算法和數(shù)據(jù)結(jié)構(gòu),例如我們熟悉的順序表、鏈表、棧和隊列以及一些常見的算法等等,編程者想使用這些就可以直接從庫中調(diào)用,不必再自己造輪子了。
下面為STL內(nèi)容的一張圖:
接下來,我們要學(xué)習(xí)STL中的string。
一、認(rèn)識下string
1.1 什么是string
string 是C++的一個類模板,字符串的類模板。要定義一個對象為字符串,就可以用string類型,說明它是一個字符串,相當(dāng)于字符數(shù)組。
int main()
{
string s1("hello yss");
return 0;
}
1.2 為什么要有string
以前我們用C語言寫代碼的時候,假如字符串如果要計算它的長度,必須要調(diào)用C標(biāo)準(zhǔn)庫中的函數(shù)才行。但是這些函數(shù)與字符串本身是分開的,與C++面向?qū)ο蟮乃枷氩黄鹾?,而且很容易出現(xiàn)越界等錯誤,所以C++提供了string類,類里就有我們想使用的函數(shù)來操作字符串,我們只需要調(diào)用類的接口(函數(shù))即可,更加簡潔。
二、string 類的接口使用
string類有許多接口,這里只介紹一些常見的。
2.1 初始化與析構(gòu)
2.1.1 初始化
1??無參與有參
int main()
{
string s1();//無參 空字符串
string s2("hello yss");//有參
cout << s2 << endl;
return 0;
}
注意:括號里字符串也可以是一個字符,但是必須要 " y " 這樣寫,不能 ’ y ’ 這種寫法,否則會報錯。
2??字符填充
前面的參數(shù)表示字符串的長度,后面的參數(shù)是要填充的字符。
int main()
{
string s1(5, 'y');
cout << s1 << endl;
return 0;
}
3??拷貝構(gòu)造
int main()
{
string s1("hello yss");
string s2(s1);
cout << s2 << endl;
return 0;
}
另一種寫法:
int main()
{
string s1("hello yss");
string s2 = s1;//用已經(jīng)存在的對象去拷貝構(gòu)造剛未存在的對象
cout << s2 << endl;
return 0;
}
4??拷貝子字符串
pos是從這位置開始拷貝,len是拷貝的個數(shù)
int main()
{
string s1("hello yss");
string s2(s1, 3, 5);//如果最后一個參數(shù)的值大于后面的
//長度,那么就直接把從pos位置開始的字符串全部拷貝構(gòu)造
cout << s2 << endl;
return 0;
}
一個空格也算一個字符
我們發(fā)現(xiàn)len=npos,說明這是一個半缺省,也就是說最后的參數(shù)可以不寫。
string s2(s1, 3);
npos表示size_t的最大值,作用是返回一個字符串中不存在的位置,可以判斷某個子字符串是否存在。
2.1.2 析構(gòu)
了解下即可
作用就是清理。
2.2 容量操作
2.2.1 長度大小——size和length
有兩個操作函數(shù),都可以計算字符串的有效長度
int main()
{
string s1("hello yss");
cout << s1.size() << endl;
cout << s1.length() << endl;
return 0;
}
一般計算大小使用size更好,因?yàn)閟ize表示大小即長度,length表示長度,鏈表、順序表有長度的說法,但是后面要學(xué)習(xí)到的樹就沒有所謂的長度了,只有大小,所以,為了方便且統(tǒng)一,計算的大小都使用size更好些。
2.2.2 空間總大小——capacity
給對象預(yù)分配好一定大小的空間。如果字符串的有效長度小于預(yù)分配的空間,空間大小不變;否則再次分配一定大小的空間
int main()
{
string s1("hello yss");
cout << s1.size() << endl;
cout << s1.capacity() << endl;
string s2("hello yssxxxxxxxxxxxxxxxx");
cout << s2.size() << endl;
cout << s2.capacity() << endl;
return 0;
}
2.2.3 判空——empty
int main()
{
string s1("hello yss");
cout << s1.empty() << endl;
string s2;
cout << s2.empty() << endl;
return 0;
}
2.2.4 清空——clear
清空的是有效字符,總空間大小不變
int main()
{
string s1("hello yss");
cout << s1.size() << endl;
cout << s1.capacity() << endl;
s1.clear();
cout << s1.size() << endl;
cout << s1.capacity() << endl;
return 0;
}
2.2.5 預(yù)留空間——reserve
1??參數(shù)是缺省值,所以參數(shù)可寫可不寫,不寫默認(rèn)缺省值為0,小于空間大小(capacity()的預(yù)留空間大?。臻g大小不變,不改變字符串中的內(nèi)容。
int main()
{
string s1("hello yss");
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
s1.reserve();
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
return 0;
}
2??參數(shù)的值大于0,小于有效字符的個數(shù)
int main()
{
string s1("hello yss");
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
s1.reserve(6);
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
return 0;
}
小于空間總大小,所以空間總大小不變,不影響字符串的內(nèi)容。
3??參數(shù)的值大于等于有效字符個數(shù),小于空間總大小
int main()
{
string s1("hello yss");
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
s1.reserve(12);
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
return 0;
}
還是因?yàn)轭A(yù)留的空間小于空間總大小,所以空間總大小不變,且不影響字符串的內(nèi)容。
4??參數(shù)的值大于空間總大小
int main()
{
string s1("hello yss");
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
s1.reserve(20);
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
return 0;
}
當(dāng)預(yù)留空間大于空間總大小時,就會再分配新的空間大小,字符串的內(nèi)容還是不受影響。
5??調(diào)用兩次,第二次參數(shù)比第一次小
int main()
{
string s1("hello yss");
s1.reserve(111);
cout << s1.capacity() << endl;
s1.reserve(6);
cout << s1.capacity() << endl;
return 0;
}
第一次調(diào)用預(yù)留空間的大小擴(kuò)容后,第二次比第一次要小,不會改變,說明reserve沒有縮容的功能。
總結(jié):
1.當(dāng)reserve預(yù)留的空間小于等于空間總大小時,空間總大小不變;
2.reserve預(yù)留空間不影響字符串的內(nèi)容
3.reserve不能縮容。
reserve預(yù)留的空間大于空間總大小就會重新分配新的空間總大小,那么這空間總大小的增長規(guī)律是怎樣的呢,看一下代碼:
int main()
{
string s1("hello yss");
size_t len = s1.capacity();
for (int i = 0; i < 100; i++)
{
s1.push_back('x');
while (len != s1.capacity())
{
len = s1.capacity();
cout << len << endl;
}
}
return 0;
}
大概是以1.5倍的大小增長,不同的編譯器可能不同。
2.2.6 改變有效字符個數(shù)、填充多余空間——resize
可支持兩種寫法,一個參數(shù)的表示有效字符個數(shù),兩個參數(shù)的分別為有效字符個數(shù)和要填充的字符。
1.先來一個參數(shù)的寫法:
1??參數(shù)的值小于有效字符個數(shù)
int main()
{
string s1("hello yss");
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
s1.resize(3);
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
return 0;
}
有效字符個數(shù)改變,字符串的內(nèi)容也改變,空間總大小不變。
2??參數(shù)的值大于有效字符個數(shù)、小于空間總大小
int main()
{
string s1("hello yss");
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
s1.resize(12);
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
return 0;
}
有效字符個數(shù)改變,字符串內(nèi)容不變(沒有填充字符),空間總大小不變。
3??參數(shù)的值大于空間總大小
int main()
{
string s1("hello yss");
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
s1.resize(20);
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
return 0;
}
有效字符個數(shù)改變,字符串內(nèi)容不變(沒有填充字符),空間總大小改變。
2.有第二個參數(shù)(填充字符)的寫法:
1??參數(shù)的值小于有效字符個數(shù)
int main()
{
string s1("hello yss");
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
s1.resize(3, 'a');
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
return 0;
}
有效字符個數(shù)改變,字符串內(nèi)容受影響,空間總大小不變。
2??參數(shù)的值大于有效字符個數(shù)、小于空間總大小
int main()
{
string s1("hello yss");
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
s1.resize(12, 'a');
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
return 0;
}
有效字符個數(shù)改變,字符串增加的空間填充字符,空間總大小不變。
3??參數(shù)的值大于空間總大小
int main()
{
string s1("hello yss");
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
s1.resize(20, 'a');
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
return 0;
}
有效字符個數(shù)改變,字符串增加的空間填充字符,空間總大小增加。
總結(jié):
1.resize影響有效字符個數(shù),小于原來有效字符個數(shù)時,字符串內(nèi)容隨之改變;
2.當(dāng)n的值(第一個參數(shù))大于原來的有效字符個數(shù)會在后面填充字符;
3.n小于空間總大小,空間總大小不變;否則會擴(kuò)容。
2.3 遍歷訪問
2.3.1 下標(biāo)遍歷——operator[]
有兩種寫法,一個是operator關(guān)鍵字+運(yùn)算符,另一個是直接方括號下標(biāo)。
int main()
{
string s1("hello yss");
for (size_t i = 0; i < s1.size(); i++)
{
cout << s1.operator[](i) << " ";
//cout << s1[i] << " ";
}
cout << endl;
return 0;
}
2.3.2 范圍for
int main()
{
string s1("hello yss");
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
return 0;
}
2.3.3 迭代器遍歷——iterator
它是可以檢查容器內(nèi)的元素并遍歷元素的數(shù)據(jù)類型,寫法先看以下代碼:
int main()
{
string s1("hello yss");
string::iterator s = s1.begin();
while (s != s1.end())
{
cout << *s << " ";
s++;
}
cout << endl;
return 0;
}
s相當(dāng)于一個指針,首先得到的是字符串的第一個字符,只要它不等于斜杠0,循環(huán)打印出每個字符,等于斜杠0跳出。
那么begin()和end()又是什么呢?
begin返回容器的首元素,end返回容器的最后一個元素的下一個地址。
還有加const的寫法,用于函數(shù)調(diào)用時為了不改變字符串的內(nèi)容形參加const,對應(yīng)的迭代器也要加const
void Func(const string& ss)
{
string::const_iterator s = ss.begin();
while (s != ss.end())
{
cout << *s << " ";
s++;
}
cout << endl;
}
int main()
{
string s1("hello yss");
Func(s1);
return 0;
}
注意:是const_iterator,中間是下劃線,與const iterator是不同的。const_iterator限制的指針指向的內(nèi)容,所以指針指向的內(nèi)容不能修改;const iterator限制的是指針本身,也就是說地址不能修改。
如果要逆置字符串該怎樣操作呢
1??先用以前的方法:
int main()
{
string s1("hello yss");
string::iterator s = s1.begin();
while (s != s1.end())
{
cout << *s << " ";
s++;
}
cout << endl;
size_t begin = 0;
size_t end = s1.size() - 1;
while (begin < end)
{
char tmp = s1[begin];
s1[begin] = s1[end];
s1[end] = tmp;
begin++;
end--;
}
s = s1.begin();
while (s != s1.end())
{
cout << *s << " ";
s++;
}
return 0;
}
這里交換兩個元素時要自己寫,感覺有點(diǎn)麻煩,可以使用模板
2??交換模板:
template<class T>
void Swap(T& a, T& b)
{
T t = a;
a = b;
b = t;
}
int main()
{
string s1("hello yss");
string::iterator s = s1.begin();
while (s != s1.end())
{
cout << *s << " ";
s++;
}
cout << endl;
size_t begin = 0;
size_t end = s1.size() - 1;
while (begin < end)
{
Swap(s1[begin], s1[end]);
begin++;
end--;
}
s = s1.begin();
while (s != s1.end())
{
cout << *s << " ";
s++;
}
return 0;
}
不看頂上的交換模板函數(shù),循環(huán)里的代碼比前面簡潔了一些,但是還是感覺挺繁瑣的
3??逆置算法函數(shù):
int main()
{
string s1("hello yss");
string::iterator s = s1.begin();
while (s != s1.end())
{
cout << *s << " ";
s++;
}
cout << endl;
reverse(s1.begin(), s1.end());
s = s1.begin();
while (s != s1.end())
{
cout << *s << " ";
s++;
}
return 0;
}
不用逆置算法函數(shù),也可以逆序遍歷
int main()
{
string s1("hello yss");
string::reverse_iterator s = s1.rbegin();
while (s != s1.rend())
{
cout << *s << " ";
s++;
}
cout << endl;
return 0;
}
這兩個接口的也有const,與前面的同理
void Func(const string& ss)
{
string::const_reverse_iterator s = ss.rbegin();///
while (s != ss.rend())
{
cout << *s << " ";
s++;
}
cout << endl;
}
int main()
{
string s1("hello yss");
Func(s1);
return 0;
}
有沒有發(fā)現(xiàn)其中一段代碼太長了,所以這里就就可以用以前學(xué)過的auto來簡化代碼
auto s = ss.rbegin();//自動推導(dǎo)類型
2.4 修改操作
2.4.1 尾插字符——push_back
int main()
{
string s1("hello yss");
s1.push_back('x');
cout << s1 << endl;
return 0;
}
2.4.2 尾插字符串——append
這個接口的可實(shí)現(xiàn)方式比較多,所以這里只介紹常見的
int main()
{
string s1("hello yss");
s1.append("a");
cout << s1 << endl;
s1.append("vvvv");
cout << s1 << endl;
return 0;
}
因?yàn)槭亲址砸p引號,但是雙引號里也可以是一個字符。
2.4.3 字符串追加字符串——operator+=
這個比前面兩個都要實(shí)用得多了,既可以尾插字符串,也可以尾插字符。而且寫起來也方便。
int main()
{
string s1("hello yss");
s1 += "vvvv";
cout << s1 << endl;
string s2("hello yss");
s2 += 'd';
cout << s2 << endl;
string s3("hello yss");
s3 += s1;
cout << s3 << endl;
return 0;
}
2.4.4 賦值——assign
1??用一個字符串賦給另一個字符串
int main()
{
string s1("hello yss");
string s2 = s1.assign(s1);
cout << s2 << endl;
return 0;
}
2??從一個字符串中提取子字符串賦給另一個字符串
int main()
{
string s1("hello yss");
string s2;
s2.assign(s1,2,5);
cout << s2 << endl;
return 0;
}
3??一個字符串賦給另一個字符串
int main()
{
string s2;
s2.assign("yyyyyyyy");
cout << s2 << endl;
return 0;
}
4??一個字符串賦給另一個字符串,限定個數(shù)
int main()
{
string s2;
s2.assign("yyyyyyyy", 3);
cout << s2 << endl;
return 0;
}
5??字符賦給一個字符串,有個數(shù)控制
int main()
{
string s3;
s3.assign(10, 'x');
cout << s3 << endl;
return 0;
}
2.4.5 插入——insert
1??
從pos位置插入一個字符串,和可以限制插入字符串的個數(shù)。
int main()
{
string s1("hello yss");
s1.insert(1, "ccc");
cout << s1 << endl;
string s2("hello yss");
s2.insert(3, "cccccc",3);
cout << s2 << endl;
return 0;
}
2??
從pos位置插入一個字符串。第二個有點(diǎn)長,在第一個的基礎(chǔ)上可以選擇要插入的字符串的起始的位置,并且限制插入的個數(shù)。
int main()
{
string s3("hello yss");
string s4("hello world");
s4.insert(5, s3);
cout << s4 << endl;
string s5("hello yss");
string s6("hello world");
s6.insert(5, s5,2, 3);
cout << s6 << endl;
return 0;
}
3??
插入字符,可以控制個數(shù)
int main()
{
string s7("hello yss");
s7.insert(5, 4, 'x');
cout << s7 << endl;
return 0;
}
2.4.6 刪除——erase
1??從pos位置開始刪除len個字符,這兩個參數(shù)都是缺省值,如果不寫參數(shù),就是一個字符串全部刪除;
2??刪除某個位置的字符;
3??刪除從一個位置到另一個位置的字符串。
int main()
{
string s1("hello yss");
s1.erase();
cout << s1 << endl;
string s2("hello yss");
s2.erase(3,5);
cout << s2 << endl;
string s3("hello yss");
s3.erase(s3.begin());
cout << s3 << endl;
string s4("hello yss");
s4.erase(s4.begin() + 1, s4.end() - 1);
cout << s4 << endl;
return 0;
}
2.4.7 查找——find
查找有一個字符串或者字符,后面沒有參數(shù)就默認(rèn)從字符串的起始位置開始找,有寫參數(shù)從pos位置開始找
int main()
{
string s1("hello yss");
size_t pos = s1.find(" ");
cout << pos << endl;
return 0;
}
2.4.8 替換——replace
用len個90#替換pos位置的字符
int main()
{
string s1("hello yss");
s1.replace(5, 1, "90#");
cout << s1 << endl;
return 0;
}
小練習(xí):把一個字符串內(nèi)的所有空格替換成20%
1??做法1:
int main()
{
string s1("hello yss hello world");
size_t pos = s1.find(" ");
while (pos != string::npos)
{
s1.replace(pos, 1, "20%");
pos = s1.find(" ", pos + 3);
}
cout << s1 << endl;
return 0;
}
假如一個字符串內(nèi)有多個空格,先用pos記錄第一個空格的位置,當(dāng)pos不等于npos成立,說明在字符串中找到了這個pos位置,然后在這個位置替換成20%。因?yàn)榍懊娴膒os已經(jīng)用過一次了,所以這里要重新定義新的pos位置找后面的空格。參數(shù)為pos+3是因?yàn)?0%有3個字符,所以要跳過這3個字符開始查找。直到后面沒有空格,找不到,返回整型的最大值,為-1,然后跳出循環(huán)。
find()接口的一個說明:找不到返回npos
這里再介紹下npos:
它是靜態(tài)成員常量,值為-1,是無符號類型的,所以也是整型的最大值。
2??做法2:
int main()
{
string s1("hello yss hello world");
string s2;
for (auto e : s1)
{
if (e == ' ')
{
s2 += "20%";
}
else
{
s2 += e;
}
}
s1.swap(s2);//交換
cout << s1 << endl;
return 0;
}
創(chuàng)建一個臨時字符串,遍歷原來的字符串,如果是空格,在臨時的字符串+=空格,否則+=原來字符串中的字符。如果不想用臨時字符串打印出來,可以交換。
2.4.9 返回C格式字符串——c_str
可以將 const string* 類型 轉(zhuǎn)化為 const char* 類型,因?yàn)樵赾語言中沒有string,所以要把它轉(zhuǎn)變成C語言中字符串的形式。
在文件操作中C語言和C++混著用:
int main()
{
string filename("test.cpp");
FILE* pf = fopen(filename.c_str(), "r");
char ch = fgetc(pf);
while (ch != EOF)
{
cout << ch;
ch = fgetc(pf);
}
return 0;
}
2.4.10 截取字符串——substr
因?yàn)閮蓚€參數(shù)都有缺省值,兩個都不寫,就相當(dāng)于直接取整個字符串了;后面的參數(shù)不寫,從pos位置開始截取剩下的全部字符串。
1??截取 . 和它后面的字符串
int main()
{
string s1("test.cpp");
size_t pos = s1.find('.');
if (pos != string::npos)
{
string s2 = s1.substr(pos);
cout << s2 << endl;
}
return 0;
}
2??如果一個字符串內(nèi)有多個點(diǎn),要截取最后一個點(diǎn)以及后面的字符串
int main()
{
string s1("test.cpp.xxx");
size_t pos = s1.rfind('.');
if (pos != string::npos)
{
string s2 = s1.substr(pos);
cout << s2 << endl;
}
return 0;
}
用原來的find()要從頭開始找,比較麻煩。所以string還提供了另一個查找的函數(shù)——rfind(),可以逆向查找,即從后向前。
3??分割網(wǎng)址
int main()
{
string s("https://cplusplus.com/reference/string/string/substr/");
string s1, s2, s3;
size_t pos1 = s.find(':');
if (pos1 != string::npos)
{
s1 = s.substr(0, pos1);
cout << s1 << endl;
}
size_t pos2 = s.find('/', pos1 + 3);
if (pos2 != string::npos)
{
s2 = s.substr(pos1 + 3, pos2 - pos1 - 3);
cout << s2 << endl;
}
s3 = s.substr(pos2 + 1);
cout << s3 << endl;
return 0;
}
網(wǎng)址的格式:
<協(xié)議>://<服務(wù)器名稱>.<域名>/<目錄>/<文件名>
我們是想分割成3個部分,所以定義3個string 對象,一個一個分割。首先分割出協(xié)議,先要找到冒號的位置,冒號的位置的pos1的值即為前面字符串的有效個數(shù),所以截取字符串從起始位置開始截取pos1個。第二個分割出服務(wù)器名稱和域名,要找的截至位置是 / ,但是要注意從pos1+3的位置開始找,找到后也是從pos1+3的位置開始截取,截取pos2 - pos1 - 3個。最后一個不必多說。
2.5 輸入操作
2.5.1 獲取一行字符串——getline
istream& is表示一個輸入流,例如cin;char delim表示終止符,例如回車,或者別的符號。
1??自己定義終止符
int main()
{
string s1;
getline(cin, s1,'#');
cout << s1 << endl;
return 0;
}
2??自己沒有定義終止符
int main()
{
string s1;
getline(cin, s1);
cout << s1 << endl;
return 0;
}
自己沒有寫終止符,默認(rèn)的終止符為回車
那么這個有什么用呢?在輸入一行字符串的時候,如果我們使用的是cin,它的默認(rèn)終止符為空格或者換行,所以如果我們輸入的字符串中有空格,打印出來的結(jié)果就不會是一行。
看以下代碼:
int main()
{
string s1;
cin >> s1;
cout << s1 << endl;
return 0;
}
所以如果在做題中還是在其他寫代碼的場景要求輸出一整行字符串,getline就是一個好的選擇。文章來源:http://www.zghlxwxcb.cn/news/detail-759911.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-759911.html
到了這里,關(guān)于【C++初階】STL之學(xué)習(xí)string的用法的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!