71.C++标准库类型string

71.标准库类型string

之所以抛弃char*的字符串而选用C++标准程序库中的string类,是因为他和前者比较起来,不必担心内存是否足够、字符串长度等等,而且作为一个泛型类出现,他集成的操作函数足以完成我们大多数情况下(甚至是100%)的需要。我们可以用 = 进行赋值操作,== 进行比较,+ 做串联(是不是很简单?)。我们尽可以把它看成是C++的基本数据类型。

C++中对于string的定义为:

typedef basic_string string; 

也就是说C++中的string类是一个泛型类,由模板而实例化的一个标准类,本质上不是一个标准数据类型。

1.构造一个string

要想使用标准C++中string类,必须要包含

#include <string>
using  std::string;
using  std::wstring;
或
using namespace std;

string/wstring了,它们两分别对应着char和wchar_t。

1.1初始化一个string对象

指令 解释
string s1 默认初始化,s1是一个空串
string s2(s1) s2 是 s1 的副本
string s2=s1 等价于 s2(s1),s2 是 s1 的副本
string s3(“value”) s3 是字面值"value"的副本,除了字面值最后的那个空字符除外
string s3=“value” 等价于 s3(“value”),s3 是字面值"value"的副本
string s4(n, ‘c’) 把 s4 初始化为由连续 n 个字符 c 组成的
string s5(cp, n) s5 是 cp 指向的数组中前 n 个字符的拷贝。此数组至少应包含 n 个字符
string s5(s1, pos1) s5是string s1从下标 pos1 开始的字符的拷贝。若pos1>s1.size(),构造函数的行为未定义
string s5(s1, pos1, len1) s5 是string s1从下标 pos1 开始进行 len1 个字符的拷贝。若pos1>s1.size(),构造函数的行为未定义。不管len1的值是多少,构造函数至多拷贝s1.size()-pos1个字符。
s.substr(pos, n) 返回一个string,包含 s 中从pos开始的 n 个字符的拷贝。pos 的默认值为0。n的默认值为s.szie()-pos,即拷贝从pos开始的所有字符。

(1)直接初始化和拷贝初始化

如果使用等号(=)初始化一个变量,实际上执行的是拷贝初始化(copy initialization),编译器把等号右侧的初始值拷贝到新创建的对象中去。与之相反,如果不使用等号,则执行的是直接初始化 (direct initialization)。

当初始值只有一个时,使用直接初始化或拷贝初始化都行。如果像上面的s4那样初始化要用到的值有多个,一般来说只能使用直接初始化的方式:

string s5 = "hiya";//拷贝初始化
string s6("hiya");//直接初始化
string s7(10, 'c');//直接初始化,s7的内容是cccccccccc

(2)string类的构造函数

构造函数 描述
string(const char* s) 将string对象初始化为s指向的NBTS
string(size_type n, char c) 创建一个包含n个元素的string对象,其中每个元素都被初始化为字符串c
string(const string &str) 将一个string对象初始化为string对象str(复制构造函数)
string() 创建一个默认的string对象,长度为0(默认构造函数)
string(const char *s, size_type n) 将string对象初始化为s指向的NBTS的前n个字符,即使超过了NBTS结尾
template<class Iter>string(Iter begin, Iter end) string(Iter begin, Iter end)将一个string对象初始化为对象str中从位置pos开始到结尾的字符,或从位置pos开始的n个字符
string(const string &str, string size_type pos = 0, size_type n = npos) 将一个string对象初始化为对象str中从位置pos开始到结尾的字符,或从位置pos开始的n个字符
string (string &&str)noexcept 这是C++11新增的,它将一个string对象初始化为string对象str,并可能修改str(这是移动构造函数)
string(initializer_list<char> il) 这是C++11新增的,它将一个string对象初始化为初始化列表il中的字符

例子:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

