c++(String)

阅读: 评论:0

c++(String)

c++(String)

STL六大组件

仿函数、算法(查找、归并、分类)、迭代器(iterator)、空间配置器(allocator和内存池先(只申请空间但是并没有初始化,避免频繁从堆中申请内存影响效率))、容器(string、vector)、配接器

string

c语言的字符串不能很好的按需进行修改,比如因为字符串一般存储在字符数组当中,而字符数组的大小是定死的,如果要对字符数组里面的内容进行调整可能会受限,所以有了string这个类型,但是其底层管理的依旧还是字符数组,支持增删查改。

stirng也是类模板typedef过来的:typedef basic_string<char> string

basic_string<T>:这个类模板发展了许多的类,来应对不同的需要。如下:typedef basic_string<wchar_t> wstring 

typedef basic_string<char32_t> u32string 

typedef basic_string<char16_t> u16string

一个汉字可以看成ASCII码表上的一个字符,但只用一个字节表示汉字,由于状态只有256种太少不适用->unicode(万国):UTF-8(一个字节为单位,可以用1个字节或者2个、3个、4个字节来编码,对不同范围的字符是同不同长度的编码。比如常见的汉字采用2个字节,生僻的汉字采用4个字节)、UTF-16、UTF-32(更加的统一但是也更加的浪费)。

int main()
{char str1[] = "apple";char str2[] = "高手";cout << sizeof(str1) << endl;//6cout << sizeof(str2) << endl;//5,看下面的监视我么们可以发现每一个汉字需要两个字符表示,而每个字符都是负数,这是因为ASCII码用了正数的部分,导致如果想要兼容ASCII的话那么汉字编码的部分只能使用负数部分,如果和ascii码混用的话,会有问题,我们拿到两个字符,到底是对应着一个汉字,还是两个ASCII符号。现在我们可以拿到一个字符,看第一个bit位的数值,如果是0对应着ASCII码,如果是1就和下面一个字符联合对应一个汉字。return 0;
}

int main()
{char str2[] = "高手";str2[3]--;cout << str2 << endl;str2[3]--;cout << str2 << endl;str2[3]--;cout << str2 << endl;str2[3]--;cout << str2 << endl;str2[3]--;cout << str2 << endl;str2[3]--;cout << str2 << endl;return 0;
}

由上可知,gbk编码是将同音字编码到一块的。

 string使用

int main()
{string s1;//无参构造string()string s2("hello world");//string (const string& str)string s3 = "hello world";//单参数构造函数可以直接赋值,因为支持隐式类型转换:构造+拷贝=>构造string s4(s3, 6, 3);//string (const string& str, size_t pos, size_t len = npos)从下标pos位置开始拷贝npos长度cout << s4 << endl;string s5(s3, 6, 13);//npos超过s5的末尾时,停止拷贝cout << s5 << endl;string s6(s3, 6);//static const size_t npos = -1;相当于将32bit的1赋值给一个无符号整形,解读成42亿多cout << s6 << endl;string s7("hello world", 5); //string(const char* s, size_t n)拷贝字符串s的前n个位置的值cout << s7 << endl;string s8(10, '*');//string (size_t n, char c)字符串填充n个字符ccout << s8 << endl;for (size_t i = 0; i < s2.size(); ++i){s2[i]++;//[]运算符重载}cout << s2 << endl;for (size_t i = 0; i < s2.size(); ++i){cout << s2[i] << " ";}cout << endl;return 0;
}

