C++中的string

1. 基本操作

1.1 头文件

1
#include <string>

1.2 声明与初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
string s0("Initial string");

// constructors used in the same order as described above:
string s1; // 默认初始化,s1为一个空字符串
string s2(s0); // s2是s0的副本
string s2 = s0; // 与上面是等价的
string s3(s0, 8, 3); // 将s0,从下标8开始,拷贝3个字符至s3
string s4("A character sequence"); // s4字符串的副本,处理字面值最后的那个空字符串外
string s4 = "A character sequence"; // 与上面是等价的
string s5("Another character sequence", 12);// 从字符串下标12开始,直到字符串结尾进行拷贝
string s6a(10, 'x'); // 把s6a初始化为10个字符'x'组成的串
string s6b(10, 42); // 把s6a初始化为10个'*'组成的串,42ASCII码为'*'
string s7(s0.begin(), s0.begin() + 7); // 从s0第一个个迭代器开始拷贝,直到s0.begin() + 7个迭代器
char cs[] = "12345";
string s8(cs, 3); // 复制字符串cs的前3个字符串到s当中

2. 运算符重载

+ 和 += 连接字符串
= 字符串赋值
>、>=、< 和 <= 字符串比大小(例如a < b, aa < ab)
==、!= 字符串是否相等
cin << s、cout >> s 字符串输入输出,不能读入空格,以空格、制表符、回车符为结束标志
getline(cin, s) 字符串输入输出,可以读入空格和制表符,以回车符作为结束标志

注意:使用重载的运算符 + 时,必须保证前两个操作数至少有一个为 string 类型。例如,下面的写法是不合法的:

1
2
3
4
5
6
7
8
#include <iostream>
#include <string>
int main() {
string str1 = "cat";
cout << "apple" + "boy" + str1; // 不合法
string str2 = str1 + "apple"; // 合法
return 0;
}

3. 字符串处理

3.1 使用下标访问元素 []

1
cout << str[0] << endl;

3.2 使用 at() 方法访问

1
cout << str.at(0) << endl;  // (如果溢出会抛出异常)

3.3 size() 获取字符串长度

1
2
string str = "12345";
str.size(); // 长度为5

3.4 length() 获取字符串长度

1
2
string str = "12345";
str.length(); // 长度为5

3.5 capacity() 获取字符串容量

1
2
string str = "12345";
str.capacity(); // 字符串容量为5

3.6 resize() 更改字符串大小

这里改变的是 size() 大小,即string中字符数量。
(1)若新的大小n比原来的 size() 小,则多于n的部分直接删除。
(2)若新的大小n大于string中当前元素数量,则会在string当前的尾部掺入适量元素,是的vector的大小变为n。如果,为 resize() 方法指定了第二个参数,则会把后插入的原始值,初始化为该指定值,如果没有为 resize() 指定第二个参数,则会把新插入的元素初始化为默认的初始值。

1
2
3
string str = "12345";
str.resize(3); // 结果为 123
str.resize(str.size()+2, '+'); // 结果为 123++

3.7 reserve() 改变字符串容量大小

3.8 empty() 字符串是否为空

1
str.empty();

3.9 clear() 清除字符串

1
str.clear();

3.10 substr() 获取子串

1
2
3
4
5
6
string s = "abcdefg";
// s.substr(pos1,n) : 返回字符串位置为pos1后面的n个字符组成的串
string s2 = s.substr(1, 5);// bcdef

// s.substr(pos) : 得到一个pos到结尾的串
string s3 = s.substr(4); // efg

3.11 push_back() 末尾追加一个字符

1
2
3
str.push_back('c');	 // 末尾追加一个字符'c'
str += "c"; // 末尾追加一个字符串"c"
str.append("c") // 末尾追加一个字符串"c"

3.12 pop_back() 删除末尾一个字符

1
str.pop_back();

以上这两个方法在 string中不常用
因为字符串追加+= 运算符,append()insert()
字符串删除erase()substr()

