1.初識string類
1)爲什麼c++不使用c語言中的字符串?
C語言中字符串是以’\0’結尾的一些字符的集合,對這些字符串的處理C語言使用strcpy等函數,但是這些函數與字符串是分離開的,不太符合面向對象的思想,而且底層空間需要用戶自己管理,也可能用戶使用時造成越界訪問。
2)string類基本瞭解
- string類是一個字符串的類。
- string類的接口與其他容器的接口基本相同,並添加了一些專門用來操作string類的接口。
- string在底層實際是:
basic_string
模板類的別名,typedef basic_string<char, char_traits, allocator> string;
- 不能操作多字節或者變長字符的序列
- 使用時必須包含
#include<string>
與using namecpace std;
2.string類的常用接口
1)string類對象的常見構造
a.常用構造瞭解
函數名稱 |
功能 |
string() |
構造空的string類對象,即空字符串 |
string(const char* s) |
用C-string來構造string類對象 |
string(size_t n,char c) |
string類對象中包含n個字符c |
string(const string&s) |
拷貝構造函數 |
string(const string&s,size_t n) |
用s中的前n個字符構造新的string類對象 |
b.常用構造測試
void test1()
{
string s1;//構造空的string類對象
string s2("hello");//用C-string來構造string類對象
string s3 = "world";//explicit 隱式轉換
string s4(s2);//拷貝構造
string s5(10, 'a');// 用10個字符'a'構造string類對象s35
string s6(s5, 5);//用s5的前5個字符構成string對象s6
cout << s1 << endl;//輸出空格
cout << s2 << endl;//hello
cout << s3 << endl;//world
cout << s4 << endl;//hello
cout << s5 << endl;//aaaaaaaaaa
cout << s6 << endl;//aaaaa
}
2)string類對象的容量操作
a.容量操作了解
函數名稱 |
功能 |
size_t size() const |
返回字符串有效字符長度 |
bool empty ( ) const |
檢測字符串釋放爲空串,是返回true,否則返回false |
size_t length() const |
返回字符串有效字符長度 |
void clear() |
清空有效字符 |
size_t capacity ( ) const |
返回空間總大小 |
void resize ( size_t n, char c ) |
將有效字符的個數該成n個,多出的空間用字符c填充 |
|
|
void resize ( size_t n ) |
將有效字符的個數改成n個,多出的空間用0填充 |
|
|
void reserve ( size_t res_arg=0 ) |
爲字符串預留空間 |
|
|
b.容量操作測試
void resize()
{
// 注意:string類對象支持直接用cin和cout進行輸入和輸出
string s("hello");
cout << s << endl;//hello
cout << s.length() << endl;//5
cout << s.size() << endl;//5 不算“\0”
cout << s.capacity() << endl << endl;//15 實質上是16個char的空間因爲'\0'的存在
s.clear();//不改變容量的大小
cout << s.size() << endl;//0
cout << s.capacity() << endl << endl;//15
s.resize(12,'b');
cout << s << endl;//bbbbbbbbbbbb
cout << s.size() << endl;//12
cout << s.capacity() << endl<<endl;//15
s.resize(18);
cout << s.size() << endl;//18
cout << s.capacity() << endl << endl;//31
s.resize(10);
cout << s << endl;//bbbbbbbbbb
cout << s.size() << endl;//10
cout << s.capacity() << endl << endl;//31
}
void reserve()
{
string s("hello");
cout << s << endl;//hello
cout << s.size() << endl;//5 不算“\0”
cout << s.capacity() << endl << endl;//15
// 測試reserve是否會改變string中有效元素個數
s.reserve(200);
cout << s.size() << endl;//5 不算“\0” 不會改變
cout << s.capacity() << endl << endl;//207
// 測試reserve參數小於string的底層空間大小時,是否會將空間縮小
s.reserve(100);
cout << s.size() << endl;//5 不算“\0”
cout << s.capacity() << endl << endl;//207 不會縮小
s.reserve(15);
cout << s.size() << endl;//5 不算“\0”
cout << s.capacity() << endl << endl;//15 因爲原始空間爲15
}
c.注意事項
1.size()與length()的方法實現基本一樣
,引入size()的原因爲了與其他容器的接口保持一致,一般情況下基本都是用size()。
2.clear()只是將string的有效字符清空,不改變底層空間的大小。
3.resize(size_t n) 與 resize(size_t n, char c)都是將字符串中有效字符個數改變到n個,不同的是當字 符個數增多時:resize(n)用0來填充多出的元素空間,resize(size_t n, char c)用字符c來填充多出的 元素空間。resize在改變元素個數時,如果是將元素個數增多,可能會改變底層容量的大 小,如果是將元素個數減少,底層空間總大小不變。
4. reserve(size_t res_arg=0):爲string預留空間,不改變有效元素個數,當reserve的參數小於 string的底層空間總大小時,reserver不會改變容量大小。
d.可以使用reserve提高插入數據時的效率,避免增容的開銷。
void test1()
{
//利用reserve提高插入數據的效率,避免增容帶來的開銷
//事先聲明好reserve
string s;
s.reserve(1);
size_t sz = s.capacity();
cout << "making s grow:\n";
for (int i = 0; i < 100; ++i)
{
s += 'c';
if (sz != s.capacity())
{
sz = s.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
cout << s << endl;
}
運行結果如下,上述代碼每次都需要增容。
可修改爲如下:
void test2()
{
//利用reserve提高插入數據的效率,避免增容帶來的開銷
//事先聲明好reserve
string s;
s.reserve(100);
size_t sz = s.capacity();
cout << "making s grow:\n";
for (int i = 0; i < 100; ++i)
{
s += 'c';
if (sz != s.capacity())
{
sz = s.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
cout << s << endl;
}
3)string類對象的訪問操作
a.訪問接口瞭解
函數名稱 |
功能 |
char& operator[](size_t pos) |
返回pos位置的字符,const string類對象調用 |
const char& operator[](size_t pos)const |
返回pos位置的字符,非const string類對象調用 |
b.測試
void test6()
{
string s1("hello bit");
cout << s1 << endl;//hello bit
s1[0] = 'H';
s1[6] = 'B';
cout << s1 << endl;//Hello Bit
const string s2("Hello Bit");
// s2[0] = 'h'; // 代碼編譯失敗,因爲const類型對象不能修改
}
4)string類對象的修改操作
a.接口瞭解
函數名 |
功能 |
void push_back(char c) |
尾插字符c |
|
|
string& append (const char* s); |
在字符串後追加一個字符串 |
|
|
string& operator+=(const string& str) |
在字符串後追加字符串str |
|
|
string& operator+=(const char* s) |
在字符串後追加C個數字符串 |
|
|
string& operator+=(char c) |
在字符串後追加字符c |
|
|
const char* c_str( )const |
返回C格式字符串 |
|
|
size_t find (char c, size_t pos = 0)const |
從pos往後找c,返回c的位置 |
size_t rfind(char c, size_t pos = npos) |
從pos往前找c,返回c的位置 |
string substr(size_t pos = 0, size_t n = npos)const |
從pos位置截取n個位置子串並返回 |
b.接口測試
void test1()
{
//1
string s1("HELLO");
s1.push_back(' ');
s1.append( "world");
cout << s1 << endl;//HELLO world
//2
string s2("HELLO");
s2 += ' ';
s2 += "world";
string s3("!!!");
s2 += s3;
cout << s2 << endl;//HElLO world!!!
cout << s2.c_str() << endl;//HElLO world!!! 作用:當需要用c的接口實現string時
//3
string num("111");
int valuse = atoi(num.c_str());
cout << valuse << endl;//111
//4
string s2("HELLO");
s2 += ' ';
s2 += "world";
string s3("!!!");
s2 += s3;
s2.insert(s2.begin(), 'x');
s2.insert(0, "bit");
cout << s2 << endl;//bitxHELLO world!!!
}
void test2()
{
//獲取後綴
string file("test.cpp");
size_t pos = file.rfind(".");
// npos是string裏面的一個靜態成員變量
// static const size_t npos = -1;
if (pos == string::npos)
{
cout << "文件沒有後綴" << endl;
}
else
{
cout << file.substr(pos, file.size() - pos) << endl;//.cpp
}
//取出url的域名
string url("https://hao.360.cn/");
size_t start = url.find("://");
if (start == string::npos)
{
cout << "url invalid" << endl;
return;
}
start += 3;
size_t finish = url.find('/', start);//size_t 因爲值可能大,find找不到就返回-1
cout << url.substr(start, finish - start) << endl;//hao.360.cn
//刪除url的前綴
pos = url.find("://");
url.erase(0, pos + 3);
cout << url << endl;//hao.360.cn/
}
c.注意事項
- 在string尾部追加字符時,s.push_back(c)/ s.append(1, c) / s += 'c’三種的實現方式差不多,一般 情況下string類的+=操作用的比較多,+=操作不僅可以連接單個字符,還可以連接字符串。
- 對string操作時,如果能夠大概預估到放多少字符,可以先通過reserve把空間預留好。
5)string類的一些非成員函數
函數 |
功能 |
operator+ |
一個字符串加一個字符串需要另一個字符串接受並返回(不建議使用,因爲效率低) |
operator>> |
輸入運算符重載 |
|
|
operator<< |
輸出運算符重載 |
getline |
獲取一行字符串 |
relational operators |
大小比較 |
6)string類對象的三種訪問方式
a.迭代器的方式
//迭代器有普通版本 反向版本 const版本
//迭代器給出了統一的方式去訪問容器,屏蔽了底層複雜的細節。
//迭代器底層類似於指針,是string的內部類型。
//string::iterator是類型 it是對象。
//begin是指向第一個數據位置的迭代器 "1"。
//end是指向最後一個數據的下一個位置的迭代器 “\0"。
//左閉右開
//普通版本
void test2()
{
string num("1234");
string::iterator it = num.begin();
int value = 0;
while (it != num.end())
{
value *= 10;
value += *it - '0';
++it;
}
cout << value << endl;
}
// const迭代器
int StrToNum1(const string& str)
{
int value = 0;
string::const_iterator it = str.begin();
while (it != str.end())
{
//讀數據
//cout << *it << " ";
//++it;
value *= 10;
value += *it - '0';
++it;
}
return value;
}
//反向迭代器
void test3()
{
string num = "1234";
string::reverse_iterator it = num.rbegin();
while (it != num.rend())
{
//讀數據
cout << *it << " ";
++it;
//修改數據
//(*it) += 1;
//++it;
}
cout << endl;
cout << StrToNum3(num) << endl;
}
b.for+下標
void test()
{
string s("hello");
for (size_t i = 0; i < str.size(); i++)
{
cout<<s1[i]<" ";
}
cout<<endl;
}
c.auto形式
void test()
{
string s("hello");
int value = 0;
for (auto e : s)
{
value *= 10;
value += (e - '0');
}
cout << value<<endl;
}