string test1;//构造空的string类对象
string test2("abcd");//用c-string构造string类对象
string test3(test2);//用拷贝构造函数构造

都是求字符串对象有效字符个数。
返回值是size_t
string test4("hello world");
size_t size1 = test4.size();//有效字符长度 返回值是size_t
size_t size2 = test4.length();//有效字符长度 返回值是size_t

均为11,11位hello + 空格 + world的长度,不包含\0,而为什么会有length和size的名称差别呢,因为在数据结构中对于链表和顺序表的结构,length可以表明长度,但是在树等结构中用size可以更直观的表明大小。
求字符串对象的容量
string str1;
string str2("abc");
size_t c1 = str1.capacity();
size_t c2 = str2.capacity();

清除字符串中的有效字符
string str("abcdefg");
str.clear();
cout << str << endl;

什么也没有打印出来,那他的capacity有变化吗?

容量是没有变化的,还是15,因此clear只是清空string中有效字符,不改变底层空间大小。
判断字符串是否为空,空返回true,否则返回false。
string str1("abcdefg");
string str2;
bool k1 = str1.empty();
bool k2 = str2.empty();
return 0;

为string对象预留空间
string str("abcd");
str.reserve(20);
size_t k = str.capacity();//不预留的话是15

容量变成了31,因此针对我们用c语言实现的数据结构中,涉及多次扩容的并且暂时知道数据容量多少的时候,先提前reserve好空间比较好。
将有效字符变成n个,查询cplusplus文档得知。

有2种用法,用空字符填充和用指定字符填充。
既然是更改字符的数量,那么肯定有3种情况。
string st1("hello wjw");
st1.resize(5);
cout << st1.size() << endl;
cout << st1.capacity() << endl;
cout << st1 << endl<st2和st3的resize差别是在于填充的字符是空字符和*的区别。