3.13 append() 末尾追加 字符 或 字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <string>

int main () {
std::string str;
std::string str2="Writing ";
std::string str3="print 10 and then 5 more";

//直接追加一个str2的字符串
str.append(str2); // "Writing "
//后面追加str3第6个字符开始的3个字符串
str.append(str3, 6, 3); // "10 "
//追加字符串形参的前5个字符
str.append("dots are cool", 5); // "dots "
//直接添加
str.append("here: "); // "here: "
//添加10个'.'
str.append(10u, '.'); // ".........."
//添加str3迭代器范围的字符串
str.append(str3.begin()+8, str3.end()); // " and then 5 more"
//最后这个比较特殊,意思是添加5个'A',实际上参数里面的65对应的asc码就是65
str.append<int>(5, 65); // "....."
//字符串追加也可以用重载运算符实现
str += "lalala";
std::cout << str << '\n';
return 0;
}

3.14 insert() 插入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
using namespace std;
int main(){
string str="to be question";
string str2="the ";
string str3="or not to be";
string::iterator it;

//s.insert(pos,str): 在s的pos位置插入str,下标从0开始
str.insert(6,str2); // to be the question

//s.insert(pos,str,a,n): 在s的pos位置插入str中插入位置a到后面的n个字符
str.insert(6,str3,3,4); // to be not the question

//s.insert(pos,cstr,n): 在pos位置插入cstr字符串从开始到后面的n个字符
str.insert(10,"that is cool",8); // to be not that is the question

//s.insert(pos,cstr): 在s的pos位置插入cstr
str.insert(10,"to be "); // to be not to be that is the question

//s.insert(pos,n,ch): 在s.pos位置上面插入n个ch
str.insert(15,1,':'); // to be not to be: that is the question

//s.insert(s.it,ch): 在s的it指向位置前面插入一个字符ch,返回新插入的位置的迭代器
it = str.insert(str.begin()+5,','); // to be, not to be: that is the question

//s.insert(s.it,n,ch): 在s的it所指向位置的前面插入n个ch
str.insert(str.end(),3,'.'); // to be, not to be: that is the question...

//s.insert(it,str.ita,str.itb): 在it所指向的位置的前面插入[ita,itb)的字符串
str.insert (it+2,str3.begin(),str3.begin()+3); // to be, or not to be: that is the question...

return 0;
}

3.15 replace() 替换

replace支持使用无符号整数寻找位置,也支持用迭代器寻找位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <string>
using namespace std;
int main () {
string base = "this is a test string.";
string str2 = "n example";
string str3 = "sample phrase";
string str4 = "useful.";

// replace signatures used in the same order as described above:

// Using positions: 0123456789*123456789*12345
string str = base; // "this is a test string."
// 第9个字符以及后面的4个字符被str2代替
str.replace(9, 5, str2); // "this is an example string." (1)
// 第19个字符串以及后面的5个字符用str3的第7个字符以及后面的5个字符代替
str.replace(19, 6, str3, 7, 6); // "this is an example phrase." (2)
// 第8个字符以及后面的9个字符用字符串参数代替
str.replace(8, 10, "just a"); // "this is just a phrase." (3)
// 第8个字符以及后面的5个字符用字符串参数的前7个字符替换
str.replace(8, 6, "a shorty", 7); // "this is a short phrase." (4)
// 第22以及后面的0个字符用3个叹号替换
str.replace(22, 1, 3, '!'); // "this is a short phrase!!!" (5)
// 迭代器的原理同上
// Using iterators: 0123456789*123456789*
str.replace(str.begin(),str.end()-3,str3); // "sample phrase!!!" (1)
str.replace(str.begin(),str.begin()+6,"replace");// "replace phrase!!!" (3)
str.replace(str.begin()+8,str.begin()+14,"is coolness",7);// "replace is cool!!!" (4)
str.replace(str.begin()+12,str.end()-4,4,'o'); // "replace is cooool!!!" (5)
str.replace(str.begin()+11,str.end(),str4.begin(),str4.end());// "replace is useful." (6)
std::cout << str << '\n';
return 0;
}