int main()
{
	string one("Lottery Winner!");//调用string(const char* s)
	cout << one << endl;//重载<< 

	string two(20, 'S');//ctor #2  调用string(size_type n, char c)
	cout << two << endl;

	string three(one);//ctor #3 调用string(const string &str)
	cout << three << endl;
	one += " Oops!";//冲咋子+=
	cout << one << endl;
	two = "Sorry! That was ";
	three[0] = 'P';

	string four;//ctor #4 调用string()
	four = two + three;//重载+,=
	cout << four << endl;
	char alls[] = "All's well that ends well";

	string five(alls, 20);//ctor #5  调用string(const char *s, size_type n)
	cout << five << "!\n";

	string six(alls + 6, alls + 10);//ctor #6  调用template<class Iter>string(Iter begin, Iter end)
	cout << six << ", ";

	string seven(&five[6], &five[10]);//ctor #6  调用template<class Iter>string(Iter begin, Iter end)
	cout << seven << "...\n";

	string eight(four, 7, 16);//ctor #7  调用string(const string &str, string size_type pos = 0, size_type n = npos)
	cout << eight << " in motion!" << endl;

	string piano_man = { 'L', 'i', 's', 'z', 't' };
	string comp_lang = { 'L', 'i', 's', 'p'};//ctor #9  调用string(initializer_list<char> il)

	system("pause");
	return EXIT_SUCCESS;
}

C++11新增的构造函数

构造函数string (string &&str)类似于拷贝构造函数,导致新创建的string为str的副本。但与拷贝构造函数不同的是,它不保证将str视为const。这种构造函数被称为移动构造函数。在有些情况下,编译器可使用它而不是复制构造函数,以优化性能。

构造函数string(initializer_list<char> il)让您能够将列表初始化语法用于string类。也就是说,它使得下面这样的声明是合法的。

string piano_man = { 'L', 'i', 's', 'z', 't' };
string comp_lang = { 'L', 'i', 's', 'p'};//ctor #9  调用string(initializer_list<char> il)

2.容量操作

函数名称 功能说明
size_t size() const 返回字符串有效字符长度
size_t length() const 返回字符串有效字符长度
size_t capacity() const 返回空间总大小
bool empty() const 检测字符串释放为空串,是返回true,否则返回false
void clear() 清空有效字符
void resize( size_t n, char c ) 将有效字符的个数该成n个,多出的空间用字符c填充
void resize ( size_t n ) 将有效字符的个数改成n个,多出的空间用0填充
void reserve ( size_t res_arg=0 ) 为字符串预留空间
  • reserve()一般和插入、operator+=配合
    它只是扩充容量,size仍为原大小。
  • resize()一般operator[]配合
    它既扩容,又改变了size值,这样就可以通过[]访问。

实际上reserve()resize()都是为了在已知数据的大概使用空间时,避免增容造成的额外开销,提高效率

例子1:

// size/length/clear/resize
void TestString1() 
{
	// 注意:string类对象支持直接用cin和cout进行输入和输出
	string s("giturtle");
	cout << s.length() << endl;
	cout << s.size() << endl;
	cout << s.capacity() << endl;
	cout << s << endl;

	// 将s中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小
	s.clear();
	cout << s.size() << endl;
	cout << s.capacity() << endl;
	// 将s中有效字符个数增加到10个,多出位置用'a'进行填充
	// “aaaaaaaaaa”
	s.resize(10, 'a');
	cout << s.size() << endl;
	cout << s.capacity() << endl;
	// 将s中有效字符个数增加到15个,多出位置用缺省值'\0'进行填充
	// "aaaaaaaaaa\0\0\0\0\0"
	// 注意此时s中有效字符个数已经增加到15个
	s.resize(15);
	cout << s.size() << endl;
	cout << s.capacity() << endl;
	cout << s << endl;
	// 将s中有效字符个数缩小到5个
	s.resize(5);
	cout << s.size() << endl;
	cout << s.capacity() << endl;
	cout << s << endl;
}

输出:

8
8
15
giturtle
0
15
10
15
15
15
aaaaaaaaaa
5
15
aaaaa
请按任意键继续. . .

例子2:

void TestString2()
{
	string s;
	// 测试reserve是否会改变string中有效元素个数
	s.reserve(100);
	cout << s.size() << endl;
	cout << s.capacity() << endl;
	// 测试reserve参数小于string的底层空间大小时,是否会将空间缩小
	s.reserve(50);
	cout << s.size() << endl;
	cout << s.capacity() << endl;
}

