string对象的初始化和普通类型变量的初始化基本相同,只是string作为类,还有类的一些特性:使用构造函数初始化。如下表,第2 4 6条是作为类才有的初始化方式:
当然,也可以用下面这种格式初始化,其实就是调用string的构造函数生成一个临时的string类,再用临时的string类初始化s5 s6。
- string s5 = string("value");
- string s6(string("value"));
区别初始化和赋值操作:
区别其实很简单,初始化是生成对象的时候(也就是刚分配内存空间时)就给它值;赋值就是过了初始化后,给对象值。下面的例子就是给st1赋值:
- string st1, st2(2,'b');
- st1 = st2; //st1此时已近占据了一块内存
用法很简单,和int、double等内置类型的cin一样使用。不过需要说明一点:
string对象会自动忽略开头的空白(既空格、换行符、制表符等),并从第一个真正的字符开始读入,直到遇到下一处空白
看下面的示例:
- string s1;
- cin >> s1;
- cout << s1 << endl;

忽略了输入Hello world前的空白。从H开始读取字符,在o后面遇到了一处空白,此时不再读取后面的字符。注意,world其实还在缓冲区内,如果再用cin读取,你无法从键盘输入,会直接读到world。
getline的函数格式:getline(cin,string对象)
getline的作用是读取一整行,直到遇到换行符才停止读取,期间能读取像空格、Tab等的空白符。实例如下:
- string s1;
- getline(cin, s1);
- cout << s1 << endl;

注意:getline函数和cin一样,也会返回它的流参数,也就是cin,所以可以用getline循环读取一行:
- string s1;
- while(getline(cin, s1))
- cout << s1 << endl;
直接给出例子:
- string str = "Hello";
- string phrase = "Hello ";
尽管两者的前面对应的字符都一样,但是phrase长度长(多一个空格),所以phrase>str。
- string str2 = "Hello";
- string phrase2 = "Hi ";
这种情况比较的是第一个相异字符,根据字符值比较大小,因为i的字符值>e的字符值,所以phrase2> str2。
总结:看上面两种情况,实际上可以都看成是一个一个字符比较。对于第一种,str最后为空,而phrase为空格,空格字符值不为负,所以phrase>str。
- string str = "Hello,";
- string phrase = "world ";
-
- string s = str + phrase;
- str += phrase;//相当于str = str + phrase
-
- cout << s << endl;
- cout << str << endl;

首先问一个问题:为什么string对象可以加上字符或字符串字面值?实际上是因为它们可以自动转换为string类型。下面看例子:
- string str = "Hello";
- string phrase = "world";
-
- string s = str + ','+ phrase+ '\n';
- cout << s ;

判断下面的加法是否正确?
string str = "Hello";
(1)string s2 = str + "," + "world";(2)string s3 = "Hello" + "," + str;
答案:(1)正确;(2)错误
(2)错误的原因是:当string对象和字符或字符串字面值相加时,必须确保+号的两侧的运算对象至少有一个string。
至于(1),需要明白,str + “,”会返回一个string类。
直接看下面的例子:
- string s = "Hello world!";
- cout << s[0] << endl;
- cout << s[s.size()-1] << endl;
-
- cout << s << endl;
- s[0] = 'h';
- cout << s << endl;

可以通过下标来遍历整个string对象:
- string s = "Hello world!";
- for (decltype(s.size()) index = 0; index != s.size(); index++){
- cout << s[index] << ",";
- }
- cout<<endl;

这里把index的类型声明为s.size()的返回类型:string::size_type,它是无符号类型。当然你也可以用其他类型,但是请注意,下标值不能超过string.size()-1,否则会产生不可预知的结果。
decltype使用可参考:有auto为什么还要decltype ?详解decltype的用法
看下面的例子:
- string s = "Hello world!";
- for (auto i = s.begin(); i != s.end(); i++){
- cout << *i << ",";
- }
- cout << endl;