以上的replace操作可以用insert和erase的操作组合替换,但是replace操作更加方便。

3.16 assign() 为字符串赋新值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <string>
using namespace std;
int main () {
string str;
string base = "The quick brown fox jumps over a lazy dog.";

// used in the same order as described above:
// 直接把base赋值给str
str.assign(base);
std::cout << str << '\n';
// 把base第10个字符以及后面的8个字符赋给str
str.assign(base, 10, 9);
std::cout << str << '\n'; // "brown fox"
// 把参数中的0到6个字符串赋给str
str.assign("pangrams are cool", 7);
std::cout << str << '\n'; // "pangram"
// 直接使用参数赋值
str.assign("c-string");
std::cout << str << '\n'; // "c-string"
// 给str赋值10个'*'字符
str.assign(10, '*');
std::cout << str << '\n'; // "**********"
// 赋值是10个'-'
str.assign<int>(10, 0x2D);
std::cout << str << '\n'; // "----------"
// 指定base迭代器范围的字符串
str.assign(base.begin()+16, base.end()-12);
std::cout << str << '\n'; // "fox jumps over"

return 0;
}

3.17 erase() 删除

删除操作有三种:

  • 指定pos和len,其中pos为为起始位置,pos以及后面len-1个字符串都删除
  • 迭代器,删除迭代器指向的字符
  • 迭代器范围,删除这一范围的字符串,范围左闭右开
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <string>

int main () {
std::string str ("This is an example sentence.");
std::cout << str << '\n'; // "This is an example sentence."

// 直接指定删除的字符串位置第十个后面的8个字符
str.erase(10, 8);
std::cout << str << '\n'; // "This is an sentence."

// 删除迭代器指向的一个字符
str.erase(str.begin()+9); // "This is a sentence."
std::cout << str << '\n';

// 删除迭代器范围的字符
str.erase (str.begin()+5, str.end()-9);
std::cout << str << '\n'; // "This sentence."
return 0;
}

3.18 compare() 函数

由于string重载了运算符,可以直接用>、<、>=、<=、==、!=来进行比较
或者使用 compare() 函数比较,其和strcmp函数一样,如果两个字符串相等,那么返回0,调用对象大于参数返回1,小于返回-1。 在compare当中还支持部分比较,里面有6个参数可以设置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
string s1="123",s2="123";
cout<<s1.compare(s2)<<endl;//0

s1="123",s2="1234";
cout<<s1.compare(s2)<<endl;//-1

s1="1234",s2="123";
cout<<s1.compare(s2)<<endl;//1

std::string str1 ("green apple");
std::string str2 ("red apple");

if (str1.compare(str2) != 0)
std::cout << str1 << " is not " << str2 << '\n';
//str1的第6个字符以及后面的4个字符和参数比较
if (str1.compare(6,5,"apple") == 0)
std::cout << "still, " << str1 << " is an apple\n";

if (str2.compare(str2.size()-5,5,"apple") == 0)
std::cout << "and " << str2 << " is also an apple\n";
//str1的第6个字符以及后面的4个字符和str2的第4个字符以及后面的4个字符比较
if (str1.compare(6,5,str2,4,5) == 0)
std::cout << "therefore, both are apples\n";
return 0;
}

3. string 转换为 const char* 、char * 或者 char[]

1、如果要将 string 转换为 const char* ,可以使用string提供的函数 c_str() ,或是函数 data()

  • data() 除了返回字符串内容外,不附加结束符’’
  • c_str() 返回一个以‘’结尾的字符数组

2、const char *c_str()
函数返回一个指向正规C字符串的指针,内容与本string串相同。这是为了与c语言兼容,在c语言中没有string类型,故必须通过string类对象的成员函数 c_str() 把string 对象转换成c中的字符串样式。注意:一定要使用 strcpy() 函数 等来操作方法 c_str() 返回的指针