输出:

0
111
0
111

3.修改操作

函数名称 功能说明
void push_back(char c) 在字符串后尾插字符c
string& append (const char* s) 在字符串后追加一个字符串
string& operator+=(const string& str) 在字符串后追加字符串str
string& operator+=(const char* s) 在字符串后追加C个数字符串
string& operator+=(char c) 在字符串后追加字符c
const char* c_str() const 返回C格式字符串
size_t find (char c, size_t pos = 0)const 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
size_t rfind(char c, size_t pos = npos) 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
string substr(size_t pos = 0, size_t n = npos)const 在str中从pos位置开始,截取n个字符,然后将其返回

例子:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

void TestString()
{
	string str;
	str.push_back(' '); // 在str后插入空格
	str.append("hello"); // 在str后追加一个字符"hello"
	str += 'b'; // 在str后追加一个字符'b'
	str += "it"; // 在str后追加一个字符串"it"
	char a = 'a';
	str += a;
	cout << str << endl;
	cout << str.c_str() << endl; // 以C语言的方式打印字符串

	// 获取file的后缀
	string file1("string.cpp");
	size_t pos = file1.rfind('.');
	string suffix(file1.substr(pos, file1.size() - pos));
	cout << suffix << endl;

	// npos是string里面的一个静态成员变量
	// static const size_t npos = -1;
	// 取出url中的域名
	string url("http://www.cplusplus.com/reference/string/string/find/");
	cout << url << endl;
	size_t start = url.find("://");
	if (start == string::npos) //npos 是一个常数,用来表示不存在的位置
	{
		cout << "invalid url" << endl;
		return;
	}
	start += 3;
	size_t finish = url.find('/', start);
	string address = url.substr(start, finish - start);
	cout << address << endl;

	// 删除url的协议前缀
	pos = url.find("://");
	url.erase(0, pos + 3);
	cout << url << endl;
}

// 利用reserve提高插入数据的效率,避免增容带来的开销
//====================================================================================
void TestPushBack() 
{
	string s;
	size_t sz = s.capacity();
	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';
		}
	}
}

void TestPushBack_P() 
{
	string s;
	s.reserve(100);
	size_t sz = s.capacity();

	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';
		}
	}
}

int main()
{
	//TestString();
	//TestPushBack();
	TestPushBack_P();
	system("pause");
	return EXIT_SUCCESS;
}

小结

1.在string尾部追加字符时,s.push_back( c ) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
2.对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。

3.1读写string对象

可以使用IO操作符读写string对象:

//注意:要想编译下面的代码还需要适当的#include语句和using声明
int main()
{
    string s;//空字符串
    cin >> s;//将string对象读入s,遇到空白停止
    cout << s << endl; //输出s
    return 0; 
}

  这段程序首先定义一个名为s的空string,然后将标准输入的内容读取到s中。在执行读取操作时,string对象会自动忽略开头的空白(即空格符、换行符、制表符等)并从第一个真正的字符开始读起1,直到遇见下一处空白为止。
  如上所述,如果程序的输入是“ Hello World! " (注意开头和结尾处的空格),则输出将是“Hello",输出结果中没有任何空格。和内置类型的输入输出操作一样,string对象的此类操作也是返回运算符左侧的运算对象作为其结果。因此,多个输入或者多个输出可以连写在一起:

string s1, s2; 
cin >> s1 >> s2; //把笫一个输入读到s1中, 笫二个输入读到s2中
cout << s1 << s2 << endl; //输出两个string对象

  假设给上面这段程序输入与之前一样的内容“ Hello World! ", 输出将是"HelloWorld!"。

3.2读取未知数量的string对象

int main()
{
    string word; 
    while(cin >> word)//反复读取,直至到达文件末尾
        cout << word<< endl;//逐个输出单词, 每个单词后面紧跟一个换行 
    return 0; 
}

  如果流有效,也就是说没遇到文件结束标记或非法输入,那么执行while语句内部的操作。