当然,获取每个字符后,你可以做一些处理,比如讲小写改大写等。
基于范围的for语句是C++11新提供的一种语句,其语法形式是:
declaration:定义一个变量,它每次的值都是expression中的基础元素
expression:一个已经定义的对象(变量)
statement:具体的语句
这样看上去还不明白的话,请看下面的例子,通过例子就可以很好理解:
- string str("some string");
- for (auto c : str)
- cout << c << ",";
- cout << endl;
c就是declaration;str就是expression;cout << c << “,”;就是statement。
for循环把c和str联系起来了。此例中,通过auto关键字让编译器推断c的类型,c这里的类型实际上推断出的是char类型,c每次的值都是str中的一个字符,上述代码的运行的结果如下:
当你需要修改str中的字符时,你可以使用引用,既:for (auto &c : str)。
关于基于范围的for的使用:C++基于范围的for循环:for(auto a:b)
形式:string s(cp,n)解释:将cp所指的数组的前n个字符拷贝给string对象s,n为可选参数。
直接看下面的例子:
定义3个字符数组,注意: cp和cp2以空字符结尾,cp3没有空字符结尾。
- const char *cp = "hello world";//最后有一个空字符
- char cp2[] = "hello world";//最后有一个空字符
- char cp3[] = { 'h', 'e' };//最后没有空字符
- (1) string s1(cp);//s1为”hello world”,长度为11
- (2) string s2(cp2);//s2为”hello world”,长度为11
- (3) string s3(cp3);//因为cp3不以空字符结尾,所以这是未定义行为
-
- (4) string s4(cp,5);//s4为”hello”,长度为5。将cp改为cp2一样
- (5) string s5(cp,13);//s5为”hello world ”,长度为13,后面有两个空字符。将cp改为cp2一样
- (6) string s6(cp3,2);//s6为”he”,长度为2
形式:
解释:
例子:
- string s1("value");
-
- (1) string s2(s1, 1);//s2为” alue”,长度为4
- (2) string s3(s1, 5);//s3为””,长度为0
- (3) string s8(s1, 6);// 错误,未定义的行为,抛出异常
-
- (4) string s4(s1, 1,3);// s4为” alu”,长度为3
- (5) string s5(s1, 1,8);// 正确,s5为” alue”,长度为4
- (6) string s6(s1, 5,8);// s6为””,长度为0
- (7) string s7(s1, 6,1);// 错误,未定义的行为,抛出异常
总结:相比于数组,拷贝string对象时,只能拷贝到string的结尾,这一点可以看数组的(5)和string对象(5)。
格式:s.substr(pos,n)解释:返回一个string对象,返回的对象包含s从pos下标开始的n个字符。pos和n均为可选参数。pos默认为下标0;n默认为s.size()-pos。示例:
- string s ("value");
-
- (1)string s2 = s.substr();//s2为”value”,大小为5
-
- (2)string s3 = s.substr(2);//s3为”lue”,大小为3
- (3)string s4 = s.substr(5);//s3为””,大小为0
- (4)string s5 = s.substr(6);//错误,s5的大小为pos = 5,小于s.size()
-
- (5)string s6 = s.substr(1,2);// s6为”al”,大小为2
- (6)string s7 = s.substr(1,7);// s7为”alue”,大小为4
- (7)string s8 = s.substr(5,7);// s8为””,大小为0
- (8)string s9 = s.substr(6,7);// 错误,s9的大小为pos = 5,小于s.size()
总结:和第2部分“拷贝string对象”的形式2基本相同。
string的insert操作有两种版本的insert:除了顺序容器通用的迭代器版本外还可以使用下标版本。下面分别介绍这两种版本。
有4种形式:
因为是顺序容器通用的迭代器版本,这里不过多介绍,只简单举例说明一下使用。
在list容器的insert操作中,我详细介绍了insert的使用,感兴趣可以看看。C++ STL::list常用操作及底层实现(中1)——实现list常用操作之插入(insert、push_front、push_back、splice)
pos是调用insert的string对象的迭代器位置;ch是单个字符;count是ch的个数。下面是例子。
- string s1("value");
-
- s1.insert(s1.begin(), 's');//执行后,s1为"svalue"
- s1.insert(s1.begin(), 1, 's');//执行后,s1为"ssvalue"
- s1.insert(s1.begin(), s1.begin(), ++s1.begin());//执行后,s1为"sssvalue"
- s1.insert(s1.end(), {'1','2'});//执行后,s1为"sssvalue12"
解释:在下标index前插入count个字符ch。
例子:
- string s1("value");
-
- s1.insert(0,2,’s’); //执行后,s1为” ssvalue”
- s1.insert(5,2,’s’); //执行后,s1为” valuess”
解释:在下标index前插入一个常量字符串或者string对象。
例子:
- string s1("value");
- string s3 = "value";
- const char* cp = "value";
-
- s1.insert(0,s3);//执行完后,s1为" valuevalue"
- s1.insert(0,cp); //执行完后,s1为" valuevalue"
解释:在下标index前插入str中的从str[index_str]开始的count个字符
例子:
- string s1("value");
- string s3 = "value";
- const char* cp = "value";
下面在s1的下标0前插入s3的从s3[1]开始的2个字符
s1.insert(0,s3,1,2);//执行后,s1为” alvalue”
解释:在index前插入常量字符串的count个字符
例子:
- string s1("value");
- const char* cp = "value";
下面在s1的下标0前插入cp的前3个字符
s1.insert(0, cp,3); //执行后,s1为” valvalue”
erase有3种形式,其方法原型如下:
basic_string & erase(size_type pos=0, size_type n=npos)解释:如果string对象s调用,它删除s从pos下标开始的n个字符,并返回删除后的s。当pos > s.size()时,报错
iterator erase(const_iterator position)解释:如果string对象s调用,它删除s迭代器position位置的字符,并返回下一个字符的迭代器。
iterator erase(const_iterator first, const_iterator last)解释:如果string对象s调用,它删除s迭代器[first,last)区间的字符,并返回last字符的迭代器。
例子:
- string s1("value");
- string s2("value");
- string s3("value");
- string s4("value");
-
- s1.erase();//执行后,s1为空
- s2.erase(0,2); //执行后,s2为”lue”
- s3.erase(s3.begin());//执行后,s3为”alue”
- s4.erase(s4.begin(),++s4.begin());//执行后,s4为”alue”
append是在string对象的末尾进行插入操作。这一点使用+运算符也能做到。
- string s("i love China!");
- s.append("forever");//执行完后,s=” i love China! forever”
replace可看作是erase和insert的结合体,它删除指定的字符,删除后再插入指定的字符。
和insert一样,可以通过下标或者是迭代器指定位置。
- string s("i very love China!");
-
- const char* cp1 = "truly";
- const char* cp2 = "truly!!!";
-
- string str1 = "really";
- string str2 = "really!!!";
-
- //1.将s从下标2开始删除4个字符,删除后在下标2处插入cp1
- s.replace(2,4,cp1);//s=” i truly love China!”
- //2.将s从下标2开始删除5个字符,删除后在下标2插入cp2的前5个字符
- s.replace(2, 5, cp2,5); //s=” i truly love China!”
- //3.将s从下标2开始删除5个字符,删除后在下标2插入str1
- s.replace(2, 5, str1);//s=”i really love China!”
- //4.将s从下标2开始删除6个字符,删除后在下标2插入str2从下标0开始的6个字符
- s.replace(2, 6, str2,0,6);//s=”i really love China!”
- //5.将s从下标2开始删除6个字符,删除后在下标2插入4个’*’字符
- s.replace(2, 6, 4, '*');//s=”i **** love China!”
- string s1("bad phrase");
-
- const char* cp3 = "sample";
- const char* cp4 = "sample!!!";
-
- string str3 = "useful";
- string str4 = "useful!!!";
-
- //1.删除[s1.begin(),s1. begin()+3)区间字符,删除后插入cp3
- s1.replace(s1.begin(),s1.begin()+3,cp3);//s1="sample phrase"
- //2.删除[s1.begin(),s1. begin()+6)区间字符,删除后插入cp4的前6个字符
- s1.replace(s1.begin(),s1.begin()+6,cp4,6);//s1="sample phrase"
- //3.删除[s1.begin(),s1. begin()+6)区间字符,删除后插入str3
- s1.replace(s1.begin(),s1.begin()+6, str3);//s1="useful phrase"
- //4.删除[s1.begin(),s1. begin()+6)区间字符,删除后插入str4[str4.begin(),str4. begin()+6)区间字符
- s1.replace(s1.begin(),s1.begin()+6, str4.begin(),str4.begin() + 6);//s1="useful phrase"
- //5. 删除[s1.begin(),s1. begin()+6)区间字符,删除后插入4个’*’字符
- s1.replace(s1.begin(),s1.begin()+6, 4, '*');//s1="**** phrase"
- //6. 删除[s1.begin(),s1. begin()+4)区间字符,删除后插入初始化列表
- s1.replace(s1.begin(), s1.begin() + 4, {'3','4','5'});//s1="345 phrase"
这两个成员函数有太多的参数形式,如何更好的记忆呢?看下表:
说明如下
pos:下标位置
len:个数
b\e\b2\e2:迭代器指向的位置,如begin() end()等
n,c:n个字符c
str:字符串str
cp:cp指向以空字符结尾的字符数组
总结表格:
assign方法可以理解为先将原字符串清空,然后赋予新的值作替换。
格式如下,可以发现,就输入参数而言,和“总结insert和replace”这一节中的表的args参数是一样的,这里不在多做说明,直接给出7个例子,在例子中说明。
实例:
- std::string str;
- std::string base = "The quick brown fox jumps over a lazy dog.";
-
- //1.参数形式1
- str.assign(base);
- std::cout << str << '\n';
- //2.参数形式2:将base从下标10开始的9个字符赋值给str
- str.assign(base, 10, 9);
- std::cout << str << '\n'; // "brown fox"
- //3.参数形式4:将"pangrams are cool"的前7个字符赋值给str
- str.assign("pangrams are cool", 7);
- std::cout << str << '\n'; // "pangram"
- //4.参数形式3:将"c-string"赋值给str
- str.assign("c-string");
- std::cout << str << '\n'; // "c-string"
- //5.参数形式5:将10个字符'*'赋值给str
- str.assign(10, '*');
- std::cout << str << '\n'; // "**********"
- //6.参数形式6:将[base.begin() + 16, base.end() - 12)赋值给str
- str.assign(base.begin() + 16, base.end() - 12);
- std::cout << str << '\n'; // "fox jumps over"
- //7.参数形式7:将初始化列表{'l','o','v','e'}赋值给str
- str.assign({ 'l', 'o', 'v', 'e' });
- std::cout << str << '\n'; // "love"
string提供6个不同的搜索函数,每个函数都有4个重载版本,所有函数的返回值都为string::size_type值,表示匹配发生位置的下标。
| string搜索函数 | 描述 |
|---|---|
| s.find(args) | 在s中查找第一次出现args的下标 |
| s.rfind(args) | 在s中查找最后一次出现args的下标 |
| s.find_first_of(args) | 在s中查找第一个在args中出现的字符,返回其下标 |
| s.find_first_not_of(args) | 在s中查找第一个不在args中出现的字符,返回其下标 |
| s.find_last_of(args) | 在s中查找最后一个在args中出现的字符,返回其下标 |
| s.find_last_not_of(args) | 在s中查找最后一个不在args中出现的字符,返回其下标 |
其中args参数格式如下:
| args参数格式 | 描述 |
|---|---|
| c,pos | 搜索单个字符。从s中位置pos开始查找字符c。pos可省略,默认值为0 |
| s2,pos | 搜索字符串。从s中位置pos开始查找字符串string对象s2。pos可省略,默认值为0 |
| cp,pos | 搜索字符串。从s中位置pos开始查找指针cp指向的以空字符结尾的C风格字符串。pos可省略,默认值为0 |
| cp,pos,n | 从s中位置pos开始查找指针cp指向的数组的前n个字符。 |
注:
下面的例子包含了args对应的4种参数形式。在后面介绍其他函数时将不在针对args都给出例子。
- std::string str("There are two needles in this haystack with needles.");
- std::string str2("needle");
- //1.对应参数args为s2,pos
- std::size_t found = str.find(str2);//返回第一个"needles"n的下标
- if (found != std::string::npos)
- std::cout << "first 'needle' found at: " << found << '\n';
- //2.对应参数args为cp,pos, n
- found = str.find("needles are small", found + 1, 6);
- if (found != std::string::npos)
- std::cout << "second 'needle' found at: " << found << '\n';
- //3.对应参数args为cp,pos
- found = str.find("haystack");
- if (found != std::string::npos)
- std::cout << "'haystack' also found at: " << found << '\n';
- //4.对应参数args为c,pos
- found = str.find('.');
- if (found != std::string::npos)
- std::cout << "Period found at: " << found << '\n';
-
- // 替换第一个needle:
- str.replace(str.find(str2), str2.length(), "preposition");
- std::cout << str << '\n';

