C++知识点 – string类
文章目录
一、成员函数
1.构造函数
(1)是无参的构造函数,
(2)是拷贝构造,
(3)是部份拷贝构造 ,
(4)是带参数的构造函数,
(5)是用s的前几个字符构造,
(6)是用n个指定字符c去构造;
代码如下:
void test__string1()
{
string s1;//无参
string s2("hello");//有参
string s3(s2);//拷贝构造
string s4(s2, 1, 3);//部分拷贝,pos是开始拷贝的位置,len是拷贝的字符长度
string s5("hello world", 5);
string s6(100, 'x');
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
cout << s4 << endl;
cout << s5 << endl;
cout << s6 << endl;
//string类中重载了 << 和 >> 运算符
}
结果为:
2.赋值重载
(1)可以重载其他string对象,
(2)可以重载字符串,
(3)可以重载字符;
代码如下:
void test__string2()
{
string s1;
string s2("hello world");
s1 = s2;
cout << s1 << endl;
s1 = "stay";
cout << s1 << endl;
s1 = 'c';
cout << s1 << endl;
}
运行结果:
二、元素访问
1.[]重载
能够允许我们以数组的形式访问string类的字符串;
普通字符串调用普通的[]重载,cosnt字符串调用const的[]重载;
这里重载[]是传引用返回,不光可以读,还可以进行修改;
代码如下:
void test__string3()
{
string s1("hello world");
cout << s1[0] << endl;
s1[0] = 'a';
cout << s1 << endl;
for (int i = 0; i < s1.size(); i++)
{
s1[i]++;
}
cout << s1 << endl;
const string s2("hello world");
cout << s2[1] << endl;
//s2[1] = 'a';
//const string对象是不能修改的
}
运行结果:
三、迭代器
用法类似指针;
1.迭代器iterator
begin:返回一个指向字符开始位置的迭代器;
end:返回一个指向字符结束位置后一个位置(‘\0’)的迭代器;
所以迭代区间都是左闭右开的[);
迭代器中不能用>,只能用!= ,因为list等对象空间就不是连续的了
代码如下:
void test__string4()
{
string s("hello");
string::iterator it = s.begin();
while (it != s.end())//迭代器中不能用>,只能用!=。因为list等对象空间就不是连续的了
{
cout << *it << ' ';
it++;
}
cout << endl;
}
2.范围for
范围for – 自动迭代,自动判断结束;
代码如下:
void test__string4()
{
string s("hello");
for (auto ch : s)
{
cout << ch << ' ';
}
cout << endl;
}
依次取s中的每一个字符,赋值给ch;
范围for的底层就是迭代器;
void test__string4()
{
string s("hello");
for (auto& ch : s)
{
ch++;
cout << ch << ' ';
}
cout << endl;
}
范围for要修改一定要改成引用,引用ch就成了*it的别名,
3.反向迭代器
代码如下:
void test__string4()
{
string s("hello");
string::reverse_iterator rit = s.rbegin();
while (rit != s.rend())
{
cout << *rit << ' ';
rit++;
}
cout << endl;
}
4.const迭代器
针对于const string对象的迭代器;
代码如下:
void test__string4()
{
const string s1("hello");
string::const_iterator cit = s1.begin();
while (cit != s1.end())
{
cout << *cit << ' ';
cit++;
}
cout << endl;
}
5.反向const迭代器
代码如下:
void test__string4()
{
const string s1("hello");
//string::const_reverse_iterator crit = s1.rbegin();
auto crit = s1.rbegin();
while (crit != s1.rend())
{
cout << *cit << ' ';
crit++;
}
cout << endl;
}
四、修改
1.push_back、append、+=
push_back:在字符串后追加一个字符;
append:在字符串后追加一个字符串;
+=:在字符串后追加一个字符串;
代码如下:
void test__string5()
{
string s("hello");
s.push_back('-');
s.push_back('-');
s.append("world");
cout << s << endl;
string s1("hello");
s1 += '-';
s1 += '!';
s1 += "world";
cout << s1 << endl;
}
2.insert、erase
在指定地址插入字符串;
删除指定长度的字符串;
例:在字符的空格处插入20%
代码如下:
void test__string7()
{
string s("ni shi shei");
for (size_t i = 0; i < s.size(); i++)
{
if (s[i] == ' ')
{
s.insert(i, "20%");
i += 3;
}
}
for (size_t i = 0; i < s.size(); i++)
{
if (s[i] == ' ')
{
s.erase(i, 1);
}
}
cout << s << endl;
}
使用insert效率较低,因为insert的底层是挪动数组;
用空间换时间的解决方案:
void test__string7()
{
string s("ni shi shei");
string newstr;
for (size_t i = 0; i < s.size(); i++)
{
if (s[i] != ' ')
{
newstr += s[i];
}
else
{
i++;
}
}
cout << newstr << endl;
}
五、容量
1.max _size 和 capacity
代码如下:
void test__string6()
{
string s1;
string s2("1111111111");
cout << s1.max_size() << endl;
cout << s2.max_size() << endl;
cout << s1.capacity() << endl;
cout << s2.capacity() << endl;
}
六、字符串应用
1.c_str
返回c形式的字符串;
代码如下:
void test__string8()
{
//用c形式读文件
string filename("test.cpp");
FILE* fp = fopen(filename.c_str(), "r");
assert(fp);
char ch = fgetc(fp);
while (ch != EOF)
{
cout << ch;
ch = fgetc(fp);
}
fclose(fp);
fp == nullptr;
}
void test__string8()
{
string filename("test.cpp");
cout << filename << endl;
cout << filename.c_str() << endl;
filename += '\0';
filename += "test.cpp";
cout << filename << endl;//以size为准
cout << filename.c_str() << endl;//以\0为准
}
string的字符串长度是以size为准的,而c_str的字符串长度是以\0为准的;
2.find、rfind、substr
返回要寻找的字符串的下标, 若没有找到,返回npos
返回一部分字符串构成子串;
从字符串尾部向前寻找;
例:取文件后缀名,代码如下:
void test__string9()
{
string filename("test.cpp");
size_t pos = filename.find('.');
if (pos != string::npos)
{
string suff = filename.substr(pos, filename.size() - pos);
cout << suff << endl;
}
}
倒着找:
void test__string9()
{
string filename("test.cpp.tar.zip");
size_t pos = filename.rfind('.');
if (pos != string::npos)
{
string suff = filename.substr(pos, filename.size() - pos);
cout << suff << endl;
}
}
分割url:
void DealUrl(const string& url)
{
//取协议
size_t pos1 = url.find("://");
if (pos1 == string::npos)
{
cout << "非法url" << endl;
return;
}
string protocol = url.substr(0, pos1);
cout << protocol << endl;
//取域名
size_t pos2 = url.find("/", pos1 + 3);
if (pos2 == string::npos)
{
cout << "非法url" << endl;
return;
}
string domain = url.substr(pos1+3, pos2 - pos1 -3);
cout << domain << endl;
//取uri
string uri = url.substr(pos2 + 1);
cout << uri << endl;
}
七、类型转换
字符串转换为其他类型;
其他类型转换为字符串;
八、非成员函数重载
1.getline
从流中提取一行作为字符串;
代码如下:
void test__string10()
{
string str;
getline(cin, str);
cout << str << endl;
}
普通流提取,碰到空格或者换行都会停止,换到提取下一个对象;
getline中间遇到空格不会被打断;
九、练习例题
1.仅反转字母
https://leetcode.cn/problems/reverse-only-letters/
代码如下:
class Solution {
public:
bool isLetter(const char c)//判断是否为字母
{
if(c >= 'a' && c <= 'z'
|| c >= 'A' && c <= 'Z')
{
return true;
}
else
{
return false;
}
}
string reverseOnlyLetters(string s) {
size_t begin = 0, end = s.size() - 1;//两个下标,分别指向头和尾
while(begin < end)
{
while(begin < end && !isLetter(s[begin]))//判断下标指向的字符是否为字母
{
begin++;
}
while(begin < end && !isLetter(s[end]))
{
end--;
}
swap(s[begin], s[end]);//交换
++begin;
--end;
}
return s;
}
};
2.字符串中的第一个唯一字符
https://leetcode.cn/problems/first-unique-character-in-a-string/
代码如下:
class Solution {
public:
int firstUniqChar(string s) {
//计数排序的思想
int CharCount[26] = {0};//创建一个记录所有字母出现次数的数组
for(auto ch : s)
{
CharCount[ch - 'a']++;//每个字母出现一次就在对应数组位置加一
}
for(size_t i = 0; i < s.size(); i++)//遍历字符串,找到第一个只出现一次的字符,并返回下标
{
if(CharCount[s[i] - 'a'] == 1)
{
return i;
}
}
return -1;
}
};
3.字符串最后一个单词的长度
https://www.nowcoder.com/practice/8c949ea5f36f422594b306a2300315da?tpId=37&&tqId=21224&rp=5&ru=/activity/oj&qru=/ta/huawei/question-ranking
代码如下:
#include <iostream>
#include <string>
using namespace std;
int main() {
string str;
getline(cin, str);
int pos = str.rfind(' ');
int len = str.size() - pos - 1;
cout << len << endl;
return 0;
}
4.验证回文串
https://leetcode.cn/problems/valid-palindrome/
代码如下:
class Solution {
public:
bool isLetterOrNum(const char c)//判断是否为字母或数字
{
return (c >= 'a' && c <= 'z'
|| c >= '0' && c <= '9');
}
bool isPalindrome(string s) {
for(size_t i = 0; i < s.size(); i++)//先将大写字母转为小写
{
if(s[i] >= 'A' && s[i] <= 'Z')
{
s[i] += 32;
}
}
int begin = 0, end = s.size() - 1;//创建头尾两个下标
while(begin < end)
{
while(begin < end && !isLetterOrNum(s[begin]))//只判断字母和数字
{
begin++;
}
while(begin < end && !isLetterOrNum(s[end]))
{
end--;
}
if(s[begin] != s[end])//不是回文返回false
{
return false;
}
begin++;
end--;
}
return true;
}
};
5.字符串相加
https://leetcode.cn/problems/add-strings/
代码如下:
//头插法
class Solution {
public:
string addStrings(string num1, string num2)
{
//取两个数的尾部,从后往前加
int end1 = num1.size() - 1;
int end2 = num2.size() - 1;
int next = 0;//进位标志
string strRet;//结果
while(end1 >= 0 || end2 >= 0)
{
int val1 = end1 >= 0 ? num1[end1] - '0' : 0;//取两数的最后一个数字
int val2 = end2 >= 0 ? num2[end2] - '0' : 0;
int ret = val1 + val2 + next;//计算这两位的结果
next = ret > 9 ? 1 : 0;//判断是否进位
//头插
strRet.insert(strRet.begin(), (ret % 10) + '0');//将计算结果插入到结果字符串最前面
--end1;
--end2;
}
if(next)//如果两个数字都加完,但是进位还有,需要考虑进位
{
strRet.insert(strRet.begin(), next + '0');
}
return strRet;
}
};
//尾插法,最后逆置
class Solution {
public:
string addStrings(string num1, string num2)
{
//取两个数的尾部,从后往前加
int end1 = num1.size() - 1;
int end2 = num2.size() - 1;
int next = 0;//进位标志
string strRet;//结果
while(end1 >= 0 || end2 >= 0)
{
int val1 = end1 >= 0 ? num1[end1] - '0' : 0;//取两数的最后一个数字
int val2 = end2 >= 0 ? num2[end2] - '0' : 0;
int ret = val1 + val2 + next;//计算这两位的结果
next = ret > 9 ? 1 : 0;//判断是否进位
//尾插,后逆置
strRet += (ret % 10 + '0');
--end1;
--end2;
}
if(next)//如果两个数字都加完,但是进位还有,需要考虑进位
{
strRet += '1';
}
reverse(strRet.begin(), strRet.end());//逆置
return strRet;
}
};