at
at 函数是在C++还没有支持运算符重载的时候提供的。
他可以像 [] 重载运算符一样,找到某个位置的字符:
string s1("hello world");
s1.at(0) = 'x';
cout << s1 << endl;
输出:
[] 重载运算符和 at()的区别就是,在范围处理上不同,如果 访问越界,[] 采用的是断言的方式进行处理的;而 at()采用的是抛出异常。
抛出异常:
断言处理:
那么异常就可以捕获异常,然后对这个异常进行处理,程序可以继续运行;但是断言程序就不能继续执行了。
string类对象的修改操作
函数名称 | 功能说明 |
push_back | 在字符串后尾插字符c |
append | 在字符串后追加一个字符串 |
operator+= (重点) | 在字符串后追加字符串str |
c_str(重点) | 返回C格式字符串 |
find + npos(重点) | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
rfind | 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 |
substr | 在str中从pos位置开始,截取n个字符,然后将其返回 |
+= 重载运算符函数,就是在这个字符串之后再追加字符,或者是字符串。
append()也是在字符串后面追加字符或者是字符串,只不过这个函数有多个接口:
assign()是在给这个字符串赋值,也就是说,会覆盖掉之前在字符串当中存储的字符串或者字符,从0开始依次覆盖了:
insert()支持在某下标位置插入字符或者字符串:
insert()的效率不是很好,所以偶尔用是可以的,频繁用有效率的问题。
erase()函数可以指定个数,指定位置 删除字符串当中的字符:
上述给的 1 这个位置的参数就是 要删除的个数,这个位置的参数是有缺省值的,如果我们不给这个参数,那么就默认把这个位置后面的字符删光。
如果我们给的个数,超过了这个字符串的结尾,那么他也不会报错,会把后面的都删光,能删多少删多少。
上述的 erase()也是尽量少用,他也是有效率的问题。
replace()在某一个区间当中,用传入的字符串替换到原字符串当中:
c_str() 把这个string类的对象当中的字符串以c形式的方式返回:
string s1("hello world");
string filename = "text.txt";
FILE* font = fopen(filename.c_str(), "r");
find()查找函数:
他支持从 pos 位置开始寻找这个字符串或者是字符,这个pos位置是有缺省参数的,不传入pos的话 默认从 0 位置开始找。如果找到了,就返回第一个符合要求的,首字符下标,如果没有找到就返回 npos (-1)。
比如下例子,我们要去把网址的 协议,域名,资源名切分出来:
string url = "https://legacy.cplusplus.com/reference/string/string/find/";
size_t pos1 = url.find("://"); // "://" 字符串之前的就是协议,其中 find 函数找到的是 ':' 这个字符的下标
string protocaol; // 存储域名的string类型的对象
if (pos1 != string::npos) // find 函数如果寻找失败,返回 npos
{
protocaol = url.substr(0, pos1); // 把协议字符串 赋值给 protocaol
}
cout << protocaol << endl;
size_t pos2 = url.find("/", pos1 + 3);
string domain;
string uri;
if (pos2 != string::npos)
{
domain = url.substr(pos1 + 3, pos2 - (pos1 + 3));
}
cout << domain << endl;
uri = url.substr(pos2 + 1);
cout << uri << endl;
输出:
rfind()函数,这个函数的功能和 find ()的功能完全是类似的,只不过find()是从前往后找,而find()是从后往前找。
find_first_of()函数:
传入一个字符串,或者是一个字符,从pos位置开始寻找(pos 缺省值 = 0)和传入的字符或者是字符字符串当中的任意一个字符相等的字符(第一个),找到之后返回这个字符的下标,如下在文档中给出的例子:
要求是把 字符串当中 "a" "e" "i" "o" "u" 的字符,全部改为 "*" :
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';
输出:
Pl**s*, r*pl*c* th* v*w*ls *n th*s s*nt*nc* by *st*r*sks.
上述是从前往后找的函数,还有一个函数和上述函数的功能是一样的,find_last_of()函数。
当然,还有查找 不是传入的 字符串和 字符的函数:find_first_not_of 和 find_last_not_of ;
注意:
- 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
- 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。
string类非成员函数
函数 | 功能说明 |
operator+ | 尽量少用,因为传值返回,导致深拷贝效率低 |
operator>> (重点) | 输入运算符重载 |
operator<< (重点) | 输出运算符重载 |
getline (重点) | 获取一行字符串 |
relational operators (重点) | 大小比较 |
字符串比较的重载运算符函数:
relational operators
上述就是对应的接口,其实上述实现的接口,有限冗余,上述一个 重载运算符函数 重载了三个形参不同的函数,分别是 字符串类型 和 string 类 参数类型 变化,其实不用实现这么多,因为 如果是 string& 参数 传入 char* 类型的参数的时候,char* 在传参的时候可以隐式类型转换 为 string& 。
getline()函数和 C当中的 gets 有点像,我们在C++当中使用的 cin 输入流,和 C 当中的scanf()函数一样,当读到 空格 '\n'就会结束读入,那么我们上述cin 就不能读取字符串,所以我们就是用 getline ()来读入字符串。
例子,返回一个字符串当中,最后一个单词的 长度:
string str;
getline(cin, str); // 读取字符串
size_t pos = str.rfind(' ');
if (pos != string::npos)
{
cout << str.size() - (pos + 1) << endl;
}
else
{
cout << str.size() << endl;
}
getline 只 遇到 '\n' 才结束。
to_string 和 stoi
to_string 可以把 内置类型 转换成string类型,这个函数返回的就是 string类型:
string s1 = to_string(1234);
string s2 = to_string(1.1);
cout << s1 << endl; //1234
cout << s2 << endl; //1.100000
如果是在C当中我们使用的是 itoa()函数把 int 类型转换成 字符串类型,但是这个函数不好用在于,接收的字符串类型,需要我们手动开空间。
如果是在 C 当中我们使用的是 atoi()来把 字符串类型转换成 int 类型:
wstring , u16string ,u32string
其实string是一个模板 typedef 出来的string。只不过这里面传入的是 char 类型:
其实本源的类其实是 basic_string 这个类型,所以我们看到 关于string类的报错的时候,会看到 basic_string 字样。
之所以这样做是因为,还有其他的string类型wstring , u16string ,u32string。
上述的本源也都是 basic_string。
上述三种类型其实不经常用,之所以把他区分开来是因为编码问题。
我们现在经常使用的 编码格式 是 ascll 表的,就是 底层存储的值,对应的映射关系表。在计算机当中存储的值是二进制的,比如在 ascll 当中,一共就 200 多个,那么 8 个bite 位就可以存储了。存储的不同的二进制的值就代表着不同的字符,比如 0110 0000 对应的十进制是 97,那么这个二进制代表的就是 a 这个字符。