windows C++ string
https://en.cppreference.com/w/cpp/string/basic_string
std::basic_string
Defined in header
<string> |
||
template< class CharT, |
(1) | |
namespace pmr { template< |
(2) | (since C++17) |
The class template basic_string
stores and manipulates sequences of character-like objects, which are non-array objects of trivial standard-layout type. The class is dependent neither on the character type nor on the nature of operations on that type. The definitions of the operations are supplied via the Traits
template parameter - a specialization of std::char_traits or a compatible traits class.
The elements of a basic_string
are stored contiguously, that is, for a basic_string
s, &*(s.begin() + n) == &*s.begin() + n for any n in [
0,
s.size())
, and *(s.begin() + s.size()) has value CharT() (a null terminator)(since C++11); or, equivalently, a pointer to s[0] can be passed to functions that expect a pointer to the first element of an array(until C++11)a null-terminated array(since C++11) of CharT
.
std::basic_string
satisfies the requirements of AllocatorAwareContainer (except that customized construct
/destroy
are not used for construction/destruction of elements), SequenceContainer and ContiguousContainer(since C++17).
If any of Traits::char_type
and Allocator::char_type
is different from CharT
, the program is ill-formed.
Member functions of However, |
(since C++20) |
Several typedefs for common character types are provided:
Defined in header
<string> |
|
Type | Definition |
std::string | std::basic_string<char> |
std::wstring | std::basic_string<wchar_t> |
std::u8string (C++20) | std::basic_string<char8_t> |
std::u16string (C++11) | std::basic_string<char16_t> |
std::u32string (C++11) | std::basic_string<char32_t> |
std::pmr::string (C++17) | std::pmr::basic_string<char> |
std::pmr::wstring (C++17) | std::pmr::basic_string<wchar_t> |
std::pmr::u8string (C++20) | std::pmr::basic_string<char8_t> |
std::pmr::u16string (C++17) | std::pmr::basic_string<char16_t> |
std::pmr::u32string (C++17) | std::pmr::basic_string<char32_t> |
Template parameters
CharT | - | character type |
Traits | - | traits class specifying the operations on the character type |
Allocator | - | Allocator type used to allocate internal storage |
Member types
Member type | Definition | ||||
traits_type |
Traits |
||||
value_type |
CharT |
||||
allocator_type |
Allocator |
||||
size_type |
|
||||
difference_type |
|
||||
reference |
value_type& |
||||
const_reference |
const value_type& | ||||
pointer |
|
||||
const_pointer |
|
||||
iterator |
|
||||
const_iterator |
|
||||
reverse_iterator |
std::reverse_iterator<iterator> | ||||
const_reverse_iterator |
std::reverse_iterator<const_iterator> |
Member functions
constructs a basic_string (public member function) |
|
destroys the string, deallocating internal storage if used (public member function) |
|
assigns values to the string (public member function) |
|
assign characters to a string (public member function) |
|
(C++23)
|
assign a range of characters to a string (public member function) |
returns the associated allocator (public member function) |
|
Element access |
|
accesses the specified character with bounds checking (public member function) |
|
accesses the specified character (public member function) |
|
(DR*)
|
accesses the first character (public member function) |
(DR*)
|
accesses the last character (public member function) |
returns a pointer to the first character of a string (public member function) |
|
returns a non-modifiable standard C character array version of the string (public member function) |
|
(C++17)
|
returns a non-modifiable string_view into the entire string (public member function) |
Iterators |
|
(DR*)
|
returns an iterator to the beginning (public member function) |
(DR*)
|
returns an iterator to the end (public member function) |
(DR*)
|
returns a reverse iterator to the beginning (public member function) |
(DR*)
|
returns a reverse iterator to the end (public member function) |
Capacity |
|
checks whether the string is empty (public member function) |
|
returns the number of characters (public member function) |
|
returns the maximum number of characters (public member function) |
|
reserves storage (public member function) |
|
returns the number of characters that can be held in currently allocated storage (public member function) |
|
(DR*)
|
reduces memory usage by freeing unused memory (public member function) |
Modifiers |
|
clears the contents (public member function) |
|
inserts characters (public member function) |
|
(C++23)
|
inserts a range of characters (public member function) |
removes characters (public member function) |
|
appends a character to the end (public member function) |
|
(DR*)
|
removes the last character (public member function) |
appends characters to the end (public member function) |
|
(C++23)
|
appends a range of characters to the end (public member function) |
appends characters to the end (public member function) |
|
replaces specified portion of a string (public member function) |
|
(C++23)
|
replaces specified portion of a string with a range of characters (public member function) |
copies characters (public member function) |
|
changes the number of characters stored (public member function) |
|
(C++23)
|
changes the number of characters stored and possibly overwrites indeterminate contents via user-provided operation (public member function) |
swaps the contents (public member function) |
|
Search |
|
finds the first occurrence of the given substring (public member function) |
|
find the last occurrence of a substring (public member function) |
|
find first occurrence of characters (public member function) |
|
find first absence of characters (public member function) |
|
find last occurrence of characters (public member function) |
|
find last absence of characters (public member function) |
|
Operations |
|
compares two strings (public member function) |
|
(C++20)
|
checks if the string starts with the given prefix (public member function) |
(C++20)
|
checks if the string ends with the given suffix (public member function) |
(C++23)
|
checks if the string contains the given substring or character (public member function) |
returns a substring (public member function) |
|
Constants |
|
[static]
|
special value. The exact meaning depends on the context (public static member constant) |
Non-member functions
concatenates two strings or a string and a char (function template) |
|
(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(C++20)
|
lexicographically compares two strings (function template) |
specializes the std::swap algorithm (function template) |
|
erases all elements satisfying specific criteria (function template) |
|
Input/output |
|
performs stream input and output on strings (function template) |
|
read data from an I/O stream into a string (function template) |
|
Numeric conversions |
|
(C++11)(C++11)(C++11)
|
converts a string to a signed integer (function) |
(C++11)(C++11)
|
converts a string to an unsigned integer (function) |
(C++11)(C++11)(C++11)
|
converts a string to a floating point value (function) |
(C++11)
|
converts an integral or floating point value to string (function) |
(C++11)
|
converts an integral or floating point value to wstring (function) |
Literals
Defined in inline namespace
std::literals::string_literals |
|
(C++14)
|
converts a character array literal to basic_string (function) |
Helper classes
(C++11)
|
hash support for strings (class template specialization) |
c++标准库里面没有像java的String类中提供的字符分割函数split ,着实不方便。
1.简洁高效的方法(不过只能包含一个分隔符):
1 #include <vector>
2 #include <string>
3 #include <iostream>
4 using namespace std;
5
6 void SplitString(const string& s, vector<string>& v, const string& c)
7 {
8 string::size_type pos1, pos2;
9 pos2 = s.find(c);
10 pos1 = 0;
11 while(string::npos != pos2)
12 {
13 v.push_back(s.substr(pos1, pos2-pos1));
14
15 pos1 = pos2 + c.size();
16 pos2 = s.find(c, pos1);
17 }
18 if(pos1 != s.length())
19 v.push_back(s.substr(pos1));
20 }
21
22 int main(){
23 string s = "a,b,c,d,e,f";
24 vector<string> v;
25 SplitString(s, v,","); //可按多个字符来分隔;
26 for(vector<string>::size_type i = 0; i != v.size(); ++i)
27 cout << v[i] << " ";
28 cout << endl;
29 //输出: a b c d e f
30 }
当处理有空格的字符串时,还是很有用的!!
2.可包含多个分隔符的实现方式
1 #include <vector>
2 #include <string>
3 #include <iostream>
4 using namespace std;
5
6 vector<string> split(const string &s, const string &seperator){
7 vector<string> result;
8 typedef string::size_type string_size;
9 string_size i = 0;
10
11 while(i != s.size()){
12 //找到字符串中首个不等于分隔符的字母;
13 int flag = 0;
14 while(i != s.size() && flag == 0){
15 flag = 1;
16 for(string_size x = 0; x < seperator.size(); ++x)
17 if(s[i] == seperator[x]){
18 ++i;
19 flag = 0;
20 break;
21 }
22 }
23
24 //找到又一个分隔符,将两个分隔符之间的字符串取出;
25 flag = 0;
26 string_size j = i;
27 while(j != s.size() && flag == 0){
28 for(string_size x = 0; x < seperator.size(); ++x)
29 if(s[j] == seperator[x]){
30 flag = 1;
31 break;
32 }
33 if(flag == 0)
34 ++j;
35 }
36 if(i != j){
37 result.push_back(s.substr(i, j-i));
38 i = j;
39 }
40 }
41 return result;
42 }
43
44 int main(){
45 // string s = "a,b*c*d,e";
46 string s;
47 getline(cin,s);
48 vector<string> v = split(s, ",*"); //可按多个字符来分隔;
49 for(vector<string>::size_type i = 0; i != v.size(); ++i)
50 cout << v[i] << " ";
51 cout << endl;
52 //输出: a b c d e
53 }
3.用C语言中的strtok 函数来进行分割
1 #include <string.h>
2 #include <stdio.h>
3
4 int main(){
5 char s[] = "a,b*c,d";
6 const char *sep = ",*"; //可按多个字符来分割
7 char *p;
8 p = strtok(s, sep);
9 while(p){
10 printf("%s ", p);
11 p = strtok(NULL, sep);
12 }
13 printf("\n");
14 return 0;
15 }
16 //输出: a b c d
参考博客:C++中String类的字符串分割实现
c++ string 有哪些函数:
C++ 中的 `std::string` 类提供了许多成员函数,用于处理字符串。以下是一些常用的 `std::string` 函数:
1. **构造函数:**
- `string()`:默认构造函数,创建一个空字符串。
- `string(const char* s)`:使用 C 风格字符串初始化。
- `string(const string& str)`:复制构造函数,使用另一个字符串初始化。
2. **赋值和拷贝:**
- `operator=`:赋值运算符,用于将一个字符串的内容复制到另一个字符串。
- `assign(str)`:将字符串 `str` 的内容赋给当前字符串。
- `assign(str, pos, len)`:将字符串 `str` 从位置 `pos` 复制 `len` 个字符到当前字符串。
3. **访问和修改:**
- `at(pos)` 或 `[pos]`:返回字符串中位置 `pos` 处的字符。
- `front()`:返回字符串的第一个字符。
- `back()`:返回字符串的最后一个字符。
- `c_str()`:返回一个指向以 null 结尾的字符数组的指针。
- `data()`:返回一个指向字符串数据的指针。
- `append(str)`:将字符串 `str` 追加到当前字符串的末尾。
- `push_back(c)`:将字符 `c` 添加到字符串的末尾。
- `pop_back()`:删除字符串的最后一个字符。
4. **比较:**
- `compare(str)`:比较字符串和 `str`,返回整数(<0、0、>0)。
- `==`, `!=`, `<`, `<=`, `>`, `>=`:用于比较两个字符串。
5. **查找和替换:**
- `find(str)`:查找字符串 `str` 的第一次出现的位置。
- `rfind(str)`:查找字符串 `str` 的最后一次出现的位置。
- `substr(pos, len)`:返回从位置 `pos` 开始的 `len` 个字符的子字符串。
- `replace(pos, len, str)`:用字符串 `str` 替换从位置 `pos` 开始的 `len` 个字符。
6. **大小和容量:**
- `size()` 或 `length()`:返回字符串中的字符数。
- `capacity()`:返回字符串当前分配的存储容量。
- `resize(new_size)`:将字符串的大小调整为 `new_size`。
- `reserve(new_cap)`:请求字符串的存储容量至少为 `new_cap`。
这只是 `std::string` 类的一小部分函数。C++标准库提供了更多的字符串处理函数,具体的函数和用法可以在C++参考文档中找到。
C++ string 类详解
字符串是存储在内存的连续字节中的一系列字符。C++ 处理字符串的方式有两种,一种来自 C 语言,常被称为 C-风格字符串,另一种是基于 string 类库的字符串处理方式。C 风格字符串的处理可以参考 https://www.cnblogs.com/tongye/p/10688941.html ,本文着重介绍 string 类库的使用。
一、string 类简介
C++ 中提供了专门的头文件 string(注意不是 string.h,这个是 C 风格字符串相关函数的头文件),来支持 string 类型。string 类定义隐藏了字符串的数组性质,让我们可以像处理普通变量那样处理字符串。string 对象和字符数组之间的主要区别是:可以将 string 对象声明为简单变量,而不是数组。
1.1 string 类几种常见的构造函数:
1)string(const char *s) :将 string 对象初始化为 s 指向的字符串
string str("Hello!");
2)string(size_type n,char c) :创建一个包含 n 个元素的 string 对象,其中每个元素都被初始化为字符 c
string str(10,'a');
3)string(const string &str) :将一个 string 对象初始化为 string 对象 str(复制构造函数)
string str1("hello!");
string str2(str1);
4)string() :创建一个默认的 string 对象,长度为 0(默认构造函数)
string str; // 创建一个空的 string 对象
string 类的设计允许程序自动处理 string 的大小,因此,上述代码创建了一个长度为 0 的string 对象,但是向 str 中写入数据时,程序会自动调整 str 的长度。因此,与使用数组相比,使用 string 对象更方便,也更安全。
1.2 用 C 语言风格初始化 string 对象:
C++ 允许使用 C 语言风格来初始化 string 对象:
string str = "hello!";
二、获取 string 对象的长度
在 C 语言中,使用 strlen 函数获取字符串的长度。在 C++ 中,可以使用 string.size() 函数或 string.length() 函数来获得 string 对象的长度。在 C++ 标准库中,两者的源代码如下:
size_type __CLR_OR_THIS_CALL length() const
{ // return length of sequence
return (_Mysize);
}
size_type __CLR_OR_THIS_CALL size() const
{ // return length of sequence
return (_Mysize);
}
可见,这两个方法是完全一样的,并没有区别。length() 方法是 C 语言习惯保留的,size() 方法则是为了兼容 STL 容器而引入的。
string str("Hello,World!");
int strLen1 = str.length();
int strLen2 = str.size();
三、复制 string 对象
在 C 语言中,使用 strcpy、strncpy 函数来实现字符串的复制。在 C++ 中则方便很多,可以直接将一个 string 对象赋值给另一个 string 对象,即:
string str1("Hello,World!");
string str2;
str2 = str1;
由于 string 类会自动调整对象的大小,因此不需要担心目标数组不够大的问题。
四、string 对象的拼接和附加
在 C 语言中,使用 strcat、strncat 函数来进行字符串拼接操作。在 C++ 中也有多种方法来实现字符串拼接和附加操作:
4.1 使用 + 操作符拼接两个字符串
string str1("hello ");
string str2("world!");
string str3 = str1 + str2;
4.1 使用 += 操作符在字符串后面附加内容
可以使用 += 来在一个 string 对象后面附加一个 string 对象、字符以及 C 风格的字符串:
string str1("hello ");
string str2("world!\n");
str1 += str2;
str1 += "nice job\n";
str1 += 'a';
4.2 使用 string.append() 函数
可以使用 string.append() 函数来在一个 string 对象后面附加一个 string 对象或 C 风格的字符串:
string str1 = "hello,world!";
string str2 = "HELLO,WORLD!";
str1.append(str2);
str1.append("C string");
4.3 使用 string.push_back() 函数
可以使用 string.push_back() 函数来在一个 string 对象后面附加一个字符:
string str("Hello");
str.push_back('a');
五、string 对象的比较
在 C 语言中,使用 strcmp、strncmp 函数来进行字符串的比较。在 C++ 中,由于将 string 对象声明为了简单变量,故而对字符串的比较操作十分简单了,直接使用关系运算符(==、!=、<、<=、>、>=)即可:
#include <string>
#include <iostream>
using namespace std;
int main()
{
string str1("hello");
string str2("hello");
if (str1 == str2)
cout << "str1 = str2" << endl;
else if (str1 < str2)
cout << "str1 < str2" << endl;
else
cout << "str1 > str2" << endl;
return 0;
}
当然,也可以使用类似 strcmp 的函数来进行 string 对象的比较,string 类提供的是 string.compare() 方法,函数原型如下:
int compare(const string&str) const;
int compare(size_t pos,size_t len,const string&str)const; // 参数 pos 为比较字符串中第一个字符的位置,len 为比较字符串的长度
int compare(size_t pos,size_t len,const string&str, size_t subpos,size_t sublen)const;
int compare(const char * s)const;
int compare(size_t pos,size_t len,const char * s)const;
int compare(size_t pos,size_t len,const char * s,size_t n)const;
compare 方法的返回值如下:
1)返回 0,表示相等;
2)返回结果小于 0,表示比较字符串中第一个不匹配的字符比源字符串小,或者所有字符都匹配但是比较字符串比源字符串短;
3)返回结果大于 0,表示比较字符串中第一个不匹配的字符比源字符串大,或者所有字符都匹配但是比较字符串比源字符串长。
六、使用 string.substr() 函数来获取子串
可以使用 string.substr() 函数来获取子串,string.substr() 函数的定义如下:
string substr(size_t pos = 0,size_t len = npos)const;
其中,pos 是子字符串的起始位置(索引,第一个字符的索引为 0),len 是子串的长度。这个函数的功能是:复制一个 string 对象中从 pos 处开始的 len 个字符到 string 对象 substr 中去,并返回 substr。
string str("Hello,World!");
string subStr = str.substr(3,5);
cout << subStr << endl;
这段代码的输出结果为:"lo,Wo"。
七、访问 string 字符串的元素
可以像 C 语言中一样,将 string 对象当做一个数组,然后使用数组下标的方式来访问字符串中的元素;也可以使用 string.at(index) 的方式来访问元素(索引号从 0 开始):
string str("Hello,World!");
cout << str[1] << endl; // 使用数组下标的方式访问 string 字符串的元素
cout << str.at(1) << endl; // 使用 at 索引访问 string 字符串的元素
八、string 对象的查找操作
8.1 使用 string.find() 方法查找字符
find 方法的函数原型如下:
1)从字符串的 pos 位置开始(若不指定 pos 的值,则默认从索引 0 处开始),查找子字符串 str。如果找到,则返回该子字符串首次出现时其首字符的索引;否则,返回 string::npos:
size_type find (const string& str, size_type pos = 0) const;
2)从字符串的 pos 位置开始(若不指定 pos 的值,则默认从索引 0 处开始),查找子字符串 s。如果找到,则返回该子字符串首次出现时其首字符的索引;否则,返回 string::npos:
size_type find (const char *s, size_type pos = 0) const;
3)从字符串的 pos 位置开始(若不指定 pos 的值,则默认从索引 0 处开始),查找 s 的前 n 个字符组成的子字符串。如果找到,则返回该子字符串首次出现时其首字符的索引;否则,返回 string::npos:
size_type find (const char *s, size_type pos, size_type n);
4)从字符串的 pos 位置开始(若不指定 pos 的值,则默认从索引 0 处开始),查找字符 ch 。如果找到,则返回该字符首次出现的位置;否则,返回 string::npos:
size_type find (char ch, size_type pos = 0) const;
举个查找子字符串的例子(查找字符的代码与这一样,只需要将 find 函数的参数换成字符即可):
#include <string>
#include <iostream>
using namespace std;
int main()
{
string str("cat,dog,cat,pig,little cat,hotdog,little pig,angry dog");
size_t catPos = str.find("cat",0);
if (catPos == string::npos) {
printf("没有找到字符串\n");
return 0;
}
while (catPos != string::npos) {
cout << "在索引 " << catPos << " 处找到字符串" << endl;
catPos = str.find("cat", catPos + 1);
}
return 0;
}
程序输出结果如下:
8.2 string.rfind()
string.rfind() 与 string.find() 方法类似,只是查找顺序不一样, string.rfind() 是从指定位置 pos (默认为字符串末尾)开始向前查找,直到字符串的首部,并返回第一次查找到匹配项时匹配项首字符的索引。换句话说,就是查找子字符串或字符最后一次出现的位置。还是以上面的程序为例,稍作修改:
#include <string>
#include <iostream>
using namespace std;
int main()
{
string str("cat,dog,cat,pig,little cat,hotdog,little pig,angry dog");
size_t catPos = str.rfind("cat",str.length()-1);
if (catPos == string::npos) {
printf("没有找到字符串\n");
return 0;
}
while (catPos != string::npos) {
cout << "在索引 " << catPos << " 处找到字符串" << endl;
catPos = str.rfind("cat", catPos - 1);
if (catPos == 0) {
cout << "在索引 " << catPos << " 处找到字符串" << endl;
break;
}
}
return 0;
}
程序输出结果如下:
可以看到,rfind 方法是从字符串末开始查找的。
8.3 string.find_first_of()
string.find_first_of() 方法在字符串中从指定位置开始向后(默认为索引 0 处)查找参数中任何一个字符首次出现的位置。举个例子说明:
#include <string>
#include <iostream>
using namespace std;
int main()
{
string str("cat,dog,cat,pig,little cat,hotdog,little pig,angry dog");
size_t pos = str.find_first_of("zywfgat");
if (pos == string::npos) {
printf("没有匹配到\n");
return 0;
}
else
cout << "在索引 " << pos << " 处匹配到" << endl;
return 0;
}
程序输出结果是:在索引 1 处匹配到。所查找的字符串 zywfgat 中,第一次出现在字符串 str 中的字符是 'a',该字符在 str 中的索引是 1.
8.4 string.find_last_of()
string.find_last_of() 方法在字符串中查找参数中任何一个字符最后一次出现的位置(也就是从指定位置开始往前查找,第一个出现的位置)。
8.5 string.find_first_not_of()
string.find_first_not_of() 方法在字符串中查找第一个不包含在参数中的字符。
8.6 string.find_last_not_of()
string.find_last_not_of() 方法在字符串中查找最后一个不包含在参数中的字符(从指定位置开始往前查找,第一个不包含在参数中的字符)。
九、string 对象的插入和删除操作
9.1 使用 string.insert() 进行插入操作
函数原型如下:
string&insert(size_t pos,const string&str); // 在位置 pos 处插入字符串 str
string&insert(size_t pos,const string&str,size_t subpos,size_t sublen); // 在位置 pos 处插入字符串 str 的从位置 subpos 处开始的 sublen 个字符
string&insert(size_t pos,const char * s); // 在位置 pos 处插入字符串 s
string&insert(size_t pos,const char * s,size_t n); // 在位置 pos 处插入字符串 s 的前 n 个字符
string&insert(size_t pos,size_t n,char c); // 在位置 pos 处插入 n 个字符 c
iterator insert (const_iterator p, size_t n, char c); // 在 p 处插入 n 个字符 c,并返回插入后迭代器的位置
iterator insert (const_iterator p, char c); // 在 p 处插入字符 c,并返回插入后迭代器的位置
举个例子:
#include <string>
#include <iostream>
using namespace std;
int main()
{
string str("abcdefgh");
str.insert(1,"INSERT"); // 在位置 1 处插入字符串 "INSERT"
cout << str << endl;
str.insert(10, 5, 'A'); // 在位置 10 处插入 5 个字符 'A'
cout << str << endl;
return 0;
}
输出结果如下:
9.2 使用 string.erase() 进行元素删除操作
函数原型如下:
string& erase (size_t pos = 0, size_t len = npos); // 删除从 pos 处开始的 n 个字符
iterator erase (const_iterator p); // 删除 p 处的一个字符,并返回删除后迭代器的位置
iterator erase (const_iterator first, const_iterator last); // 删除从 first 到 last 之间的字符,并返回删除后迭代器的位置
举个例子:
#include <string>
#include <iostream>
using namespace std;
int main()
{
string str("Hello,World!");
str.erase(5,6); // 删除从索引位置 5 开始的 6 个字符
cout << "str 为:" << str << endl;
return 0;
}
关于 erase() 函数的用法可以参考 https://www.cnblogs.com/liyazhou/archive/2010/02/07/1665421.html
十、string 对象的一些其他操作
10.1 使用 getline() 函数来获取 string 输入
string str;
getline(cin,str); // 从输入流中读取一行数据到 str
10.2 使用 empty() 函数判断字符串是否为空
string str;
if(str.empty()){
cout << "字符串为空" << endl;
}
string.empty() 函数,若字符串为空,则返回真,否则返回假。
10.3 使用 swap 函数交换两个字符串
#include <string>
#include <iostream>
using namespace std;
int main()
{
string str1 = "hello,world!";
string str2 = "HELLO,WORLD!";
str1.swap(str2);
cout << str1 << endl;
cout << str2 << endl;
return 0;
}
参考资料:
C++ Primer Plus(第六版)
char szName[100] = "abcde\0123";
string str1 = szName; //str1是abcde
string str2(szName); //str2是abcde
string str3(szName, 9); //str3是abcde\0123, 该构造函数常用于保存二进制数据
参考:
https://www.cnblogs.com/yuehouse/p/10230589.html
https://blog.csdn.net/qq_33826977/article/details/79306496
https://www.cnblogs.com/tongye/p/10760154.html
https://en.cppreference.com/w/cpp/string/basic_string