把最后一个”sixth”换成” seventh”。
- cout << " rfind()函数:" << endl;
- std::string str("The sixth sick sheik's sixth sheep's sick.");
- std::string key("sixth");
-
- std::size_t found = str.rfind(key);//找到最后一个sixth的下标
- if (found != std::string::npos)
- str.replace(found, key.length(), "seventh");//替换找到的sixth
-
- std::cout << str << '\n';

把str中的所有元音字母aeiou换成*。
- std::string str("Please, replace the vowels in this sentence by asterisks.");
- std::size_t found = str.find_first_of("aeiou");
- while (found != std::string::npos)
- {
- str[found] = '*';
- found = str.find_first_of("aeiou", found + 1);
- }
-
- std::cout << str << '\n';

找到str中的第一个非小写字母或空格的字符。
- std::string str("look for non-alphabetic characters...");
-
- std::size_t found = str.find_first_not_of("abcdefghijklmnopqrstuvwxyz ");
-
- if (found != std::string::npos)
- {
- std::cout << "The first non-alphabetic character is " << str[found];
- std::cout << " at position " << found << '\n';
- }

SplitFilename()函数是将str1和str2中的文件名和文件名所在路径分开并打印。注意,str2中有两个\,其中第一个是转义字符。同样,find_last_of()中的\的第一个也是转义字符。
- #include <iostream> // std::cout
- #include <string> // std::string
- #include <cstddef> // std::size_t
-
- void SplitFilename (const std::string& str)
- {
- std::cout << "Splitting: " << str << '\n';
- std::size_t found = str.find_last_of("/\\");
- std::cout << " path: " << str.substr(0,found) << '\n';
- std::cout << " file: " << str.substr(found+1) << '\n';
- }
-
- int main ()
- {
- std::string str1 ("/usr/bin/man");
- std::string str2 ("c:\\windows\\winhelp.exe");
-
- SplitFilename (str1);
- SplitFilename (str2);
-
- system("pause");
- return 0;
- }

