目录
一、 string类对象的常见构造
二、string类对象的容量操作
2.1 size(返回字符串有效字符长度)
2.2 capacity(返回空间总大小)
2.3 reserve扩空间编辑
2.4 resize初始化不会覆盖本来的空间编辑
2.5 对于test_string7中每一句代码进行调试运行
三、string类对象的访问及遍历操作
3.1 operator[]
3.2 迭代器
四、笔试题
1.关于代码输出正确的结果是( )
2.关于代码输出正确的结果是( )
3.关于代码输出正确的结果是( )
4. 把字符串转换成整数_牛客题霸_牛客网 (nowcoder.com)
5.917. 仅仅反转字母 - 力扣(Leetcode)
6.541. 反转字符串 II - 力扣(Leetcode)
7.125. 验证回文串 - 力扣(Leetcode)
一、 string类对象的常见构造
string初始化:
#define _CRT_SECURE_NO_WARNINGS 1
#include<string>
#include<iostream>
using namespace std;
//string管理动态增长的字符数组,这个字符串以\0结尾
int main()
{
std::string s;
//string初始化
string s1;//构造空的string类对象s1
cout << "s1: " << s1 << endl;
string s2("hello world");
s2 += "!!!!";
cout << "s2: " << s2 << endl;
string s3(s2);
cout << "s3: " << s3 << endl;
string s4 = s2;//拷贝构造
cout << "s4: " << s4 << endl;
string s5("https://blog.csdn.net/weixin_57604904?spm=1000.2115.3001.5343", 4);
cout << "s5: " << s5 << endl;
string s6(6, 'C');
cout << "s6: " << s6 << endl;
string s7(s2, 6, 5);//第6个字符往后,不包括第6个,5个字符进行初始化
cout << "s7: " << s7 << endl;
string s8(s2, 6, 4);
cout << "s8: " << s8 << endl;
string s9(s2, 1, 100);//不会报错,会到\0截止
cout << "s9: " << s9 << endl;
string s10(s2, 6);//从s2的第六个字符往后直接到\0
cout << "s10: " << s10 << endl;
string s11(s2, 1);
cout << "s11: " << s11 << endl;
string s12(s2, 10);
cout << "s12: " << s12 << endl;
return 0;
}
s1可修改:
void test_string()
{
string s1("hello");
string s2("xxxx");
s1 = s2;
s1 = "xxx";
s1 = 'y';
cout << s1 << endl;
cout << s2 << endl;
}
int main()
{
test_string();
return 0;
}
二、string类对象的容量操作
2.1 size(返回字符串有效字符长度)
1. size()与length()方法底层实现原理完全相同,
引入size()的原因是为了与其他容器的接口保持一致,
一般情况下基本都是用size()。
2.2 capacity(返回空间总大小)
void test_string6()
{
string s("helloworld");
size_t sz = s.capacity();//空间总大小
cout << sz << endl;
//s.reserve(1000);//扩空间
//s.resize(1000, 'x');//扩空间+初始化
for (int i = 0; i < 1000; i++)
{
s.push_back('c');//尾插
if (sz != s.capacity())//尾插后,容量如果满了,容量会变
{
sz = s.capacity();//变了后就有新的sz
cout << "capacity changed: " << sz << "\n";
}
}
}
int main()
{
test_string6();
return 0;
}
初始size与capacity:
字符串有效字符长度与空间总大小:
尾插push_back,空间满了就扩容:
第一次扩容:
运行结果:
2.3 reserve扩空间
2.4 resize初始化不会覆盖本来的空间
注意:观察如下代码:
void test_string7()
{
string s("hello");
s.reserve(100);
s.resize(100, 'x');//注意不会改变hello,只是让size=100,后面补x
s.reserve(10);//说明reserve会扩不会缩
//s[9] = 'y'//会报断言错误
s.resize(10, 'x');//但是resize使得size= 10//s[9] = 'x'
s[9] = 'y';//不会报错
}
int main()
{
test_string7();
return 0;
}
2.5 对于test_string7中每一句代码进行调试运行
1.capacity变为111
2.size变为100,补x
3. capacity没有变(reserve不会缩只会扩)
4.resize会改变size,从100->10
5.改变s[9],不会报错
3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,
不同的是当字符个数增多时:
resize(n)用0来填充多出的元素空间,
resize(size_t n, char c)用字符c来填充多出的元素空间。
注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,
当reserve的参数小于string的底层空间总大小时,reserve不会改变容量大小。
三、string类对象的访问及遍历操作
3.1 operator[]
返回位于字符串中指定位置的字符:
1. 对于operator[],字符串中的第一个字符用值0(而不是1)表示,
s1.operator[](i)//返回位于字符串中指定位置i的字符。
如果字符串对象是const限定的,则该函数返回const char&。否则,它返回char&。
char& operator[] (size_t pos);const char& operator[] (size_t pos) const;
2. 为什么不能返回char?
1).返回的是位于字符串指定位置pos的字符,return str[pos]
其因为是在堆区开辟的,出了作用域还在,所以按常规操作肯定是返回char&别名更合适,减少拷贝;
2).这样引用返回可以使得返回值可修改,因为如果是char返回,返回是先创建临时变量,返回的是临时变量,而临时变量具有常性,const权限放大,不能赋值和修改,而char&不会,可以修改。
void test_string2()
{
string s1("hello");//初始化
s1.at(1) = 'x';
//s1[6];//会报错assert判断的
cout << s1.size() << endl;//s1.size()返回有效字符个数,不包含\0
for (size_t i = 0; i < s1.size(); i++)
{
cout << s1[i] << " ";
}
cout << endl;
}
int main()
{
test_string2();
return 0;
}
3.2 迭代器
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
void test_string4()
{
string s = "helloworld";
string::reverse_iterator rit = s.rbegin();
while (rit != s.rend())
{
cout << *rit << " ";
rit++;
}
cout << endl;
}
void test_string5()
{
const string s = "helloworld";
string::const_reverse_iterator rit = s.rbegin();
while (rit != s.rend())
{
cout << *rit << " ";
rit++;
}
cout << endl;
}
int main()
{
test_string4();
test_string5();
return 0;
}
四、笔试题
1.关于代码输出正确的结果是( )
关于代码输出正确的结果是( )
int main(int argc, char *argv[])
{
string a="hello world";
string b=a;
if (a.c_str()==b.c_str())
{
cout<<"true"<<endl;
}
else
cout<<"false"<<endl;
string c=b;
c="";
if (a.c_str()==b.c_str())
{
cout<<"true"<<endl;
}
else
cout<<"false"<<endl;
a="";
if (a.c_str()==b.c_str())
{
cout<<"true"<<endl;
}
else
cout<<"false"<<endl;
return 0;
}
A.false false false
B.true false false
C.true true true
D.true true false
分析:a 和 b的值虽然相同,
但是a.c_str()==b.c_str()比较的是存储字符串位置的地址,
a和b是两个不同的对象,内部数据存储的位置也不相同,因此不相等,
后面c="",a=""与b对象都没有任何的影响,所以都不相等
故答案为:A
2.关于代码输出正确的结果是( )
下面程序的输出结果正确的是( )
int main()
{
string str("Hello Bit.");
str.reserve(111);
str.resize(5);
str.reserve(50);
cout<<str.size()<<":"<<str.capacity()<<endl;
return 0;
}
A.10 50
B.5 50
C.5 111
D.10 111
分析:
str.reserve(111); //调整容量为 111
str.resize(5); //调整元素个数为 5
str.reserve(50); //调整容量为 50,由于调整的容量小于已有空间容量,故容量不会减小
所以size=5 capacity=111
故答案为: C
3.关于代码输出正确的结果是( )
下面程序的输出结果正确的是( )
int main(int argc, char *argv[])
{
string strText = "How are you?";
string strSeparator = " ";
string strResult;
int size_pos = 0;
int size_prev_pos = 0;
while((size_pos=strText.find_first_of(strSeparator, size_pos)) != string::npos)
{
strResult = strText.substr(size_prev_pos, size_pos-size_prev_pos);
cout<<strResult<<" ";
size_prev_pos = ++size_pos;
}
if(size_prev_pos != strText.size())
{
strResult = strText.substr(size_prev_pos, size_pos-size_prev_pos);
cout<<strResult<<" ";
}
cout<<endl;
return 0;
}
A.Howareyou?
B.How Are You?
C.How are
D.How are you?
分析:程序的目的是以字符串strSeparator = " "作为分隔符,
对字符串string strText = "How are you?";进行分割,每分割出一个单词就进行一次打印
故答案为 D
实际的 find_first_of函数的作用是:
在一个字符串中进行查找,返回值是第一个与指定字符串中任何字符匹配的字符位置;
如果没有找到匹配的内容,就返回 string::npos。
注意与find的区别:发现find是返回-1,说明找不到。
find:
substr:
substr(size_type _Off = 0,size_type _Count = npos)
参数:
_Off——所需的子字符串的起始位置。字符串中第一个字符的索引为 0,默认值为0。
_Count——复制的字符数目
返回值——一个子字符串,从其指定的位置开始
假设string s(“student12”);
string x=s.substr() //默认时的长度为从开始位置到尾
string y=s.substr(5) //获得字符串s中 从第5位开始到尾的字符串
string z=s.substr(5,3); //获得字符串s中 从第5位开始的长度为3的字符串
具体解析:
int main()
{
string strText = "How are you?";
string strSeparator = " ";
string strResult;
int size_pos = 0;
int size_prev_pos = 0;
//找不到就返回npos
//在strText中找空格,返回位置为3,因为是从0开始。
//找不到就返回string::pos
//只要strText.find_first_of(strSeparator, size_pos)) != string::npos就说明
//找到了空格,找到了空格就返回所在位置或者说是下标size_pos
while ((size_pos = strText.find_first_of(strSeparator, size_pos)) != string::npos)
//在strText的第0开始找strSeparator也就是空格,返回的位置只要不是-1,就进入循环,
//如果在一个字符串strText中查找另一个字符串strSeparator,
//如果strText1中含有strSeparator中的任何字符,则就会查找成功
{
//从0开始找(3-0)个元素,找到How
strResult = strText.substr(size_prev_pos, size_pos - size_prev_pos);
cout << strResult << " ";//再输出一个空格
size_prev_pos = ++size_pos;//然后同时++,都变成4
//之后size_pos = 4,从位置4开始找空格,到了位置7,7-4=3
//size_prev_pos = size_pos = 8
//之后就找不到空格了,跳出循环,size_pos = -1,size_prev_pos = 8
}
if (size_prev_pos != strText.size())//已知size = 12,8!=12进入if
//只要不和size相等就进入循环
{
strResult = strText.substr(size_prev_pos, size_pos - size_prev_pos);
//从8开始找到-9,就相当于找到底
cout << strResult << " ";
}
cout << endl;
return 0;
}
4. 把字符串转换成整数_牛客题霸_牛客网 (nowcoder.com)
描述
将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为 0 或者字符串不是一个合法的数值则返回 0
class Solution
{
public:
int StrToInt(string str)
{
if (str == "")//如果没有字符串返回0
return 0;//输入一个字符串,包括数字字母符号,可以为空
int ret = 0;
int flag = 1;
if (str[0] == '-')
{
str[0] = '0';
flag = -flag;
}
if (str[0] == '+')
{
str[0] = '0';
}
for (int i = 0; i < str.size(); ++i)
{
if (str[i] < '0' || str[i] > '9')
//注意:字符串中可能出现任意符号,出现除 +/- 以外符号时直接输出 0
{
return 0;
}
if (str[i] >= '0' && str[i] <= '9')
//比如str是0123,str[1] = 1,ret = 0+1
//str[2] = 2,ret = 1*10+2=12
//str[3] = 3,ret = 12*10+3=123
{
ret = ret*10 + str[i] - '0';//‘0’ascii码值是48
//字符'1'的ascii码值是49,49-48 =1,从字符1变成整数1
}
}
if (flag == -1)
{
ret *= -1;//如果flag等于-1,那么ret就要乘上-1
}
return ret;
}
};
5.917. 仅仅反转字母 - 力扣(Leetcode)
给你一个字符串 s
,根据下述规则反转字符串:
- 所有非英文字母保留在原有位置。
- 所有英文字母(小写或大写)位置反转。
返回反转后的 s
。
class Solution {
public:
bool isLetter(char ch)
{
if(ch >= 'A' && ch <= 'Z')
{
return true;
}
else if(ch >= 'a' && ch <= 'z')
{
return true;
}
else
{
return false;
}
}
string reverseOnlyLetters(string s)
{
string::iterator left = s.begin();
string::iterator right = s.end()-1;
while(left < right)
{
while(left<right && !isLetter(*left))
//是字母就为true,取反为0
//不是字母就返回false,取反为1
left++;//说明不是字母就++
while(left<right && !isLetter(*right))
right--;
swap(*left,*right);//是字母就进行交换
left++;
right--;
}
return s;
}
};
6.541. 反转字符串 II - 力扣(Leetcode)
给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。
如果剩余字符少于 k 个,则将剩余字符全部反转。
如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
class Solution {
public:
void reverse(string& s, int left, int right)
{
while (left < right)
{
swap(s[left], s[right]);
left++;
right--;
}
}
string reverseStr(string s, int k)
{
//从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。
//每次i向前移动2k个字符
for (int i = 0; i < s.size(); i += 2 * k)
{
//x为走了2k步后的剩余字符,如果k<= x <2k,那么反转前k个数
//那么s.size()>=(k+2k),就反转前k个数
if (s.size() >= (i + k))
{
reverse(s, i, i + k - 1);
continue;//反转前k个数后,调到for循环,继续向后走2k步,如果还大于,接着反转前k个数
}
//如果x<k,反转剩下的字符,剩下的就是i到s.size()-1
reverse(s, i, s.size() - 1);
}
return s;
}
};
7.125. 验证回文串 - 力扣(Leetcode)
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。
字母和数字都属于字母数字字符。
给你一个字符串 s
,如果它是 回文串 ,返回 true
;否则,返回 false
。
class Solution {
public:
char func(char c)
{
if (('0' <= c && c <= '9') || ('a' <= c && c <= 'z'))
return c;//数字0~9或a~z无需转换
else if ('A' <= c && c <= 'Z')
return c + 'a' - 'A'; //所有大写转小写,A~Z转化为对应a~z
else
{
return ' '; //其他字符转换为空格符' '
}
}
bool isPalindrome(string s)
{
if (s.size() == 0 || s.size() == 1)
return true;//空字符和一个字符都是回文结构
string::iterator left = s.begin();
string::iterator right = s.end() - 1;
char c1, c2;
while (left < right)
{
c1 = func(*left); //调用转化函数
c2 = func(*right); //调用转化函数
if (c1 == ' ')
{
left++;
}
if (c2 == ' ')
{
right--;
}
//c1和c2如果是空格,说明是别的字符且已经转换了
if (c1 != ' ' && c2 != ' ')
{
if (c1 != c2)
{
return false;
}
left++; right--;
}
}
return true;
}
};