1. 简介
①什么是string类
源文档
译:
1. 字符串是表示字符序列的类2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。3. string 类是使用 char( 即作为它的字符类型,使用它的默认 char_traits 和分配器类型 ( 关于模板的更多信息,请参阅basic_string) 。4. string 类是 basic_string 模板类的一个实例,它使用 char 来实例化 basic_string 模板类,并用 char_traits和allocator 作为 basic_string 的默认参数 ( 根于更多的模板信息请参考 basic_string) 。5. 注意,这个类独立于所使用的编码来处理字节 : 如果用来处理多字节或变长字符 ( 如 UTF-8) 的序列,这个类的所有成员( 如长度或大小 ) 以及它的迭代器,将仍然按照字节 ( 而不是实际编码的字符 ) 来操作。
②string类的作用
string类是一个用于处理字符串的类,它可以存储任意数目的字符。它可以用来表示文本或其他字符串数据,例如:文本文件内容、网络传输的数据、数据库中的文本列、用户输入或输出等等。
string类的作用有以下几个方面:
存储和处理文本数据:string类可以存储和操作任意数目的字符,可以处理文本数据,如搜索、替换、截取子串等操作。
方便字符串操作:使用string类可以方便地进行字符串操作,如连接(concatenate)两个字符串、删除(erase)字符串中的一些字符、复制(copy)字符串等。
兼容性:string类是C++标准库的一部分,可用于在不同的计算机和操作系统之间进行可靠的代码交换。
2. string类的常见接口
①string类对象的常见构造
源文档
在这里我们只对部分作讲解 ,以如下代码为例
int main()
{
string s1;// 创建一个没有初始值的字符串s1
string s2("张三");// 创建一个以"张三"为初始值的字符串s2
string s3("hello world");// 同上
string s4(10, '*');// 创建一个s4字符串并以10个'*'初始化它
string s5(s2);// 拷贝构造一个s5字符串
string s6(s3, 6, 5);// 将s3中第6个位置的及之后的5个字符串拷贝构造到s6中
string s7(s3, 6);// 将s3中第6个位置及之后的所有字符串(这里的nops为一个极大的无符号整形数,其为size_t nops = -1)拷贝构造到s7中
string s8(s3, 6, 100);// 类似于s6,是将s3中第6个位置的及之后的100个字符串拷贝构造到s8中
return 0;
}
分别打印它们有
②string类对象的容量操作
源文档
同样的这里只对部分函数作讲解
⑴size、length、 capacity
源文档如下
测试代码如下
int main()
{
string s("hello, world");
cout << s.size() << endl;// 显示字符串的有效长度
cout << s.length() << endl;// 同上
cout << s.capacity() << endl;// 显示字符串的容量
cout << s << endl;
}
运行如下
⑵reserve、resize
原文档如下
测试代码如下
int main()
{
string str = "hello world";
cout << "Str: " << str << endl;
cout << "Size: " << str.size() << endl;
cout << "Capacity: " << str.capacity() << endl;
cout << endl;
str.resize(5);// 将str的长度改为5,超过5的部分全部被截断
cout << "Resized: " << str << endl;
cout << "Size: " << str.size() << endl;
cout << "Capacity: " << str.capacity() << endl;
cout << endl;
str.resize(15,'*');// 将str的长度改为15,没有字符的部分全部用'*'填充
cout << "Resized: " << str << endl;
cout << "Size: " << str.size() << endl;
cout << "Capacity: " << str.capacity() << endl;
cout << endl;
str.reserve(20);// 将capacity预置为20,不对size更改
cout << "Reserved: " << str << endl;
cout << "Size: " << str.size() << endl;
cout << "Capacity: " << str.capacity() << endl;
cout << endl;
str.reserve(10);// 将capacity预置为小于size时,编译器可能不会对其作出更改
cout << "Reserved: " << str << endl;
cout << "Size: " << str.size() << endl;
cout << "Capacity: " << str.capacity() << endl;
cout << endl;
return 0;
}
运行有
⑶clear、shrink_to_fit
测试代码
int main()
{
string str = "hello world";
cout << "Str: " << str << endl;
cout << "Size: " << str.size() << endl;
cout << "Capacity: " << str.capacity() << endl;
cout << endl;
// 清空字符串(将size置为0),但不改变容量大小
str.clear();
cout << "Cleared: " << str << endl;
cout << "Size: " << str.size() << endl;
cout << "Capacity: " << str.capacity() << endl;
cout << endl;
// 提出将capacity缩小到适应大小的请求,最后是否缩小由编译器确定(与resever类似)
str.shrink_to_fit();
cout << "Capacity after shrink: " << str.capacity() << endl;
return 0;
}
运行有
③string类对象的访问及遍历操作
⑴使用[]
源文档
我们使用以下代码进行举例
int main()
{
string s1("hello world");
const string s2("Hello World");
cout << s1 << " " << s2 << endl;
cout << s1[0] << " " << s2[0] << endl;
s1[0] = 'H';
cout << s1 << endl;
// s2[0] = 'h'; 代码编译失败,因为const类型对象不能修改
//使用时,可以同时改变字符串的内容
for (size_t i = 0; i < s1.size(); ++i)
{
cout << s1[i] << endl;
}
return 0;
}
运行有
⑵使用迭代器iterator
迭代器是什么?
C++迭代器(Iterator)是一种对象,它允许遍历容器中的元素。迭代器提供了对容器元素的访问和操作,使程序员能够对容器进行遍历、查找、修改等操作。
而迭代器本质上是一个泛型指针,它实现了对指向容器元素的指针的抽象,使得我们可以在不了解容器内部细节的情况下对其进行访问和操作。当我们需要遍历、查找或修改容器中的元素时,迭代器为我们提供了一种统一的接口,使得我们可以将不同类型的容器当作同一类型来处理。
迭代器源文档
我们以如下代码举例
int main()
{
string s("hello world");
string::iterator it = s.begin();
while (it != s.end())
{
//也可以通过解引用来改变字符串内容
(*it)++;
cout << *it << " ";
++it;
}
cout << endl;
// 倒着遍历字符串
string::reverse_iterator rit = s.rbegin();
// C++11之后,直接使用auto定义迭代器,让编译器推到迭代器的类型
// 即可以使用auto rit = s.rbegin();
while (rit != s.rend())
{
(*rit)--;
cout << *rit << " ";
}
return 0;
}
运行有
对于一些只读的字符串,需要使用const修饰,对应的begin()等也要对于更换为const形式,部分源文档如下
void func(const string& s)
{
// 对于const常量字符串
// 只能使用const修饰的迭代器,以防造成权限的放大
string::const_iterator it = s.begin();
while (it != s.end())
{
// it += 2;
cout <<*it <<" ";
++it;
}
cout <<endl;
// string::const_reverse_iterator rit = s.rbegin( );
// 可以使用auto来自动识别
auto rit = s.rbegin();
while (rit != s.rend( ))
{
// (*rit) += 3;
cout <<*rit <<" ";
++rit;
}
}
⑶使用范围for
以如下代码为例
int main()
{
string s = "hello world";
for (auto& ch : s)
{
ch += 1;
cout << ch << " ";
}
return 0;
}
运行有
④string类对象的修改操作
源文档
⑴+=、append、push_back与insert
测试代码
int main()
{
string str = "hello ";
cout << "str:" << str << endl;
cout << "strSize: " << str.size() << endl;
cout << "strCapacity: " << str.capacity() << endl;
str += "world";// 向str追加"world"
str += '!';
cout << "str:" << str << endl;
cout << "strSize: " << str.size() << endl;
cout << "strCapacity: " << str.capacity() << endl;
cout << endl;
string str1 = "hello ";
cout << "str1:" << str1 << endl;
cout << "str1Size: " << str1.size() << endl;
cout << "str1Capacity: " << str1.capacity() << endl;
str1.append("world", 3);// 向str1后追加"world"的前3个字符
cout << "str1:" << str1 << endl;
str1.append(2, '!');// 向str1后追加两个'!'
cout << "str1:" << str1 << endl;
cout << "str1Size: " << str1.size() << endl;
cout << "str1Capacity: " << str1.capacity() << endl;
cout << endl;
string str2 = "hello ";
cout << "str2:" << str2 << endl;
cout << "str2Size: " << str2.size() << endl;
cout << "str2Capacity: " << str2.capacity() << endl;
// 遍历字符串"world",并将每个字符尾插到str2中
for (char c : "world")
{
str2.push_back(c);
}
cout << "str2:" << str2 << endl;
cout << "str2Size: " << str2.size() << endl;
cout << "str2Capacity: " << str2.capacity() << endl;
cout << endl;
string str3 = "hello ";
cout << "str3:" << str3 << endl;
cout << "str3Size: " << str3.size() << endl;
cout << "str3Capacity: " << str3.capacity() << endl;
// 在str3的第6个位置插入字符串"world"
str3.insert(6, "world");
cout << "str3:" << str3 << endl;
// 在str3的最后的位置插入"!"
str3.insert(str3.size(), "!");
cout << "str3:" << str3 << endl;
cout << "str3Size: " << str3.size() << endl;
cout << "str3Capacity: " << str3.capacity() << endl;
return 0;
}
运行有
在插入数据时,会设计到扩容的问题, 实际上在不同的环境下扩容的方式各不相同
int main()
{
string s1("hello world");
cout << s1.size() << endl;
cout << "初始容量:" << s1.capacity() << endl;
size_t old = s1.capacity();
for (size_t i = 0; i < 100; i++)
{
s1 += 'x';
if (old != s1.capacity())
{
cout << "扩容:" << s1.capacity() << endl; old = s1.capacity();
}
}
return 0;
}
对于这样一段代码,在g++和vs两种环境下有着不同的结果,即
g++:
vs:
通过观察我们发现在g++中string使用的是2倍扩容,在vs中使用的是1.5倍扩容 。
⑵assign、erase、replace与pop_back
源文档
测试代码
int main()
{
string str = "hello world";
string str1, str2, str3, str4;
// 将str对象的从下标为0到下标为5的字符拷贝构造到str1上
str1.assign(str, 0, 5);
cout << "str1:" << str1 << endl;
str2 = str;
// 将str2对象的从下标为6的位置往后的5个字符删除
str2.erase(6, 5);
cout << "str2:" << str2 << endl;
str3 = str;
// 将str3对象的从下标为2的位置往后的5个字符替换为"*****"
str3.replace(2, 5, "*****");
cout << "str3:" << str3 << endl;
str4 = str;
// 尾删str4
str4.pop_back();
cout << "str4:" << str4 << endl;
return 0;
}
运行有
⑶c_str、substr
源文档
测试代码
int main()
{
string str = "hello world";
// 将string类对象转化成const char* 的C语言字符类型
const char* cstr = str.c_str();
cout << "cstr:" << cstr << endl;
// 将str从下标为6向后5个字符拷贝构造到sub中
string sub = str.substr(6, 5);
cout << "sub:" << sub << endl;
return 0;
}
运行有
⑷find、rfind、find_first_of、find_last_of、find_first_not_of、find_last_not_of
源文档
测试代码
int main()
{
string str = "hello world";
// 从头开始查找字符 l
size_t pos1 = str.find('l');
cout << "pos1:" << pos1 << endl;
// 从尾开始查找字符 l
size_t pos2 = str.rfind('l');
cout << "pos2:" << pos2 << endl;
// 从前往后查找"world"中任意一个字符(即'w'、'o'、'r'、'l'、'd')第一次出现的位置
size_t pos3 = str.find_first_of("world");
cout << "pos3:" << pos3 << endl;
// 从后往前查找"world"中任意一个字符(即'w'、'o'、'r'、'l'、'd')第一次出现的位置
size_t pos4 = str.find_last_of("world");
cout << "pos4:" << pos4 << endl;
// 从前往后查找不在"helord"中任意一个字符(即'h'、'e'、'l'、'o'、'r'、'd')第一次出现的位置
size_t pos5 = str.find_first_not_of("helord");
cout << "pos5:" << pos5 << endl;
// 从后往前查找不在"helord"中任意一个字符(即'h'、'e'、'l'、'o'、'r'、'd')第一次出现的位置
size_t pos6 = str.find_last_not_of("helord");
cout << "pos6:" << pos6 << endl;
return 0;
}
运行有
在这里再举一个实际运用的例子
int main()
{
//分别获得网络的协议、域名和资源名
// string url = "ftp://www.baidu.com/?tn=65081411_1_oem_dg";
// https://gitee.com/xuanxuan-qujiu-pavilion
string url = "https://zh.wikipedia.org/wiki/Wikipedia:%E9%A6%96%E9%A1%B5";
// 协议 域名 资源名
size_t pos1 = url.find("://");
string protocol;
if (pos1 != string::npos)
{
protocol = url.substr(0, pos1);
}
cout << protocol << endl;
string domain;
string uri;
size_t pos2 = url.find('/', pos1 + 3);
if (pos2 != string::npos)
{
domain = url.substr(pos1 + 3, pos2 - (pos1 + 3));
uri = url.substr(pos2 + 1);
}
cout << domain << endl;
cout << uri << endl;
return 0;
}
⑤string类的非成员函数
源文档
依旧仅对部分函数举例
⑴operator+
源文档
测试代码
int main()
{
string str1 = "hello";
string str2 = " world";
// 将str1和str2拼接起来拷贝构造给str3
string str3 = str1 + str2;
cout << "str3:" << str3 << endl;
// 将一个字符串和str1拼接再拷贝构造给str4
string str4 = "I am " + str1;
cout << "str4:" << str4 << endl;
return 0;
}
运行有
⑵relational operators
测试代码
int main()
{
string str1 = "abc";
string str2 = "Abc";
string str3 = "xyz";
if (str1 == str2)
cout << "str1 == str2" << endl;
else
cout << "str1 != str2" << endl;
if (str1 != str3)
cout << "str1 != str3" << endl;
else
cout << "str1 == str3" << endl;
if (str1 < str3)
cout << "str1 < str3" << endl;
else
cout << "str1 >= str3" << endl;
if (str2 > str3)
cout << "str2 > str3" << endl;
else
cout << "str2 <= str3" << endl;
return 0;
}
运行有
⑶operator<<、operator>>
测试代码
int main()
{
string str;
cout << "Please enter a string: ";
cin >> str;
cout << "You entered: " << str << endl;
return 0;
}
运行有
我们可以发现,输出的只有hello这一个字符串,这是因为cin是读取到' '或'\0'就算作读取一次,因此就需要下面这个函数
⑷getline
测试代码
int main()
{
string str;
cout << "Please enter a string: ";
getline(cin, str);
cout << "You entered: " << str << endl;
return 0;
}
运行有