目标:熟悉使用,了解底层原理
一、标准库中的string类
1字符串是表示字符序列的类
2.标准的字符串类提供了对此类对象的支持,其接口类似干标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
3.string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。
4.string类是basic string模板类的一个实例,它使用char来实例化basicstring模板类,并用char_traits和allocator作为basic_string的默认参数(关于更多的模板信息请参考basic
string)。
5.注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的选代器,将仍然按照字节(而不是实际编码的字符)来操作。
总结:
1.string是表示字符串的字符串类
2.该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
3.string在底层实际是:basic_string模板类的别名,typedef basic_string<char,char_traits allocatorstring;
1、4个默认成员函数
#include <string>
void test_string()
{
string s1; //重点常用 构造
string s2("hello"); //重点常用 构造
string s3("hello",2);
string s4(s2); //重点常用 拷贝构造
string s5(s2,1,2);
string s6(s2,1,string::npos);
string s7(s2,1);
string s8(10,"a");
cout<<s1<<endl;//为空
cout<<s2<<endl;//hello
cout<<s3<<endl;//he
cout<<s4<<endl;//hello
cout<<s5<<endl;//el 从第一个开始拷贝2个字符
cout<<s6<<endl;//ello 从第一个开始拷贝到字符的结束
cout<<s7<<endl;//ello 从第一个开始拷贝到字符的结束 第三个参数默认为string::npos;
cout<<s8<<endl;//aaaaaaaaaa 拷贝10个a
s1 = s8; //重点常用 赋值
cout<<s1<<endl;//aaaaaaaaaa 拷贝10个a
}
2、string的遍历
void test_string2()
{
string s1("hello");
s1 += ' ';
s1 += 'world';
cout << s1 << endl;//hello world
//遍历每一个字符
//s1.size()表示字符串的长度
//读
for(size_t i = 0;i<s1.size();==i)
{
cout << s1[i]<<" ";//h e l l o w o r l d;
}
//写
for(size_t i = 0;i<s1.size();==i)
{
s1[i] += 1;
cout << s1[i]<<" ";//i f m m p ! x p s m e
}
//迭代器
//写
string::iterator it = s1.begin();
//áuto it = s1.begin();这种方式也可以
while(it != s1.end())
{
*it -= 1; // i f m m p ! x p s m e -1后变为 h e l l o w o r l d
++it;
}
cout << endl;
//读
it = s1.begin();
while(it != s1.end())
{
cout << *it << " ";
++it;
}
cout << endl;
//范围for 基于他的范围自动去遍历
//原理被替换成迭代器 c++11
for(auto ch :s1)
{
cout << ch <<" ";//h e l l o w o r l d;
}
//获取字符数组首地址,用C字符串的形式遍历
const char * str = s1.c_str();
while(*str)
{
cout<< *str << " ";
}
cout << endl;
cout << s1 <<endl; //调用的string重载的operator<<
cout << s1.c_str()<<endl; // 直接输出const char*
return 0;
}
迭代器是一个像指针的东西 begin()返回的是字符串开始的位置 end()返回结束的位置
3、反向迭代器
void test_string3()
{
string s1("hello world")
//倒着遍历?反向迭代
string::revesrse_iterator rit = s1.rbegin();
while(rit != s1.end())
{
cout << *rit << " ";//d l r o w o l l e h
++rit;
}
}
4、字符串转整型
begin有两种方式接收参数带const和不带const
const 迭代器只能读不能写
int String2int(const string& str)
{
int val = 0;
string::const_iterator it = str.begin();
while(it != str.end())
{
val*=10;
val+= (*it - '0');
++it;
}
return val;
}
int main()
{
string nums("12345");
cout << String2int(nums)<<endl;
return 0;
}
5. capacity增容和find查询和substr取子串
capacity
这个容量不一定等于字符串长度。它可以相等或更大,当向字符串中添加新字符时,额外的空间允许对象优化其操作。
请注意,此容量不假定字符串的长度有限制。当这个容量用完并且需要更多时,它会被对象自动扩展(重新分配存储空间)。字符串长度的理论限制由成员max_size给出。
字符串的容量可以在任何时候修改对象,即使这种修改意味着大小的减少或容量尚未耗尽(这与向量容器中的容量保证形成了对比)。
可以通过调用成员保留来显式地更改字符串的容量。
find
在字符串中搜索由其参数指定的序列的第一个匹配项
substr
返回一个新构造的字符串对象,该对象的值初始化为此对象的子字符串的副本
void test_string4()
{
string s1("hello world")
cout << s1.capacity()<<endl;//15个字节
s1+="111111";
cout << s1.capacity()<<endl;//31个 基本上按照1.5倍去增容
s1.clear();//删除字符串的内容,该字符串将变为空字符串(长度为0个字符)。
cout << s1.capacity()<<endl;//删除字符串的内容 capacity的空间不变
size_t pos1 = s1.find('.');//在字符串中搜索由其参数指定的序列的第一个匹配项
if(pos1 != string::npos)
{
cout << s1.substr(pos1) << endl;
}
域名切割
void spliturl (const string& url)
// 分离出url 协议 域名资源名称
//http: www.cplusplus.com/reference/string string rfind
// http://2019ncov.chinacdc.cn/2019-nCoV/
size_t il = url.find(':’);
if(il != string::npos)
{
cout << url.substr(0, i1) << endl;
}
size_t i2 = url.find('/', il + 3);
if(i2 != string::npos)
{
cout << url.substr(il + 3,i2 -(i1 + 3))<< endl;
}
cout<< urlsubstri + 1) << endl;
6.string中的 reserve()和 resize()
reserve() 为容器预留足够的空间,避免不必要的重复分配。预留空间大于等于字符串的长度。预留空间可以通过capacity()查看。
resize() 调整字符串的大小。如果字符串长度变小,多余的字符会被截掉。若长度变大,可以设置填充的字符。长度可以通过size()查看。
6.1 reserve()
string str = "April";
str.reserve(20);
str的预留空间为15。
调用函数reserve()后,预留空间变为31。(内存按照(n*16-1)分配)
6.2 resize()
字符串str的长度变短,多余的字符被裁掉了。也可以增容
string str = "April";
str.resize(3);
字符串str的长度变长,使用字符 ‘.’填充。(默认填充空字符)
string str = "April";
str.resize(10,'.');
6.3 string中的插入删除字符
push_back() 将字符追加到字符串的末尾,使其长度增加一。
append() 通过在字符串当前值的末尾附加附加字符来扩展字符串
insert() 字符串中插入其他字符
erase()擦除字符串的一部分,缩短其长度
void test_string5()
{
string s1("hello world")
s1.push_back('x');//hello worldx
s1.append("111111");//hello worldx111111
s1+='x';//hello worldx111111x //推荐用加等
s1+="1xxxxx";//hello worldx111111x1xxxxx
s1.insert(s1.begin(),'0')//0hello worldx111111x1xxxxx
s1.erase(1,2);//0hlo worldx111111x1xxxxx
s1.erase(1);// 0
}
二、模拟实现string类
2.1实现一个简单的string类
//实现一个简单的string类
namespace zh
{
class string
{
public:
/*string()
:str(new char[1])
{
str[0] = '\0';
}
string(char* strs)
:str(new char[strlen(str)+1])
{
strcpy(str, strs) ;
}*/
string(char* strs = " ")
:_str(new char[strlen(strs) + 1])
{
strcpy(_str, strs);
}
string(const string& s)
:_str(new char[strlen(s._str)+1])
{
strcpy(_str, s._str);
}
~string()
{
delete[] _str;
_str = nullptr;
}
size_t size() { return strlen(_str); }
char& operator[](size_t i) { return _str[i]; }
string& operator = (const string& s)
{
if (&s != this)
{
//开辟一个和s一样大的空间
char* pStr = new char[strlen(s._str) + 1];
strcpy(pStr, s._str);//然后拷贝给当前的
delete[] _str;//释放旧的空间
_str = pStr;//然后指向新开的空间
}
return *this;
}
const char* c_str() { return _str; }
private:
char* _str;
};
void testString1()
{
string s0("hello");
string s1(s0);
cout << s0.c_str() << endl;
cout << s1.c_str() << endl;
}
}
using namespace zh;
int main()
{
testString1();
return 0;
}
2.2实现一个支持增删改查的string
//实现一个支持增删改查的string
namespace zh
{
class string
{
public:
typedef char* iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
string(const char* str ="")
:_str(new char[strlen(str)+1])
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
string(const string& s)
:_str(nullptr), _size(0), _capacity(0)
{
string tmp(s_str);
this->swap(tmp);
}
void swap(string& s)
{
::swap(_str, s._str);
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
string& operator=(const string& s)
{
this->swap(s);
return *this;
}
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
size_t size() const { return _size; }
size_t capacity() { return _capacity; }
const char* c_str() { return _str; }
const char& operator[](size_t i) const { assert(i < _size); return _str[i]; }
void push_back(char ch)
{
/*if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;
reverse(newcapacity);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';*/
insert(_size, ch);
}
void reverse(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete _str;
_str = tmp;
_capacity = n;
}
}
void resize(size_t n,char ch = '\0')
{
if (n < _size)
{
_str[n] = '\0';
_size = n;
}
else
{
if (n > _capacity)
{
reverse(n);
}
for (size_t i = 0; i < _size; i++)
{
_str[i] = ch;
}
_size = n;
_str[_size] = '\0';
}
}
void append(const char* ch)
{
/*size_t len = strlen(ch);
if (_size + len > _capacity)
{
reverse(_size + len);
}
strcpy(_str+ _size, ch);
_size += len;*/
insert(_size, ch);
}
string& operator+=(char ch)
{
this->push_back(ch);
return *this;
}
string& operator+=(const char* ch)
{
this->append(ch);
return *this;
}
string& insert(size_t pos,char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;
reverse(newcapacity);
}
int end = _size;
while (end >= pos)
{
_str[end + 1] = _str[end];
--end;
}
_str[end + 1] = ch;
++_size;
return *this;
}
string& insert(size_t pos,const char* ch)
{
assert(pos <= _size);
//如果空间不够先增容
size_t len = strlen(ch);
if (_size + len > _capacity)
{
reverse(_size + len);
}
//挪动数据
size_t end = _size;
while (end >= pos)
{
_str[end + len] = _str[end];
--end;
}
/*for (size_t i = 0; i < len; i++)
{
_str[pos++] = ch[i++];
}*/
strncpy(_str + pos, ch, len);
_size += len;
return *this;
}
void erase(size_t pos, size_t len = npos)
{
if (len >= _size - pos)
{
_str[pos] = '\0';
_size = pos;
}
else
{
size_t i = pos + len;
while (i <= _size)
{
_str[i - len] = _str[i];
++i;
}
_size -= len;
}
}
size_t find(char ch, size_t pos =0)
{
for (size_t i = pos; i < _size; ++i)
{
if (_str[i] == ch);
return i;
}
return npos;
}
size_t find(const char* str, size_t pos = 0)
{
char* p = strstr(_str, str);
if (p == nullptr)
{
return npos;
}
else
{
return p - str;
}
}
bool operator < (const string& s)
{
int ret = strcmp(_str, s._str);
return ret < 0;
}
bool operator == (const string& s)
{
int ret = strcmp(_str, s._str);
return ret == 0;
}
bool operator<=(const string& s)
{
return *this < s || *this == s;
}
bool operator > (const string& s)
{
return !(*this < s) ;
}
bool operator > (const string& s)
{
return !(*this < s);
}
bool operator >=(const string& s)
{
return !(*this < s);
}
bool operator !=(const string& s)
{
return !(*this == s);
}
private:
char* _str;
size_t _size;
size_t _capacity;
static size_t npos;
};
size_t string::npos = -1;;
ostream& operator << (ostream& out, const string& s)
{
for (size_t i = 0; i < s.size(); i++)
{
cout << s[i];
}
return out;
}
istream& operator >> (istream& in, string& s)
{
while (1)
{
char ch;
ch = in.get();
if (ch == ' ' || ch == '\n')
break;
else
s += ch;
}
return in;
}
void testString()
{
string s1("hello");
}
}
using namespace zh;
int main()
{
testString();
return 0;
}
三、学习语法后做一道简单的算法题
917. 仅仅反转字母 点这里
387. 字符串中的第一个唯一字符 点这里
总结
c++中有很多语法,不知道的可以在这个网站查询:https://cplusplus.com/