查找最后一个不是whitespaces对象的字符,并删除(erase函数)此字符后面的所有字符。
- std::string str("Please, erase trailing white-spaces \n");
- std::string whitespaces(" \t\f\v\n\r");
-
- std::size_t found = str.find_last_not_of(whitespaces);
- if (found != std::string::npos)
- str.erase(found + 1);
- else
- str.clear(); // str is all whitespace
-
- std::cout << '[' << str << "]\n";

在前面,我们介绍了用比较运算符比较两个字符串的方法,同时string对象中有一个成员函数compare,它也可以比较字符串,并且有6种不同的参数形式,比较字符串时更加灵活。compare的参数形式如下:
| 参数形式s.compare() | 说明 |
|---|---|
| s2 | 比较s和s2 |
| pos1, n1, s2 | 将s中从pos1开始的n1个字符与s2比较 |
| pos1, n1, s2, pos2, n2 | 将s中从pos1开始的n1个字符与s2中从pos2开始的n2个字符比较 |
| cp | 比较s与cp指向的以空字符结尾的数组 |
| pos1, n1, cp | 将s中从pos1开始的n1个字符与cp指向的以空字符结尾的数组比较 |
| pos1, n1, cp,n2 | 将s中从pos1开始的n1个字符与cp指向的以空字符结尾的数组前n个字符比较 |
注意:
例子:
- std::string str1("green apple");
- std::string str2("red apple");
-
- //1.str1和str2比较:参数形式1
- if (str1.compare(str2) != 0)
- std::cout << str1 << " is not " << str2 << '\n';
- //2.str1的下标6开始的5个字符和"apple"比较:参数形式5
- if (str1.compare(6, 5, "apple") == 0)
- std::cout << "still, " << str1 << " is an apple\n";
- //3.str2的下标str2.size() - 5(就是下标6)开始的5个字符和"apple"比较:参数形式5
- if (str2.compare(str2.size() - 5, 5, "apple") == 0)
- std::cout << "and " << str2 << " is also an apple\n";
- //4.str1的下标6开始的5个字符和str2的下标4开始的5个字符比较:参数形式3
- if (str1.compare(6, 5, str2, 4, 5) == 0)
- std::cout << "therefore, both are apples\n";
- //5.str1的下标6开始的5个字符和"apple pie"的前5个字符比较:参数形式6
- if (str1.compare(6, 5, "apple pie",5) == 0)
- std::cout << "apple pie is maked by apple\n";
-
- //6.str1和"poisonous apple"比较:参数形式4
- if (str1.compare("poisonous apple") < 0)
- std::cout << "poisonous apple is not a apple\n";
格式和说明如下:
注意:
std::stod(".3e3")结果为300。- std::string pi = "pi is " + std::to_string(3.1415926);
- std::string perfect = std::to_string(1 + 2 + 4 + 7 + 14) + " is a perfect number";
- std::cout << pi << '\n';
- std::cout << perfect << '\n';