3.3使用getline读取一整行

  有时我们希望能在最终得到的字符串中保留输入时的空白符,这时应该用getline函数代替原来的>>运算符。 getline函数的参数是一个输入流和一个string对象,函数从给定的输入流中读入内容,直到遇到换行符为止(注意换行符也被读进来了),然后把所读的内容存入到那个string对象中去(注意不存换行符)。getline只要一遇到换行符就结束读取操作并返回结果, 哪怕输入的一开始就是换行符也是如此。 如果输入真的一开始就是换行符,那么所得的结果是个空string。
  和输入运算符一样,getline也会返回它的流参数。 因此既然输入运算符能作为判断的条件(参见C++Primer1.4.3节, 第13页),我们也能用getline的结果作为条件。例如,可以通过改写之前的程序让它一次性输入整行,而不再是每行输出一个词了:

int main() 
{
    string line; 
    //每次读入一整行, 直至到达文件末尾
    while (getline (cin, line)) 
        cout << line << endl; 
    return 0; 
}

  因为line中不包含换行符, 所以我们手动地加上换行操作符。和往常一样, 使用endl结束当前行并刷新显示缓冲区。

3.4比较string对象

  string类定义了几种用于比较字符串的运算符。这些比较运算符逐—比较string对象中的字符,并且对大小写敏感,也就是说,在比较时同一个字母的大写形式和小写形式是不同的。

●1.如果两个string对象的长度不同,而且较短string对象的每个字符都与较长string对象对应位置上的字符相同,就说较短string对象小于较长string 对象。

●2.如果两个string对象在某些对应的位置上不一致,则string对象比较的结果其实是string对象中第一对相异字符比较的结果。

compare 函数

标准库 String 类型还提供了 compare 函数,与 C 中的 strcmp 函数很相似。

形式:s.compare(args);

函数()内的参数 解释
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 指向的地址开始的 n2 个字符进行比较

3.5为对象赋值

  一般来说,在设计标准库类型时都力求在易用性上向内置类型看齐,因此大多数库类型都支持赋值操作。对于string类而言,允许把个对象:

string st1(10, 'c'), st2;//st1的内容是cccccccccc;st2是一个空字符串
st1 = st2; 

3.6两个对象相加

  两个string对象相加得到一个新的string对象,其内容是把左侧的运算对象与右侧的运算对象串接而成。也就是说,对string对象使用加法运算符(+)的结果是一个新的string对象,它所包含的字符由两部分组成:前半部分是加号左侧string对象所含的字符、后半部分是加号右侧string对象所含的字符。另外,复合赋值运算符(+=) (参见C++Primer1.4.1节,第10页)负责把右侧string对象的内容追加到左侧string对象的后面:

string s1 = "hello, ", s2 = "world\n";
strings3 = s1 + s2;//s3的内容是hello,world\n
s1 += s2;//等价于s1= s1 + s2 

3.7字面值和string对象相加

  因为标准库允许把字符字面值和字符串字面值 (参见2.1.3节,第36页)转换成string对象,所以在需要string对象的地方就可以使用这两种字面值来替代。利用这点将之前的程序改写为如下形式:

string s1 = "hello", s2 = "world";//在s1和s2中都没有标点符号 
string s3 = s1 + ", " + s2 +'\n'; 

  当把string对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符(+)的两侧的运算对象至少有一个是string:

string s4 = s1 + ", ";//正确:把一个string对象和一个字面值相加
string s5 = "hello" + ", ";//错误: 两个运算对象都不是string 
//正确:每个加法运算符都有一个运算对象是string
string s6 = s1 + ", " + "world"; 
string s7 = "hello" +", "+ s2;//错误:不能把宇面值直接相加

  s6的初始化形式之前没有出现过,但其实它的工作机理和连续输入连续输出(参见C++Primer1.2节, 第6页)是一样的, 可以用如下的形式分组:

string s6 = (s1 + ", ") + "world"; 

  其中子表达式s1 + “ ,"的结果是一个string对象, 它同时作为第二个加法运算符的左侧运算对象,因此上述语句和下面的两个语句是等价的:

string tmp = s1 + ", ";//正确:加法运算符有一个运算对象是
string s6 = tmp + "world";//正确:加法运算符有一个运算对象是string

因为某些历史原因,也为了与C兼容,所以C++语言中的字符串字面值并不是标准库类型string的对象。切记,字符串字面值与string是不同的类型。

3.8string 对象上的操作

指令 解释
os<< s 将 s 写到输出流os当中,返回os (cout<< s)
is>> s 从 is 中读取字符串赋值给 s,字符串以空白分隔,返回 is 例如(cin>> s)
getline(is, s) 从is中读取一行赋给 s,返回 is 例如getline(cin, s)
s.empty() s 为空返回 true,否则返回 false
s.size() 返回 s 中字符的个数
s[n] 返回 s 中第 n 个字符的引用,位置 n 从 0 计起
s1+s2 返回 s1 和 s2 连接后的结果
s1=s2 用 s2 的副本代替 s1 中原来的字符
s1==s2 如果 s1 和 s2 中所含的字符完全一样,则他们相等;
s1!=s2 (接上)string 对象的相等性判断对字母大小写敏感
<, <=, >, >= 利用字符在字典中的顺序进行比较,大小写敏感

ps :cin 输入遇到空格停止,有时希望在最终得到的字符串中保留空格,这时应该用 getline 函数代替。getline 从输入流中读取内容,知道遇到换行符为止(注意换行符也被读进来了),然后把所读内容存入 string 对象中(注意不存换行符)。
ps2:对于 size 函数来说,返回的是一个 string::size_type 类型的值。这是一个无符号类型的值,而且能足够存放下任何 string 对象的大小。因此在c++11新标准中,最好通过auto来推断类型变量,比如:

auto len = line.size();  // len的类型是 string::size_type

  由于size函数返回的是一个无符号整形数,因此切记,如果在表达式中混用了带符号数和无符号数可能产生意想不到的效果。例如,假设 n 是一个具有负值的 int,则表达式 s.size()<n 的判断结果几乎肯定是 true。这是因为负值 n 会自动地转换成一个比较大的无符号值。

ps3:字面值和 string 对象相加,当把 string 对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加号(+)的两侧的运算对象至少有一个是 string。因为某些历史原因,也为了与C兼容,所以C++语言中的字符串字面值并不是标准库类型 string 对象。切记,字符串字面值与 string 是不同的类型。

3.9处理 string 对象中的字符

指令 解释
isalnum(c) c为字母或数字时为真
isalpha(c) c为字母时为真
iscntrl(c) c为控制字符时为真
isdigit(c) c为数字时为真
isgraph(c) c为不是空格但可以打印时为真
islower(c) c为小写字母时为真
isprint(c) c为可打印字符时为真(即空格或具有可视形式 )
ispunct(c) c为标点符号时为真(即c不是控制字符、数字、字母、可打印空白中的一种)
isspace(c) c为空白时为真(即空格、横向制表符、纵向制表符、回车符、换行符、进纸符中的一种)
issupper(c) c为大写字母时为真
isxdigit(c) c为十六进制数字时为真
tolower(c) 如果c是大写字母,输出对应的小写字母;否则原样输出
toupper(c) 如果c是小写字母,输出对应的大写字母;否则原样输出

当处理每个字符的时候,可以使用基于范围的for 语句,其语法形式是:

for(declaration: expression)
	statement;

  其中,expression是一个对象,用于表示一个序列;declaration部分定义一个变量,该变量将被用于访问序列中的基础元素。每次迭代,declaration部分的变量会被初始化为expression部分的下一个元素值。

例子1:

string str("some string")
for (auto c : str)
    cout << c << endl;

例子2:使用范围for语句和ispunct函数来统计string对象中标点符号的个数:

string s("Hello World!!!"); 
// punct_cnt的类型和s.size的返回类型一样;参见C++Primer2.5.3节(笫62页) 
decltype(s.size()) punct_cnt = 0; //统计s中标点符号的数量
for (auto c : s) 
    if (ispunct (c)) 
        ++punct_cnt; 