比如:最好不要这样

1
2
3
string s = "1234";
const char* p = s.c_str(); // c最后指向的内容是垃圾,因为s对象被析构,其内容被处理
// 加const或者用char *p = (char*)str.c_str(); 强制转换

应该使用如下方式:

3.1 调用 string的 c_str() 函数 [返回的是const char , 或者使用(char ) 强转成 char *]

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <string>
#include <cstring>
int main() {

char p[20];
string str = "hello world";
// c_str()函数返回一个指向正规C字符串的指针cosnt char *, 内容与本string串相同,以'\0'结尾.
strcpy(p, str.c_str()); // 使用strcpy将返回的字符串拷贝至char p[] 数组
cout << p << endl;
}

3.2 调用 string 的 data() 函数 [返回的是const char , 或者使用(char ) 强转成 char *]

和上面方法一致,只是返回字符串内容,不附加结束符’’

3.3 调用 string 的 copy() 函数 [返回的是 char *]

1
2
3
4
5
string str = "hello world"; 
char p[40];
str.copy(p, 5, 0); // 这里5,代表复制几个字符,0代表复制的位置
*(p+5)='\0'; // 要手动加上结束符!!!
cout << p << endl;

4. char * 、char []转换为 string

4.1 char * 转 string

可以直接赋值

1
2
3
string s;
char *p = "hello world";
s = p;

4.2 char [] 转 string

可以直接赋值

1
2
3
string s;
char ch[] = "hello world";
s = ch;

5. char* 与 char[] 互转

5.1 char[] 转 char*

可以直接赋值

1
2
char ch[] = "hello world";
char *p = ch;

5.2 char * 转 char[]

不能直接赋值,可以循环char*字符串逐个字符赋值,也可以使用strcpy()strcpy_s()等函数。

1
2
3
char *p = "hello world";
char ch[20];
strcpy(ch, p);

总结:char[] 数组转换为其他类型,可以直接赋值。

6. 查找

6.1 find() 函数

主要是查找一个字符串是否在调用的字符串中出现过,大小写敏感。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
using namespace std;
int main(){
string str ("There are two needles in this haystack with needles.");
string str2 ("needle");

// different member versions of find in the same order as above:
//在str当中查找第一个出现的needle,找到则返回出现的位置,否则返回结尾
size_t found = str.find(str2);
if (found!=string::npos)
cout << "first 'needle' found at: " << found << '\n';
//在str当中,从第found+1的位置开始查找参数字符串的前6个字符
found=str.find("needles are small",found+1,6);
if (found!=string::npos)
cout << "second 'needle' found at: " << found << '\n';
//在str当中查找参数中的字符串
found=str.find("haystack");
if (found!=string::npos)
cout << "'haystack' also found at: " << found << '\n';
//查找一个字符
found=str.find('.');
if (found!=string::npos)
cout << "Period found at: " << found << '\n';
//组合使用,把str2用参数表中的字符串代替
// let's replace the first needle:
str.replace(str.find(str2),str2.length(),"preposition");
cout << str << '\n';
return 0;
}

6.2 rfind() 函数

找最后一个出现的匹配字符串,返回的位置仍然是从前往后数的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
using namespace std;

int main() {
string str ("The sixth sick sheik's sixth sheep's sick.");
string key ("sixth");// ^
//rfind是找最后一个出现的匹配字符串
size_t found = str.rfind(key);
if (found!=string::npos) {
cout<<found<<endl;//输出23
str.replace (found,key.length(),"seventh");//找到的sixth替换成seventh
}

cout << str << '\n';
return 0;
}

6.3 find_….of函数

  • find_first_of(args) :查找args中任何一个字符第一次出现的位置
  • find_last_of(args) :最后一个出现的位置
  • find_fist_not_of(args) : 查找第一个不在args中的字符
  • find_last_not_of(args) : 查找最后一个不在args中出现的字符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
using namespace std;