int main()
{string s1("hello world");cout << s1.size() << endl;cout << s1.length() << endl;//length和size的结果是一样的,功能一样,size()之所以出现是为了和stl库的其它结构保持一致cout << s1.max_size() << endl;//意义不大cout << s1.capacity() << endl;//capacity是不包含'/0'的,但实际上是开了''的空间。也就是如果结果是15,实际上开了16个字节的空间return 0;
}
int main()
{string s1("hello");s1.push_back(' ');s1.push_back('!');//插入一个字符cout << s1 << endl;s1.append("world");//插入一个字符串cout << s1 << endl;s1 += ' ';s1 += '!';s1 += "world";//其实+=的底层实现还是push_back和appendcout << s1 << endl;
}
class string
{
private:char* _ptr;char _buf[16];size_t _size;size_t _capacity;
};//其实string结构和这个比较类似,当需要存储的字符串空间比较小的时候直接存在_buf数组当中,相当于浪费了一个ptr指针,但是ptr指针指向的堆区空间此时是不需要开辟的
//当需要存储的字符串的大小大于16的时候就不存储在_buf当中了,直接存储在ptr所指向的堆区空间上了,这个时候浪费了一个16个字节大小的字符数组,这也是x86环境下sizeof(string)的大小是28=12+char _buf[16]的原因
int main()
{// 观察扩容情况  -- 1.5倍扩容string s;cout << sizeof(s) << endl;//28cout << "making s grow:n";size_t sz = s.capacity();cout << "capacity changed: " << sz << 'n';for (int i = 0; i < 100; ++i){s.push_back('c');if (sz != s.capacity()){sz = s.capacity();cout << "capacity changed: " << sz << 'n';}}return 0;

 由下图可知第一次的capacity是15,但是根据string的类结构我们可以知道这并不是额外开辟的,而是本身结构自带的,而且也应证了capacity是不计算''的。所以第一次正经在堆区开辟的空间的大小其实是32。但是不同的编译器扩容的原理是不一样的 。

reserve(100)只会改变capacity但是不会改变size,也就是将capacity扩容到100。

resize(100)是size和capacity都会编程100,空间填入缺省值''。也可以s1.resize(100,'x'),指定用字符'x'来填充capacity。可以理解为扩容+初始化。也可以用来缩小size&#size(5)

就是只保留5个,而capacity是不会动的。这是因为缩容是不支持原地缩容,如果想要原地缩容就必须支持释放部分空间,但是这在底层是不支持的,会增加内存管理的难度,缩容是开辟一块新的空间再按需拷贝部分数据,这样的实现代价是非常大的。

 迭代器:

迭代器对于string这个容器而言和[]下标或者范围for比较,用起来并不是那么的方便。但是string必须要有迭代器,目的是为了和其他的容器在使用上保持异种一致。就好像string有了length()函数之后为什么还要有capacity()函数。[]下标在string容器比较好用,但是如果更换了容器就不一定了。

int main()
{string s1("hello world");string::iterator it = s1.begin();while (it != s1.end())//采用迭代器进行遍历。end是最后字符串最后一个有效字符的下一个位置,类似于c语言字符串的''{cout << *it << " ";++it;}cout << endl;for (auto ch : s1)//采用范围for来进行遍历,但是auto的底层实现和迭代器是一样的{cout << ch << " ";}cout << endl;return 0;
}
///
int main()
{string s1("hello world");string::reverse_iterator rit = s1.rbegin();//反向迭代器,从后向前,而且反向迭代器的类型名称又发生了变化while (rit != s1.rend())//rbegin是最后一个有效字符的位置,也就是d的位置。而rend的位置是第一个字符的前一个位置{cout << *rit << " ";++rit;//注意是++}
}
///
void Func(const string& s)
{// 遍历和读容器的数据,不能写,只能读取string::const_iterator it = s.begin();//此时迭代器的类型变成了const类型的的string的迭代器:const_iterator begin() const;while (it != s.end()){cout << *it << " ";++it;}cout << endl;auto rit = s.rbegin();while (rit != s.rend())//时迭代器的类型变成了const类型的的string的反向迭代器:string::const_reverse_iterator rit = s.rbegin();{cout << *rit << " ";++rit;}cout << endl;
}
//迭代器的类型在这里共有4种。const做为关键字修饰定义出来的对象和变量是不可修改的

at()和[]的区别

int main()
{string s1("hello world");try{s1.at(100);//越界了是抛异常,处理方式相对比较合理。si[100];//越界之后是采用的assert断言,所以程序会直接中断,比较激进}catch (const exception& e){cout << e.what() << endl;}return 0;
}

int main()
{//insert/erase不推荐经常使用,能少用就少用。因为他们可能都存在要挪动数据,效率低下。删除的话如果全部删除还好说,但是如果只是删除中间的部分的话代价就会非常的大string s1("world");s1.insert(0, "hello");cout << s1 << endl;s1.insert(5, 1, ' ');//string& insert (size_t pos, size_t n, char c)从下标pos开始插入n个字符ccout << s1 << endl;s1.insert(5, " ");//string& insert(size_t pos, const char* s)从下标pos开始插入字符串scout << s1 << endl;s1.insert(s1.begin()+5, ' ');//从begin()+5的位置开始插入cout << s1 << endl;string s2("hello world");s2.erase(5, 1);//string& erase (size_t pos = 0, size_t len = npos);cout << s2 << ase(s2.begin() + 5);//iterator erase (iterator p)cout << s2 << endl;string s3("hello world");s3.erase(5, 30);cout << s3 << endl;string s4("hello world");s4.erase(5);//string& erase (size_t pos = 0, size_t len = npos)这里采用缺省值直接全删除了cout << s4 << endl;return 0;
}

 replace函数并不建议,因为会涉及到空间不够扩容和挪动数据,效率不够

int main()
{string s1("hello world");s1.replace(5, 1, "%%d");cout << s1 << endl;string s2("hello world i love you");size_t num = 0;for (auto ch : s2)//提前统计空格的数量{if (ch == ' ')++num;}s2.reserve(s2.size() + 2 * num);//提前开空间,避免repalce时扩容size_t pos = s2.find(' ');while (pos != string::npos)//string::npos是静态局部常量static const size_t,{s2.replace(pos, 1, "%20");pos = s2.find(' ', pos + 3);//减少重复搜索,直接从上次搜索的地方开始}cout << s2 << endl;// 下面是采用空间换时间的方法string s3("hello world i love you");string newStr;num = 0;for (auto ch : s3){if (ch == ' ')++num;}serve(s3.size() + 2 * num);for (auto ch : s3){if (ch != ' ')newStr += ch;elsenewStr += "%20";}cout << newStr << endl;return 0;
}

 String::swap(void swap (string& str))和swap的区别?

string::swap是根据string类定义的成员函数,所以更有针对性,可以采用更高效的方法进行交换,直接交换两个string对象中_ptr的指向就可以了。但是标准库里面的swap泛函数效率就比较低了,是 通过新建一个临时变量进行swap的。

int main()
{string s1("hello world");cout << s1 << endl;//const char* c_str() const:调用的是string的<<运算符重载函数cout << s1.c_str() << endl;//调用的是iostream库里面的<<运算符重载cout << (void*)s1.c_str() << endl;//这里打印的是地址cout << s1 << endl;cout << s1.c_str() << endl;s1 += '';s1 += '';s1 += "xxxxx";cout << s1 << endl;//是根据s1的size来进行打印的cout << s1.c_str() << endl;//打印字符串,遇到''就截止了//c_str()函数的作用:为c语言提供一个函数接口,更好的兼容。虽然在有些时候功能有些重合string filename("test.cpp");//此时fopen是没有办法打开string类的FILE* fout = fopen(filename.c_str(), "r");if (fout == nullptr)perror("fopen fail");char ch = fgetc(fout);while (ch != EOF){cout << ch;ch = fgetc(fout);//不写的话会造成死循环,也就是一直读取同一个字符}fclose(fout);return 0;
}

int main()
{string file("string.cpp.tar.zip");size_t pos = file.rfind('.');//从后向前找if (pos != string::npos){string suffix = file.substr(pos);//string substr (size_t pos = 0, size_t len = npos) const:从pos位置开始读取len长度的字符cout << suffix << endl;}string url("/");cout << url << endl;size_t start = url.find("://");//find找字符串的位置if (start == string::npos){cout << "invalid url" << endl;}start += 3;size_t finish = url.find('/', start);string address = url.substr(start, finish - start);cout << address << endl;return 0;
}

int main()
{std::string str("Please, replace the vowels in this sentence by asterisks.");std::size_t found = str.find_first_of("abcdv");//从头找str中"abcdv"中的任意一个字符就返回下标。find_last_not_of就是从str的最末尾开始向前找while (found != std::string::npos){str[found] = '*';found = str.find_first_of("abcdv", found + 1);//优化防止从头开始找}std::cout << str << 'n';string s1("hello world");string s2("hello world");s1 == s2;s1 == "hello world";"hello world" == s1;return 0;
}

 

本文发布于:2024-02-01 11:47:58,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170675928036379.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

上一篇:数据结构笔记
下一篇:C++初阶
标签:String
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23