cout << punct_cnt
     << " punctuation characters in "<< s << endl; 

输出:

3 punctuation characters in Hello World!!! 

1.使用范围for语句改变字符串中的字符

  如果想要改变string对象中字符的值,必须把 循环变显定义成引用 类型(参见2.3.l 节, 第45页)。

string s("Hello World'!!"); 
//转换成大写形式
for (auto &c : s)//对于s中的每个字符(注意: C是引用)
    c = toupper(c);//C是一个引用, 因此赋值语句将改变s中字符的值
cout << s << endl; 

输出:

HELLO WORLD ! ! ! 

2.只处理一部分字符?

  要想访问string对象中的单个字符有两种方式: 一种是使用下标, 另外一种是使用迭代器,其中关于迭代器的内容将在C++Primer3.4节(第95页)和第9章中介绍。

  下标运算符([])接收的输入参数是string::size_type类型的值(参见3.2.2 节, 第79页), 这个参数表示要访间的字符的位置;返回值是该位置上字符的引用。

3.使用下标执行迭代

  另一个例子是把s的第一个词改成大写形式:

//依次处理s中的字符直至我们处理完全部字符或者遇到一个空白
for(decltype(s.size()) index= 0; index != s.size() && !isspace(s[index]); ++index) 
    s[index] = toupper(s[index]);//将当前字符改成大写形式

输出:

SOME string 

注意:C++标准并不要求标准库检测下标是否合法。一旦使用了一个超出范围的下 标,就会产生不可预知的结果。

4.使用下标执行随机访问

  例如,想要编写一个程序把0到15之间的十进制数转换成对应的十六进制形式,只需初始化一个字符串令其存放16个十六进制“数字”:

const string hexdigits= "0123456789ABCDEF";//可能的十六进制数字
cout << "Enter a series of numbers between 0 and 15" 
     <<" separated by spaces. Hit ENTER when finished: " 
     << endl; 
string result;//用于保存十六进制的字符串
string::size_type n;//用于保存从输入流读取的数
while (cin >> n) 
    if (n < hexdigits.size()) //忽略无效输入
        result += hexdigits[n];//得到对应的十六进制数字
cout << "Your hex number is: " << result << endl; 

假设输入的内容如下:

12 0 5 15 8 15 

程序的输出结果将是:

Your hex number is: C05F8F 

5.使用迭代器

看下面的例子:

void TestString() 
{
	string s = "Hello world!";
	for (auto i = s.begin(); i != s.end(); i++)
	{
		cout << *i << ",";
	}
	cout << endl;
}

输出:

H,e,l,l,o, ,w,o,r,l,d,!,

3.10改变 string 的其他方法

表9.13: 修改string的操作
s.insert(pos, args) 在pos之前插入args指定的字符。pos可以是一个下标或一个迭代器。接受下标的版本返回一个指向s的引用,接受迭代器的版本返回指向第一个插入字符的迭代器
s.erase(pos, len) 删除从位置pos开始的len个字符。如果len被省略,则删除从pos开始直至末尾的所有字符。返回一个指向s的引用
s.assign(args) 将 s 中的字符替换为 args 制定的字符。返回一个指向 s 的引用
s.append(args) 将args追加到s。返回一个指向s的引用
s.replace(range, args) 删除s中范围range内的字符,替换为args指定的字符。range是一个下标和一个长度,或者是一对指向s的迭代器。返回一个指向s的引用。
args可以是下列形式之一;append和assign可以使用所有形式。
str不能与s相同,迭代器b和e不能指向s。
str 字符串str
str,pos,len str中从pos开始最多len个字
cp,len 从cp指向的字符数组的前(最多)len个字符
cp cp指向的以空字符结尾的字符数组
n,c n个字符c
初始化列表 花括号包围的,以逗号分隔的字符列表
b,e 迭代器b和e指定的范围内的字符

3.11string 搜索操作