int main() {
string str1 ("Please, replace the vowels in this sentence by asterisks.");
size_t found1 = str1.find_first_of("aeiou");
//把所有元音找出来用*代替
while (found1!=string::npos) {
str1[found1]='*';
found1=str1.find_first_of("aeiou",found1+1);
}
cout << str1 << '\n';

//在str2中找到第一个不是消协英文字母和空格的字符
string str2 ("look for non-alphabetic characters...");
size_t found2 = str2.find_first_not_of("abcdefghijklmnopqrstuvwxyz ");
if (found2!=string::npos) {
cout << "The first non-alphabetic character is " << str2[found2];
cout << " at position " << found2 << '\n';
}
return 0;
}

find_last_of()find_last_not_of()first基本相同,就不写例子代码了。

7. 实例

7.1 查找给定字符串并把相应子串替换为另一给定字符串

string 并没有提供这样的函数,所以我们自己来实现。由于给定字符串可能出现多次,所以需要用到 find() 成员函数的第二个参数,每次查找之后,从找到位置往后继续搜索。直接看代码(这个函数返回替换的次数,如果返回值是 0 说明没有替换):

1
2
3
4
5
6
7
8
9
10
int str_replace(string &str, const string &src, const string &dest) {
int counter = 0;
string::size_type pos = 0;
while ((pos = str.find(src, pos)) != string::npos) {
str.replace(pos, src.size(), dest);
++counter;
pos += dest.size();
}
return counter;
}

7.2 从给定字符串中删除一给定字串

方法和上面相似,内部使用 erase() 完成。代码:

1
2
3
4
5
6
7
8
9
int str_erase(string &str, const string src) {
int counter = 0;
string::size_type pos = 0;
while ((pos = str.find(src, pos)) != string::npos) {
str.erase(pos, src.size());
++counter;
}
return counter;
}

7.3 给定一字符串和一字符集,从字符串剔除字符集中的任意字符

1
2
3
4
5
6
7
8
9
int str_wash(string &str, const string src) {
int counter = 0;
string::size_type pos = 0;
while ((pos = str.find_first_of(src, pos)) != string::npos) {
str.erase(pos, 1);
++counter;
}
return counter;
}

7.4 分割字符串(以逗号分隔符为例,分割得到相应数字)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/*
输入一连串数字,数字之间逗号隔开,把数字存到数组或者向量里。
*/
#include <iostream>
#include <string>
#include <vector>
#include <stdio.h>
using namespace std;

int main() {
string str_input;
cout<<"输入一串以逗号为分隔符的数字字符串:"<<endl;
while(cin >> str_input) {
vector<int> nums;
// string->char *
char *s_input = (char *)str_input.c_str();
const char * split = ",";
// 以逗号为分隔符拆分字符串
char *p = strtok(s_input, split);

int a;
while(p != NULL) {
// char * -> int
sscanf(p, "%d", &a);
nums.push_back(a);
p=strtok(NULL, split);
}

cout<<"输出得到的数字:"<<endl;
for(a = 0; a < nums.size(); a++) {
cout<<nums[a]<<endl;
}
}
return 0;
}

8. 数值转换

在io的部分有过数值和字符串相互转换的例子,使用的是stringstream函数,在c++11当中有定义好的现成的函数取调用,非常方便。

string和数值转换
to_string(val) 把val转换成string
stoi(s,p,b) 把字符串s从p开始转换成b进制的int
stol(s,p,b) long
stoul(s,p,b) unsigned long
stoll(s,p,b) long long
stoull(s,p,b) unsigned long long
stof(s,p) float
stod(s,p) double
stold(s,p) long double

//注意,下段代码在MinGw中会报错!即使使用c++11编译也一样,无法识别to_string!

1
2
3
4
5
6
7
8
9
10
11
12
#include <bits/stdc++.h>
using namespace std;

int main() {
ios::sync_with_stdio(false);
string s1;
s1=to_string(100);
cout<<s1<<endl;
int a=stoi(s1,0,10)+1;
cout<<a<<endl;
return 0;
}

9. Reference