可以看到当n<size的时候,直接返回前n个字符。不修改容量
size<=n<=capacity的时候,插入数据,不修改容量
capacity<n的时候,扩容,再插入数据。
for (size_t i = 0; i < str.size(); i++)
{
cout << str[i] << endl;
}
用[]重载
string::iterator it = str.begin();
while (it != str.end())
{
cout << *it << endl;
}
c++中string类中存在迭代器。begin和end获取一个字符的迭代器,begin是字符串第一个字符,end是最后一个字符的下一个位置的迭代器。
同时还有反向迭代器,反向遍历。
string::reverse_iterator rit = str.rbegin();
while (rit != str.rend())
{
cout << *rit << endl;
}
在c++11中,可以直接使用auto关键词推导迭代器类型。
auto it = str.begin();
while (it != str.end())
{
cout << *it << endl;
}
注意以下情况,如果我们要求只读一个字符串的时候,如何使用迭代器?比如
void test(const string& str)
{
string::iterator it = str.begin();
while(it != str.end())
{
cout<<*it<
编译器报错,说不能从const的迭代器转换到非const的迭代器。
查询文档得知。

设计了2种迭代器。
当我们只读的时候,应使用string::const_iterator的格式。
同样的针对第一种[]遍历,

也有2种,const和非const。
const string str("abcdefg");
for (size_t i = 0; i < str.size(); i++)
{
str[i] += 1;
cout << str[i] << endl;
}
当给定的是常量字符串对象,[]就会调用const类型的,如果试图修改字符串,就会报错。

针对只读的功能函数,设计的时候提供const版本即可
针对只写的功能函数,设计的时候提供非const版本即可
针对读写的功能函数,设计的时候提供const和非const版本
string str("abcd");
for (auto ch : str)
{
cout << ch;
}
尾插一个字符,会扩容
string str("abcd");
size_t k = str.capacity();
for(size_t i = 0; i < 100; i++)
{
str.push_back('a');
if (k != str.capacity())
{
k = str.capacity();
cout << "capacity changed:" << str.capacity() << endl << endl;
}
}
}

每次大概是1.5倍的扩容,

在字符串末尾添加字符串
string str("abcd");
str.append("hello");
string str2("bit");
str.append(str2);
cout << str << endl;
可以追加一个字符串对象,也可以直接追加字符串。

追加字符串
string str("abcd");
str += "hello bit";
string str2("abcd");
str += str2;
cout << str << endl;

class Solution {
public:
string addStrings(string num1, string num2) {
int end1 = num1.size()-1;
int end2 = num2.size()-1;
string num3;
int carry = 0;
while(end1>=0||end2>=0)
{
int sum1 = end1>=0?num1[end1]-'0':0;
int sum2 = end2>=0?num2[end2]-'0':0;
int ret = sum1 + sum2 + carry;
carry = ret / 10;
ret %= 10;
num3 += ret+'0';
--end1;
--end2;
}
if(carry == 1)
{
num3 += '1';
}
reverse(num3.begin(),num3.end());
return num3;
}
};
逆置字符串,注意!区间为左闭右开!,所以reverse(a,a+1)没有意义!
string str("hello wjw");
reverse(str.begin(), str.end());
cout << str << endl;

是给2个迭代器,逆置2个迭代器范围内的数据。
查找第一个符合的字符,并且返回他的下标。


如果没有查找到,返回npos

所以在查找的时候一般可以让查找值是否等于-1来判断是否找到。
string str("hello wjw");
size_t pos = str.find('j');
if (pos != -1)
{
cout << str[pos] << endl;
}
string库实现的时候,给pos位置一个缺省值,因此如果我们只传对象的时候,默认是从下标0开始查找的。
那如何查找下一个字符呢?
string str("woshi woshi");
size_t pos = str.find('w');
while (pos != string::npos)
{
cout << str[pos] << endl;
pos = str.find('w', pos + 1);
}
设置下一次查找的位置在第一次找到的pos之后一个位置即可。
反向查找第一个符合的字符,并且返回其下标。
返回pos位置开始的后续的len个字符

如图len是给了缺省值npos的,npos是size_t类型,因此如果我们不传参len的话,是相当于直接返回pos开始直到字符串结束的所有字符。
因此比如我们想取一个文件的后缀名的时候,就可以使用这个
string file("test.cpp");
size_t pos = file.rfind('.');
string str = file.substr(pos);
cout << str << endl;

在string里面查找与给定的字符c或者字符串相符合的 string里面的第一个字符的下标。
string str("wo ai ni ge chou sha bi");
size_t pos = str.find_first_of("oie");
while (pos != string::npos)
{
str[pos] = '*';
pos = str.find_first_of("oie", pos + 1);
}
cout << str << endl;

删除pos位置的字符

第一个是删除pos位置开始,如果不给len的长度就会删除从pos位置一直到字符串结束的所有字符。
第二个是给一个迭代器,删除这个位置的字符
第三个是给2个迭代器,删除2个迭代器范围内的所有字符。
string str("wo ai ni ge chou sha bi");//类似头删
str.erase(0,1);
cout << str << endl;
string str("wo ai ni ge chou sha bi");
str.erase(str.begin());
cout << str << endl;
string str("wo ai ni ge chou sha bi");
str.erase(str.begin(), str.end());
cout << str << endl;
在数据结构中顺序表我们学过,头删是要挪动数据的,因此我们尽量少用。时间复杂度为O(N^2)
返回一个c语言中的数组,该数组为c字符串,包含\0
string str("wo ai ni ge chou sha bi");
char* str1 = new char[str.size() + 1];
strcpy(str1, str.c_str());
cout << str1 << endl;

将字符串全部替换为另外一个字符串的前n个,如果没有定义n,n默认为npos,相当于先clear再+=。

string str("wo ai ni ge chou sha bi");
cout << str.capacity()<< endl;
str.assign("abcd");
cout << str.capacity() << endl;

如果是变小,容量不变
string str("wo ai ni ge chou sha bi");
cout << str.capacity()<< endl;
str.assign("abcdadsadasdsadasdsadsadsadasd123213123213213213123213213213");
cout << str.capacity() << endl;

如果是变大,扩容。
assign相当于赋值拷贝
string str("wo ai ni ge chou sha bi");

从pos位置开始的len个字符替换成str
string str("wo ai ni ge chou sha bi");
str.replace(3, 2, "buai");
cout << str << endl;
如图就是把从第一个a开始的2个字符替换成buai。

#define _CRT_SECURE_NO_WARNINGS
#include
#include
namespace wjw
{
class string
{
public:
typedef char* iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
string(const char* str)
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
//拷贝构造
void swap(string& str)
{
std::swap(_str, str._str);
std::swap(_capacity, str._capacity);
std::swap(_size, str._size);
}
string(const string& str)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
string tmp(str._str);
swap(tmp);//野指针需要给初始化列表
}
string()
{
_str = new char[1];
_str[0] = '\0';
_capacity = _size = 0;
}
string& operator=(string str)
{
//注意点一是返回值 二是传参传引用并且用const修饰
//三是判断*this和str是否相等 四是要释放原来的空间
swap(str);
return *this;
}
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
void reserve(size_t n)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
void push_back(char ch)
{
if (_size == _capacity)
{
reserve(_capacity * 2);
}
else
{
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
}
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len + 1);
}
strcpy(_str + _size, str);
_size += len;
}
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
string& erase(size_t pos, size_t len = npos)
{
assert(pos < _size);
if (len == npos || len >= _size - pos)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return *this;
}
string& insert(size_t pos, char str)
{
size_t end = _size;
while (end >= (int)pos)
{
_str[end + 1] = _str[end];
--end;
}
return *this;
}
string& insert(size_t pos, const char* str)
{
size_t len = strlen(str);//计算不含\0
if (len + _size > _capacity)
{
reserve(_size + len);
}
size_t end = _size;
while (end >= (int)pos)
{
_str[end + len] = _str[end];
--end;
}
strncat(_str + pos, str, len);
_size += len;
return *this;
}
size_t find(char ch,size_t pos = 0)const//查找字符版本
{
assert(pos < _size);
while (pos < _size)
{
if (_str[pos] == ch)
{
return pos;
}
++pos;
}
return npos;
}
size_t find(const char* str, size_t pos = 0)const
{
assert(pos < _size);
const char* st = strstr(_str + pos, str);
if (st == nullptr)
{
return npos;
}
else
{
return st - _str;
}
}
void clear()
{
_size = 0;
_str[0] = '\0';
}
private:
char* _str;
size_t _size;
size_t _capacity;
const static size_t npos = -1;
};
}