前言
string是一个非常常见的数据类型,用于表示文本或字符序列。以下是关于它的详细介绍:
string(字符串)是由零个或多个字符组成的有限序列。字符可以是字母、数字、标点符号、空格或其他符号。例如,“Hello”、“123”、“!@#$” 等都是字符串。
在 C++ 中,string是标准库(<string>头文件)中的一个类。可以通过以下方式定义和使用字符串:
#include <iostream>
#include <string>
int main() {
std::string str = "Hello, World!";
std::cout << str << std::endl;
return 0;
}
一、string类对象的构造函数
string实现了多个构造函数的重载,常用的构造函数如下:
string();构造空字符串(默认构造函数) .
string (size_t n, char c);生成n个C 字符的字符串
string (const char* s); 复制由 s 指向的以 null 结尾的字符序列(C 字符串)or 用C-string来构造string类对象 .
string (const char* s, size_t n);复制s所指的前n个字符序列
string(const string& str);生成str的复制品(拷贝构造) .
string(const string& str, size_t pos, size_t len = npos);//复制str中从字符位置 pos位置开始并跨越len个字符的部分(如果len超过npos,那就取到npos停止)
使用示例 :
string s1; 构造空字符串 .
string s2("hello world"); C格式字符串构造string类对象s2 .
string s3("hello world",3);复制"hello world"的前三个字符
string s4(10, 's'); 生成10个's'字符的字符串
string s3(s2); 拷贝构造 .
string s4(s2, 6, 1000); 取不到这么多,到npos就停下了;
string s5(s2, 6); 不给具体取多少,那就自动取到npos结束
二、sring元素的访问
有三种模式进行遍历
string底层结构:
namespace ky
{
class string
{
public:
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[_size];
}
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[_size];
}
private:
//两段存储
char _buff[16];//<16,存到对象本身里面,不用去堆上开空间,效率提高
char* _str;//如果>=16个字符就到堆开辟空间存储,之前的_buff浪费掉,(以空间换时间),减少内存碎片,提高效率
size_t _size;
size_t _capacity;
};
}
下标法
operator [ ]
char& operator[] (size_t pos);
const char& operator[] (size_t pos) const;
void teststring1()
{
string s1;
string s2("hello world");
const string s6 = "xxxxxx";
s6[0] = 'y';//权限放大,不能修改
1.下标 + []
for (size_t i = 0; i < s2.size(); i++)
{
//[]+下标修改对象元素内容
s2[i] += 1;
//s2.operator[](i) += 1;编译器会翻译成这样
}
for (size_t i = 0; i < s2.size(); i++)
{
//[]+下标访问对象元素
cout << s2[i] << " ";
}
cout << endl;
}
正向迭代器 :
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
反向迭代器 :
reverse_iterator rbegin()
const_reverse_iterator rbegin() const;
reverse_iterator rend();
const_reverse_iterator rend() const;
2.迭代器
[)
//char*
string::iterator it = s2.begin();
//auto it = s2.begin();
while (it != s2.end()) //最好不要用 < 因为string底层是数组,空间连续,但是其他的就不一定连续无法比较大小
{
*it += 1;
cout << *it << " ";
++it;
}
cout << endl;
//const char*
string::const_iterator it5 = s5.begin(); //迭代器本身可以修改,指向的内容不能修改
//auto it5 = s5.begin();
while (it5 != s5.end())
{
cout << *it5 << " ";
++it5;
}
cout << endl;
//反向迭代器遍历;
string::reverse_iterator rit = s2.rbegin();
while (rit != s2.rend())
{
*rit += 1; //没有const限制可修改本身
cout << *rit << " ";
++rit;
}
cout << endl;
string::const_reverse_iterator rit2 = s2.rbegin();
while (rit2 != s2.rend())
{
//*rit += 1;//const限制,要修改权限就会放大
cout << *rit2 << " ";
++rit;
}
cout << endl;
3.基于for循环遍历(底层还是用迭代器)
在编译器的前置处理阶段就替换成了iterator,把 *it 给了 ch
编译器编译到语法层面就没有这个基于范围for
for (auto ch : s2)
{
cout << ch << " ";
}
cout << endl;
at.( ) char& at (size_t pos); const char& at (size_t pos) const;
at 方法和operator[ ]无本质区别,且operator[ ]更加常用, at()方法很少用到
{
string s1 = "hello world";
for (size_t i = 0;i < s1.size();i++)
{
cout << s1.at(i) << " ";
}
cout << endl;
}
三、 string的大小和容量
1.size( )
size()用来计算字符串中有效字符的个数,不包含’\0’
2. capacity( )
返回当前为字符串分配的存储空间的大小,以字节表示
capacity() 用来计算当前字符串的容量,最多能够存放的有效字符的个数
同样的一个字符串, 不同编译器下给定的capacity的大小可能会不太一样,没有规律~
3. clear( )
clear用于清空字符串,使得字符串中有效字符的个数为0,因此size为0,capacity不变
4. empty( )
empty()用于判断字符串是否为空,为空返回1,不为空返回0
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1;
cout << sizeof(s1.max_size()) << endl;//实际开不出这么大的空间
string s2("hello world");
cout << s2.size() << endl; //11 '\0'只起标识符作用
cout << s2.length() << endl;//length和size功能一样但为了和其他容器使用规则一样统一用size().
cout << s2.capacity() << endl;//15 不算'\0',只计算存在的有效字符, 实际上空间一共有16个
//size和capacity均不包含'\0'
s2.clear();
cout << s2.size() << endl;
cout << s2.capacity() << endl;
string s4 = "hello";
string s5;
cout << s4.empty() << endl;//1
cout << s5.empty() << endl;//0
}
5. shrink_to_fit( )
shrink_to_fit()是用来向系统请求把容量capacity减小到和有效字符个数size一样大
注意,这只是一个请求,实际情况下即使用了该方法,capacity也不一定改变
string s2("hello world");
cout << s2.size() << endl; //11
cout << s2.capacity() << endl;//15
s2.shrink_to_fit();
cout << s2.size() << endl; //11
cout << s2.capacity() << endl;//15
clear()只能清空字符串,不会改变容量,这时再用shrink_to_fit就可以完美地将capacity置成0
6. reserve( )
请求更改容量
请求根据计划的大小更改调整字符串容量,长度最多为 n 个字符
reserve一般是用来提前申请空间的,有些情况下使用reserve可以很好地避免频繁扩容
因为缩容有不确定性
系统实际分配的空间都是略大于我们指定的n个字符的~
namespace ky
{
class string
{
public:
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[_size];
}
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[_size];
}
private:
//两段存储
char _buff[16];//<16,存到对象本身里面,不用去堆上开空间,效率提高
char* _str;//如果>=16个字符就到堆开辟空间存储,之前的_buff浪费掉,(以空间换时间),减少内存碎片,提高效率
size_t _size;
size_t _capacity;
};
}
//扩容
void teststring4()
{
这两行代码为了展示分段存储
string s1("1111111");//_buff
string s2("12152484845548848455");
cout << s1.size() << endl; 7
cout << s1.capacity() << endl; 15
s2.reserve(40);//s2本身的size和capacity比40要大,但是reserve不会改变size和capacity,
实际中reserve只用于扩容因为缩容不确定,比如这里capacity没有缩小到和size一样大,但是gcc下会缩容.
cout << s2.size() << endl; 20
cout << s2.capacity() << endl; 47
string s;
s.reserve(1000);//这里就是reserve的用武之地,下面就不会再扩容了,避免频繁扩容
vs2022下的扩容规律
size_t old = s.capacity();
cout << "capacity changed :" << old << '\n';
cout << "making s grow:\n";
for (int i = 0; i < 100; ++i)
{
//s.push_back('c');
s += 'c';
if (old != s.capacity())
{
old = s.capacity();
cout << "capacity changed: " << old << '\n';
}
}
}
在VS2022编译器下可以看出从_buff到 _str转换的时候是异地扩容,当_buff空间不够的时候把_buff复制下来在_str上开辟一块_buff 2倍的空间来存放原来_buff的数据,如果后面频繁扩容,就1.5倍扩容,这种扩容机制是由编译器底层决定的
例如,在gcc下就是2倍扩容
7. resize
void teststring5()
{
string s5("hhhhhhhh");
cout << s5.size() << endl;8
cout << s5.capacity() << endl;15
s5.resize(5);
cout << s5.size() << endl;5
cout << s5.capacity() << endl;15
s5.resize(20);//扩容 + 插入null characters
//原来的size数据不变,后面+ null characters
cout << s5.size() << endl;20
cout << s5.capacity() << endl;31
}
四、增删查改
1. push_back( )
int main()
{
string s1;
s1.push_back('x');
s1.push_back('x');
s1.push_back('x');
cout << s1 << endl; // xxx
}
2. append( )
append是用于追加到字符串,也就是尾插
通过在字符串的当前值末尾附加其他字符来扩展字符串
1. 追加整个字符串
int main()
{
string s1 = "hello";
s1.append("w");
s1.append("world");
string s2("hello world");
string s3("hello ky");
s2.append(s3);
cout << s2 << endl;
}
2. 追加字符串的一部分
int main()
{
string s1 = "hello";
string s2 = "world";
s1.append(s2, 0, 3);//从s2下标为0开始尾插三个字符
cout << s1 << endl;
//取迭代器区间
string s2("hello world");
s1.append(s2.begin() + 6, s2.end());
cout << s1 << endl;
3. operator+=( )
operator+=( ) 也是追加字符,字符串,尾插到字符串末尾
string s1;
string s2 = "hahahahihihi"
s1 += " ";
s1 += "hahaha";
s1 += s2;
cout << s1 << endl;
4 . assign( )
assign( )和赋值差不多
//赋值 string对象
string s1 = "hello";
string s2 = "world";
s1.assign(s2); //将s2赋值给s1
cout << s1 << endl; //world
//赋值 常量字符串
string s3 = "hello";
s3.assign("world"); //将"world"赋值给s3
cout << s3 << endl; //world
//赋值 常量字符串的前n个字符
string s4 = "hello";
s4.assign("world", 2); //将"world"的前2个字符赋值给s4
cout << s4 << endl; //wo
//赋值 n个字符
string s5 = "hello ";
s5.assign(2, 'w'); //赋值2个'w'字符给s5
cout << s5 << endl; //ww
//赋值 string的子串
string s6 = "hello";
string s7 = "Syntactic sugar";
s6.assign(s7, 3, 6); //赋值s7从下标为3的位置开始的6个字符给s6
cout << s6 << endl; //tactic
5. insert( )
insert()是在指定位置插入字符或者字符串
insert一般用处不大,效率太低
int main()
{
插入单个字符
string s1 = "hello world";
s1.insert(0, 3, 'h'); //下标为0的位置插入3个'h'
cout << s1 << endl; //hehhh llo world
string s2 = "hello world";
s2.insert(s2.begin(), 'w'); //开始位置插入字符'x'
cout << s2 << endl; //xhello world
string s3 = "hello world";
s3.insert(s3.begin(), 2, 'w'); //开始位置插入2个字符'w'
cout << s3 << endl; //wwhello world
插入字符串
//插入整个字符串
string s1 = "hello world";
s1.insert(2, "come"); //下标为2位置开始插入字符串"come"
cout << s1 << endl; //hecomello world
//插入字符串的一部分
string s2 = "hello world";
string s3 = "Syntactic sugar";
s2.insert(2, s3, 2, 7); //取s3字符串下标从2开始的7个字符插入s2下标为2的位置
cout << s2 << endl; //hentactic world
string s4 = "hello world";
s4.insert(2, "world", 2); //取world下标为0(默认)开始的2个字符插入到s4中下标为2的位置
cout << s4 << endl; //hewollo world
6. erase( )
从字符串中擦除字符
erase()是用于删除指定位置起的一个或若干个字符
这个效率也低
string s("555555555555")
s.erase(5, 3);//删除s下标从5开始的3个子符
cout << s << endl;
s.erase(5);删除s下标从5开始的后面的字符(直到npos)
cout << s << endl;
//都可以达到头删的效果
s.erase(0, 1);
s.erase(s.begin());
7. replace( )
replace是用新的字符或者字符串替换原字符串部分字符
效率也一般
string s1("2222222");
string s2("3333333333");
s1.replace(0, 2, s2);把s1从下标为0开始的2个字符替换成s2
cout << s1 << endl;
cout << s2 << endl;
string s1 = "hello world";
s1.replace(2, 5, "xxxxxxxxxxxx"); //将s1从下标2开始的5个字符替换
//成"xxxxxxxxxxxx"
cout << s1 << endl; //hexxxxxxxxxxxxorld
string s4 = "hello world";
s4.replace(2, 5, 3, '*'); //将s4下标从2开始的5个字符替换成3个'*'
cout << s4 << endl; //he***orld
//空格替换成%号
string s2("hello world hello c++");
size_t pos = s2.find(' ');
while (pos != string::npos)
{
s2.replace(pos, 1, "%%");//从下标为pos的位置开始的1个字符换成%%
pos = s2.find(' ', pos + 2);//从替换的这里接着找不用从头再来
}
cout << s2 << endl;
//以空间换时间,效率提高
string s2("hello world hello c++");
string s3;
for (auto ch : s2)
{
if (ch == ' ')
s3 += "%%";
else
s3 += ch;
}
cout << s3 << endl;
8. find( )
//find会返回找到的字符串的首字符或者字符的下标
string s1 = "hello world";
size_t pos1 = s1.find("llo",1); //从下标1开始查找字符串"llo"
cout << pos1 << endl; //2
size_t pos2 = s1.find("llx", 1);
cout << pos2 << endl; //找不到返回npos(size_t类型,值为-1,是一个很大的整数在X86下接近2个G)
string s2 = "hello world";
size_t pos3 = s1.find('l'); //默认从s2的第一个位置开始找'l'字符首次出现的位置,直到npos
cout << pos3 << endl; //2
9. rfind( )
find()是从左到右查找,而rfind()是从右到左查找
10. substr( )
substr()用于获取子串,返回获取到的子串
string s1 ="hello world";
string s2 = s1.substr(1,3);//获取从下标为1开始的3个字符作为原字符串子串返回
cout << s2 << endl;
string s3 = s1.substr(1); //s1从下标为1开始取完赋值给s3
cout << s3 << endl; //ello world
string s4 = s1.substr(); //取完整个s1
cout << s4 << endl; //hello world
取后缀名就能用到substr
string file("test.cpp");
string file("test.cpp.tar.zip");//linux下取真实的后缀名;
size_t pos = file.rfind('.');倒着找就可取最后一个真实的后缀名
size_t pos = file.find('.');
if (pos != string::npos)
{
//string str = file.substr(pos, file.size() - pos);
string str = file.substr(pos);//直接用给的缺省值npos,直接取到结尾
cout << str << endl;
}
五 、输入getline( )
在控制台,终端上输入的数据是在缓冲区中的,如流提取,cin,scanf()实际中我们可能会输入多个数据,整型,string,
c/c++规定 空格和回车默认是分隔符
如果遇到空格就会认为当前串已经结束了,后面的数据就拿不到了,后面的内容是给下一个提取串的,只是我们还没有提取.
getline( ) 不指定分隔符,默认以回车结束,因此可以直接读取一行
也可以自定义终止符
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s1;
cin >> s1;
cout << s1 << endl;
//abcdef T
//取不到 T 遇到空格或回车自动结束
string s1;
string s2;
cin >> s1;
cin >> s2;
cout << s1 << endl;
cout << s2 << endl;
string s;
getline(cin, s);
getline(cin, s,'#');//可以自定义终止符号
cout << s << endl;
}
标签:基本,string,s2,s1,cout,用法,字符串,size From: https://blog.csdn.net/2301_79262802/article/details/144133418