指令 解释
s.find(args) 查找 s 中 args 第一次出现的位置
s.rfind(args) 查找 s 中 args 最后一次出现的位置
s.find_first_of(args) 在 s 中查找 args 中任何一个字符第一次出现的位置
s.find_last_of(args) 在 s 中查找 args 中任何一个字符最后一次出现的位置
s.find_first_not_of(args) 在 s 中查找第一个不在 args 中的字符
s.find_last_not_of(args) 在 s 中查找最后一个不在 args 中的字符
上表中 ( ) 内的参数 解释
c, pos 从 s 中位置 pos 开始查找字符 c。pos 默认 0
s2, pos 从 s 中位置 pos 开始查找字符串 s2。pos 默认 0
cp, pos 从 s 中位置 pos 开始查找指针 cp 指向的以空字符结尾的 C 风格字符串。pos 默认 0
cp, pos ,n 从 s 中位置 pos 开始查找指针 cp 指向的数组的前 n 个字符。pos 和 n 无默认值

ps:每个搜索操作都返回一个 string::size_type 值,表示匹配发生位置的下标。如果搜索失败,则返回一个名为 string::npos 的 static 成员。由于 npos 是一个 unsigned 类型,此初始值意味着 npos 等于任何 string 最大的可能大小。

指定在哪里开始搜索

string::size_type pos = 0; 
//每步循环查找name中下一个数
while ((pos = name.find_first_of(numbers, pos)) != string::npos)
{ 
    cout << "found number at index: "<< pos << " element is " << name[pos] << endl; 
    ++pos;//移动到下一个字符 
}

查找name中出现数字的位置

3.12数值转换

指令 解释
to_string(val) 一组重载函数,返回数值 val 的 string 表示。val 可以是任何算数类型。
stoi(s, p, b) 返回 s 的起始子串(表示整数内容)的数值,返回类型为 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) 返回 s 的起始子串(表示浮点数内容)的数值,返回值类型为 float
stod(s, p) 返回类型为 double
stold(s, p) 返回类型为 long double

ps:p: 是 size_t 指针,用来保存 s 中第一个非数值字符的下标,p 默认为 0 ,即,函数不保存下标。

b: 是转换所用的基数,默认值为10。

3.13迭代器

成员 返回值
begin 返回指向字符串第一个元素的指针
end 返回指向字符串最后元素下一个位置的指针
rbegin 返回字符串最后一个元素的指针
rend 返回字符串第一个元素前一个位置的指针
cbegin 同begin,只读
cend 同end,只读
crbegin 同rbegin,只读
crend 同rend,只读

ps: 反向迭代器执行++操作,也是在反着遍历,比如rbegin指向字符串最后一个字符,rbegin++,此时就指向倒数第二个字符。

参考文章链接:https://blog.csdn.net/wanttifa/article/details/81333458

4.访问操作

函数名称 功能说明
char& operator[] ( size_t pos ) 返回pos位置的字符,const string类对象调用
const char& operator[] ( size_t pos )const 返回pos位置的字符,非const string类对象调用
obj.at(i) 实质上是一个函数

operator[].at()的区别在于:

  • at: 出现错误会抛出异常。如果出现越界,会抛异常处理。
  • []:出现错误命中断言。如果下标i访问的不是在合理的size范围中(比如越界),会报错。

例子:

void TestString()
{
	String s1("giturtle");
	const String s2("giturtle");
	cout << s1 << " " << s2 << endl;
	cout << s1[0] << " " << s2[0] << endl;

	s1[0] = 'H';
	cout << s1 << endl;

	for (size_t i = 0; i < s1.size(); ++i){
		cout << s1[i] << endl;
	}

	// s2[0] = 'h'; 代码编译失败,因为const类型对象不能修改
}

输出:

giturtle giturtle
g g
Hiturtle
H
i
t
u
r
t
l
e

5.string类非成员函数

函数 功能说明
operator+ 尽量少用,因为效率低
operator>> 输入运算符重载
operator<< 输出运算符重载
getline 获取一行字符串
关系运算符>< 大小比较

参考:(10条消息) C++ 之 String类详解_c++ string_giturtle的博客-CSDN博客

posted @ 2023-03-22 22:59  CodeMagicianT  阅读(92)  评论(0编辑  收藏  举报