string相关接口
头文件#include <string>
一、关于编码集
void test_string1()
{
char str1[] = "吃饭";
cout << str1 << endl;
char str2[5];
str2[0] = -77;
str2[1] = -43;
str2[2] = -73;
str2[3] = -70;
str2[4] = '\0';
cout << str2 << endl;
}
str2输出为“痴泛”,说明底层的汉字字符都是通过编码集控制的。
二、关于构造函数
构造函数有很多,我们经常用的就是直接用字符串构造,拷贝构造,赋值操作符。还有一些构造函数可以确定从某个位置开始进行拷贝构造,具体看下方代码中的相关接口。
void test_string2()
{
string s1("hello");//构造
string s2(s1);//拷贝构造
string s3("world");
s1 = s3;//赋值
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
string s4(s3, 3);//从对象的第三位开始取,直到\0
cout << s4 << endl;
string s5("world", 3);//取该字符串的前三位
cout << s5 << endl;
string s6(s3, 1, 3);//从对象的第一位开始取,取三位
cout << s6 << endl;
string s7("world", 1, 3);//从该字符串的第一位开始取,取三位
cout << s7 << endl;
}
三、size/capacity
size: 返回的是当前string对象的大小(length和size的作用相同,但是我们经常用size)
capacity: 返回的是容量的大小
push_back:尾插一个字符
void test_string3()
{
string s1("hello");
s1.push_back(' ');
s1.push_back('w');
s1.push_back('o');
s1.push_back('r');
s1.push_back('l');
s1.push_back('d');
cout << s1.size() << endl;
cout << s1.length() << endl;
cout << s1.capacity() << endl;//最开始capacity容量为15
//持续插入就会增容
s1.push_back('d');
s1.push_back('d');
s1.push_back('d');
s1.push_back('d');
s1.push_back('d');
cout << s1.size() << endl;
cout << s1.length() << endl;
cout << s1.capacity() << endl;//空间不够,增容
}
如果我们想观测到增容是怎么增的,我们可以写这么一段代码测验一下。
容量最开始一般是15,第一次增容是增2倍,之后增容基本是增1.5倍
void test_string4()
{
//string的增容过程:第一次2倍,之后是1.5倍增容
string s;
size_t sz = 0;
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';
}
}
}
运行结果:
四、clear/empty
clear: 清空整个字符串
empty: 判断该字符串是否为空
void test_string5()
{
//clear:清空整个字符串
s1.clear();
s1.push_back('d'); //清空之后重新插入数据
s1.push_back('d');
s1.push_back('d');
s1.push_back('d');
s1.push_back('d');
for (auto e : s1)//打印
{
cout << e << " ";
}
cout << endl;
//empty
s1.clear();
if (s1.empty())
{
s1.push_back('a');
s1.push_back('b');
s1.push_back('c');
}
for (auto e : s1)//打印
{
cout << e << " ";
}
cout << endl;
}
五、reserve和resize
reserve: 扩容
resize: 分三种情况
- 当传的值小于size小于capacity,直接缩小size到传递的值
- 当传的值大于size小于capacity,后面多出来size部分如果没有传值的话就初始化为0,如果传字符的话就初始化为传的字符,size变为传过来的大小,capacity暂时不变(因为不超过容量)
- 当传的值大于size大于capacity,后面多出来的部分如果没有传值的话就初始化为0,如果传字符的话就初始化为传的字符,size变为传递过来的值,capacity会增容(增容原理就是第一次二倍,以后基本是1.5倍)
void test_string6()
{
//reserve和resize
string s1("hello world");
s1.reserve(100);
cout << s1.size() << endl;//11
cout << s1.capacity() << endl;//111
string s2("hello world");
s2.resize(5);//小于size小于capacity,直接缩小size
cout << s2.size() << endl;//5
cout << s2.capacity() << endl;//15(容量最开始就是15)
cout << s2 << endl;
string s3("hello world");
s3.resize(14, 'a');//大于size小于capacity,后面的初始化为a,size变为14,capacity是15不变
cout << s3.size() << endl;//14
cout << s3.capacity() << endl;//15
cout << s3 << endl;
string s4("hello world");
s4.resize(20, 'b');//大于size大于capacity,后面的初始化为x,size变为20,capacity会增容
cout << s4.size() << endl;//20
cout << s4.capacity() << endl;//31
cout << s4 << endl;
}
运行结果:
六、查找某个字符对它进行更改
使用string的三种遍历方式都可以对某个字符进行更改,具体代码如下。
可以戳博主这篇文章:
C++:string的三种遍历方式(operator[ ],迭代器,新式for循环)
void test_string7()
{
string s("hello,world");
for (size_t i = 0; i < s.size(); i++)
{
if (s[i] == ',')
{
s[i] = '%';
}
}
cout << s << endl;
for (auto& e : s)
{
if (e == '%')
{
e = ',';
}
}
cout << s << endl;
string::iterator sit = s.begin();
while (sit != s.end())
{
if (*sit == ',')
{
*sit = '%';
}
sit++;
}
cout << s << endl;
size_t pos = s.find('%');
s[pos] = ',';
cout << s << endl;
}
运行结果:
七、关于字符串的追加
append: 这是一个函数。给某个对象追加某个对象,完成两个字符串的拼接
+=: 这是一个运算符的重载。同样可以实现一个字符串与一个字符串的拼接,这样的写法更加直观一些。
+: 这是一个运算符的重载。也可以实现一个字符串加一个字符串,但是这个运算符效率太低,不建议使用。
void test_string8()
{
string s1("hello ");
string s2("world");
s1.append(s2);
cout << s1 << endl;
string s3("hello ");
string s4("world");
s3 += s4;
s3 += "!!!";
cout << s3 << endl;
string s5("hello ");
string s6("world");
string s7 = s5 + s6;//不建议用+
cout << s7 << endl;
}
运行结果:
八、insert/erase
insert: 在某个位置插入一个字符、一个字符串或一个对象
erase: 删除某位置的字符
void test_string9()
{
string s1("ello");
string s2(" world");
s1.insert(0, 1, 'h');//在第0个位置的前面插入一个字符h
cout << s1 << endl;
s1.insert(s1.end(), 'w');//在end()位置,插入一个字符w
cout << s1 << endl;
s1.erase(5, 1);//从第五个位置开始删除,删除1个字符
cout << s1 << endl;
s1.insert(5, " world");//从第五个位置开始插入,插入一个字符串
cout << s1 << endl;
s1.insert(5, s2);//从第五个位置开始插入,插入一个对象s2
cout << s1 << endl;
}
运行结果:
九、swap(两个字符串的交换)
string里实现了一个swap函数,这个swap函数是完成两个字符串的交换,底层实现的原理是交换指向的对象即可,消耗比较小,建议使用。
但是我们也知道C++库里也有一个swap函数,这个swap函数实现的原理是创建临时变量,不断交换赋值,这样对于string类就会消耗特别大,所以不建议使用C++库里的swap函数。
void test_string10()
{
string s1("hello");
string s2("world");
s1.swap(s2);//string类里实现的swap,建议使用
cout << s1 << "--" << s2 << endl;
swap(s1, s2);//C++库里的swap,不建议使用,效率低下
cout << s1 << "--" << s2 << endl;
}
运行结果:
十、replace和c_str
replace: 完成字符的替换
c_str: 以C语言的方式输出一个字符串(遇到‘\0’就会停止)
void test_string11()
{
string s("http://www.cplusplus.com/");
s.replace(0, 4, "https");//从第一位开始的四个字符替换为‘https’
cout << s << endl;
s[5] = '\0';
cout << s << endl;//遇到\0不会结束,直到size才结束
cout << s.c_str() << endl;//以C形式输出,遇到\0就会停止
}
运行结果:
十一、find和substr
find: 找某个字符的位置
substr: 获取子串
应用这两个接口实现取一个文件名的后置和取一个网站的域名,代码如下:
string GetSuffix(const string& s)
{
size_t pos = s.find('.');
if (pos == string::npos)
{
return s;
}
else
{
//string suffix(s, pos, s.size() - pos);//构造
string suffix = s.substr(pos);//获取子串:从pos取到最后
return suffix;
}
}
void test_string12()
{
//取后缀
string file("file.cpp");
string suffix(file, 4, 4);
cout << suffix << endl;
size_t pos = file.find('.');//找到了返回第一次出现的位置,没找到返回npos(-1)
if (pos != string::npos)//说明找到了
{
string suffix(file, pos, file.size()-pos);//构造函数:从file的pos(.)位置开始,取总长减pos个字符,拷贝构造给suffix
cout << suffix << endl;
}
cout << GetSuffix("test.c.cpp") << endl;
//取域名
string url("http://www.cplusplus.com/reference/string/string/substr/");
size_t start = url.find("://")+3;//找"://"字符串,然后+3记录最后一个/的位置
size_t finish = url.find('/',start);//从刚刚记录的start位置开始找下一个'/'
//start和finish之间的一段就是域名
string domain=url.substr(start, finish-start);//从start开始输出,输出finish-start个字符(finish-start就是域名的字符个数)
cout << domain << endl;
}
运行结果: