• C++之string(2)


    1.内容回顾

    string是C++标准库里的东西,严格来说不属于STL。所以说大家会看到STL里面,它把一些数据结构划分在Containers,也就是容器里面。但是string没有在这个地方,string在Miscellaneous headers里。STL是C++标准库的一部分,它是关于算法和数据结构的库。
    在这里插入图片描述
    string类是basic_string类模板的一个实例化,它使用char(即bytes)作为其字符类型。basic_string类模板还可以用wstring、u16string、u32string 作为其字符类型,能很好解决不同编码的问题。
    在这里插入图片描述basic_string类模板大概原型:为了支持增删查改,所以不会写成_str = str,大家结合string的底层来理解。

    template<class T>
    class basic_string
    {
    public:
    	basic_string(const T* str)
    	{
    		// 开空间存储字符串,方便增删查改
    		size_t len = strlen(str);
    		_str = new T[len + 1];
    		strcpy(_str, str);
    	}
    	//引用也是为了方便修改,减少拷贝
    	T& operator[](size_t pos)
    	{
    		return _str[pos];
    	}
    private:
    	T* _str;
    	size_t _size;
    	size_t _capacity;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    如果string对象是const限定的,则该函数返回一个const T&

    const T& operator[] (size_t pos) const;
    
    • 1

    string的构造:

    void Teststring()
    {
    string s1; // 构造空的string类对象s1
    string s2("hello world!"); // 用C格式字符串构造string类对象s2
    string s3(s2); // 拷贝构造s3
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    string的遍历方式:

    #include <string>
    #include <iostream>
    using namespace std;
    int main()
    {
    	string s("hello world");
    	//s += "hello";
    	// 1:下标+[]
    	//s[12];
    	for (size_t i = 0; i < s.size(); ++i)
    	{
    		s[i] += 1;
    		cout << s[i] << " ";
    	}
    	cout << endl;
    
    	// 2: 迭代器 -- 像指针一样的访问数据结构的东西
    	string::iterator it = s.begin();
    	while (it != s.end())
    	{
    		*it -= 1;
    		cout << *it << " ";
    
    		++it;
    	}
    	cout << endl;
    	
     // 3:范围for--语法糖 底层:编译器替换成了迭代器
    	for (auto e : s)
    	{
    		cout << e << " ";
    	}
    	cout << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    注:C语言对越界的检查是抽查,有时候不一定能检查出来。string类对越界的检查很严格,只要越界程序必然崩溃(实际上是用assert来实现)。

    2. operator+=和insert

    学过数据结构的同学都知道insert必然是不高效的,因为它在插入数据之前一定会挪动数据。如果空间不够甚至还会扩容。
    在这里插入图片描述
    在这里插入图片描述
    槽点:string的insert,pos下标支持字符串不支持字符,迭代器支持字符不支持字符串,诶。

    void test_string1()
    {
    	string s("hello");
    	s += ' ';
    	s += "world";
    	cout << s << endl;
    
    	s.insert(0, 1, 'x');
    	s.insert(s.begin(), 'y');
    	cout << s << endl;
    
    	s.insert(3, 1, 'x');
    	s.insert(s.begin()+3, 'y');
    	cout << s << endl;
    
    	s.insert(0, "sort ");
    	cout << s << endl;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    3. erase

    设计有点冗余,其实大家记住第一种用法就基本够用了。
    在这里插入图片描述
    缺省值:npos
    在这里插入图片描述
    如果第二个参数不给,默认是整型的最大值。如果长度超出剩余字符的长度,就从pos位置开始把剩余字符删完。

    void test_string2()
    {
    	string s("hello world");
    	cout << s << endl;
    
    	s.erase(s.begin());
    	cout << s << endl;
    
    	s.erase(s.begin()+3);
    	cout << s << endl;
    
    	s.erase(3, 2); 
    	cout << s << endl;
    
    	//s.erase(3);
    	s.erase(3, 100);
    	cout << s << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述

    4. swap

    在这里插入图片描述
    大家猜一猜下面两种交换方式有什么区别:

    void test_string3()
    {
    	string s1("hello world");
    	string s2("string");
    
    	// C++98
    	s1.swap(s2);   // 效率高
    	//STL库中的函数模板,能支持任意类型的交换
    	swap(s1, s2);  // 效率低
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    5. c_str

    在这里插入图片描述
    返回一个指向数组的指针,该数组包含一个以空结束的字符序列(即C-string),表示string对象的当前值, 能很好的和C语言(如strcpy)的一些接口兼容配合。

    void test_string4()
    {
    	string s1("hello world");
    	cout << s1 << endl;
    	cout << s1.c_str() << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    6. find、rfind、substr

    返回第一次匹配的第一个字符的位置。如果没有找到匹配,函数返回string::npos。
    在这里插入图片描述
    返回最后一次匹配的第一个字符的位置。 如果没有找到匹配,函数返回string::npos。
    在这里插入图片描述
    返回具有此对象的子字符串的字符串对象(从pos位置返回len长度的子字符串)。
    在这里插入图片描述

    6.1 取出文件后缀

    • 只有 一个后缀:
    void test_string5()
    {
    	// 要求取出文件的后缀
    	string file("string.cpp");
    	//string file("string.c");
    	
    	size_t pos = file.find('.');
    
    	if (pos != string::npos)
    	{
    		string suffix = file.substr(pos, file.size() - pos);
    		//string suffix = file.substr(pos);
    		cout << file << "后缀:" << suffix << endl;
    	}
    	else
    	{
    		cout << "没有后缀" << endl;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    • 多个后缀:倒着找
    void test_string6()
    {
    	// 要求取出文件的后缀
    
    	string file("string.c.tar.zip");
    
    	size_t pos = file.rfind('.');
    
    	if (pos != string::npos)
    	{
    		//string suffix = file.substr(pos, file.size() - pos);
    		string suffix = file.substr(pos);
    
    		cout << file << "后缀:" << suffix << endl;
    	}
    	else
    	{
    		cout << "没有后缀" << endl;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    6.2 分离协议、域名和URI

    void test_string7()
    {
    	// 取出url中的域名
    	string url1("http://www.cplusplus.com/reference/string/string/find/");
    	string url2("https://leetcode.cn/problems/design-skiplist/solution/tiao-biao-probabilistic-alternative-to-b-0cd8/");
    
    	string& url = url1;
    
    	// 协议 域名 uri
    	string protocol;
    	size_t pos1 = url.find("://");
    	if (pos1 != string::npos)
    	{
    		//pos下标(:)就是协议字符的个数
    		protocol = url.substr(0, pos1);
    		cout << "protocol:" << protocol << endl;
    	}
    	else
    	{
    		cout << "非法url" << endl;
    	}
    
    	string domain;
    	//第二个参数表示从":"位置+3开始找
    	size_t pos2 = url.find('/', pos1+3);
    	if (pos2 != string::npos)
    	{
    		domain = url.substr(pos1+3, pos2 - (pos1+3));
    		cout << "domain:" << domain << endl;
    	}
    	else
    	{
    		cout << "非法url" << endl;
    	}
    
    	string uri = url.substr(pos2 + 1);
    	cout << "uri:" << uri << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    string& url = url1;
    在这里插入图片描述
    string& url = url2;
    在这里插入图片描述

    7. 关系运算符重载

    在这里插入图片描述
    其实没必要提供这么多,接口。就比如operator<如果不提供后面两个,我们也可以用构造函数来进行比较。
    关系运算符不能用函数模板,因为每一个类的比较规则都不一样。
    swap可以用函数模板是因为不管什么类型都是借助第三个变量来实现交换。
    在这里插入图片描述

    8. getline

    在这里插入图片描述
    字符串最后一个单词的长度
    在这里插入图片描述
    C语言的scanf和C++的cin都是默认多个数据以空格或者\n间隔。如果缓冲区有以空格分割的多个数据,cin和scanf是取不完的,碰到第一个空格就结束了。此时getline就能很好地解决这个问题。

    #include <iostream>
    #include <string>
    using namespace std;
    
    int main()
    {
        string str;
        getline(cin,str);
        size_t pos = str.rfind(" ");
        if(pos != string::npos)
        {
            cout<<str.size()-(pos+1)<<endl;
        }
        else
        {
            cout<<str.size()<<endl;
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    9. 练习题

    9.1 字符串中的第一个唯一字符

    字符串中的第一个唯一字符
    在这里插入图片描述
    利用哈希的思想,因为只包含小写字母,所以我们开一个26大小的数组。

    class Solution {
    public:
        int firstUniqChar(string s) {
            int count[26] = {0};
            //统计每个字符出现的次数
            for(auto ch : s)
            {
                count[ch-'a']++;
            }
    
            for(size_t i = 0; i < s.size(); ++i)
            {
                if(count[s[i]-'a'] == 1)
                return i;
            }
    
            return -1;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    9.2 字符串相加

    字符串相加
    在这里插入图片描述
    大数运算,注意考虑进位,进位初始化为0.如果大于9,进位就为1,每次进位后记得重置为0.最后把运算结果头插。由于string的insert效率极低(持续的头插是n^2),考虑尾插后再逆置(O(n))。如果两个个位数相加的结果大于9,while循环结束后还得再加上进位的1.
    注:字符0的ASCII是48,字符数字要与’0’相减换算成对应的值。
    在这里插入图片描述

    class Solution {
    public:
        string addStrings(string num1, string num2) {
            int end1 = num1.size()-1;
            int end2 = num2.size()-1;
            int carry = 0;
    
            string retstr;
            while(end1 >= 0 || end2 >= 0)
            {
                int val1 = end1 >= 0 ? num1[end1]-'0' : 0;
                int val2 = end2 >= 0 ? num2[end2]-'0' : 0;
                int ret = val1 + val2 + carry;
                if(ret > 9)
                {
                    ret -= 10;
                    carry = 1;
                }
                else
                {
                    carry = 0;
                }
                retstr += ('0' + ret);
                --end1;
                --end2;
            }
    
            if(carry == 1)
            retstr += '1';
    
            reverse(retstr.begin(), retstr.end());
    
            return retstr;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
  • 相关阅读:
    C语言指针(习题篇)
    LeetCode(cai鸟之路)139. 单词拆分
    基于随机森林实现特征选择降维及回归预测(Matlab代码实现)
    vue3状态管理工具pinia的插件书写,pinia全局错误处理插件安排
    七种常见的电子邮件安全协议简析
    day08|字符串题目part01
    【分布式-1】分布式理论
    单线程传奇Redis,为何引入多线程?
    使用HTML制作静态网站作业——我的校园运动会(HTML+CSS)
    express中间件next函数在作用
  • 原文地址:https://blog.csdn.net/iwkxi/article/details/125544094