- std::string str_dec = "2001, A Space Odyssey";
- std::string str_hex = "40c3";
- std::string str_bin = "-10010110001";
- std::string str_auto = "0x7f";
-
- std::string::size_type sz; // alias of size_t
-
- //1.转换基数为10进制,sz保存','下标,i_dec = 2001
- int i_dec = std::stoi(str_dec, &sz);
- //2.转换基数为16进制。所以i_hex = 0x40c3,十进制为16579
- int i_hex = std::stoi(str_hex, nullptr, 16);
- //3.转换基数为2进制。所以i_bin = -10010110001B,十进制为-1201
- int i_bin = std::stoi(str_bin, nullptr, 2);
- //4.自动确定 转换基数
- int i_auto = std::stoi(str_auto, nullptr, 0);
-
- std::cout << str_dec << ": " << i_dec << " and [" << str_dec.substr(sz) << "]\n";
- std::cout << str_hex << ": " << i_hex << '\n';
- std::cout << str_bin << ": " << i_bin << '\n';
- std::cout << str_auto << ": " << i_auto << '\n';

- cout << "stof示例" << endl;
- std::string orbits("686.97 365.24");
- std::string::size_type sz; // alias of size_t
-
- //1.mars = 686.97,sz保存空格下标
- float mars = std::stof(orbits, &sz);
- //1.将" 365.24"转换为float类型,earth = 686.97
- float earth = std::stof(orbits.substr(sz));
- std::cout << "One martian year takes " << (mars / earth) << " Earth years.\n";

以上就是string的常用操作使用说明