• string类详解


    1:构造string类

    1.1:方法

    • string() :构造一个空字符串对象
    • string(const char* str) :用C-String构造对象
    • string(size_t n, char c):string类对象中包含n个c
    • string(const string& str):拷贝构造对象

    1.2:测试

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

    image-20221101094248937

    2:size和length

    2.1:用途

    都是求字符串对象有效字符个数

    返回值是size_t

    2.2:测试

    string test4("hello world");
    size_t size1 = test4.size();//有效字符长度 返回值是size_t
    size_t size2 = test4.length();//有效字符长度 返回值是size_t
    
    • 1
    • 2
    • 3

    image-20221101094506008

    均为11,11位hello + 空格 + world的长度,不包含\0,而为什么会有length和size的名称差别呢,因为在数据结构中对于链表和顺序表的结构,length可以表明长度,但是在树等结构中用size可以更直观的表明大小。

    3:capacity

    3.1:用途

    求字符串对象的容量

    3.2:测试

    	string str1;
    	string str2("abc");
    	size_t c1 = str1.capacity();
    	size_t c2 = str2.capacity();
    
    • 1
    • 2
    • 3
    • 4

    image-20221101095316537

    4:clear

    4.1:用途

    清除字符串中的有效字符

    4.2:测试

    	string str("abcdefg");
    	str.clear();
    	cout << str << endl;
    
    • 1
    • 2
    • 3

    image-20221101095738756

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

    image-20221101095912474

    容量是没有变化的,还是15,因此clear只是清空string中有效字符,不改变底层空间大小。

    5:empty

    5.1:用途

    判断字符串是否为空,空返回true,否则返回false。

    5.2:测试

    	string str1("abcdefg");
    	string str2;
    	bool k1 = str1.empty();
    	bool k2 = str2.empty();
    	return 0;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    image-20221101100236689

    6:reserve

    6.1:用途

    为string对象预留空间

    6.2:测试

    string str("abcd");
    str.reserve(20);
    size_t k = str.capacity();//不预留的话是15
    
    • 1
    • 2
    • 3

    image-20221101100751436

    容量变成了31,因此针对我们用c语言实现的数据结构中,涉及多次扩容的并且暂时知道数据容量多少的时候,先提前reserve好空间比较好。

    7:resize

    7.1:用途

    将有效字符变成n个,查询cplusplus文档得知。

    image-20221101101301572

    有2种用法,用空字符填充和用指定字符填充。

    既然是更改字符的数量,那么肯定有3种情况。

    • n在size和capacity中间 插入数据,闭区间
    • n<size 删除数据
    • n>capacity 扩容,补充数据

    7.2:测试

    	string st1("hello wjw");
    	st1.resize(5);
    	cout << st1.size() << endl;
    	cout << st1.capacity() << endl;
    	cout << st1 << endl<
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    st2和st3的resize差别是在于填充的字符是空字符和*的区别。

    image-20221101102730810

    可以看到当n<size的时候,直接返回前n个字符。不修改容量

    size<=n<=capacity的时候,插入数据,不修改容量

    capacity<n的时候,扩容,再插入数据。

    8:string的三种遍历

    8.1:方法一 for循环和[]重载

    	for (size_t i = 0; i < str.size(); i++)
    	{
    		cout << str[i] << endl;
    	}
    
    • 1
    • 2
    • 3
    • 4

    用[]重载

    8.2:方法二 迭代器

    	string::iterator it = str.begin();
    	while (it != str.end())
    	{
    		cout << *it << endl;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    c++中string类中存在迭代器。begin和end获取一个字符的迭代器,begin是字符串第一个字符,end是最后一个字符的下一个位置的迭代器

    同时还有反向迭代器,反向遍历。

    	string::reverse_iterator rit = str.rbegin();
    	while (rit != str.rend())
    	{
    		cout << *rit << endl;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在c++11中,可以直接使用auto关键词推导迭代器类型。

    	auto it = str.begin();
    	while (it != str.end())
    	{
    		cout << *it << endl;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    8.2.1:const迭代器

    注意以下情况,如果我们要求只读一个字符串的时候,如何使用迭代器?比如

    void test(const string& str)
    {
    	string::iterator it = str.begin();
    	while(it != str.end())
    	{
    	cout<<*it<
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    image-20221101110123317

    编译器报错,说不能从const的迭代器转换到非const的迭代器。

    查询文档得知。

    image-20221101110231449

    设计了2种迭代器。

    当我们只读的时候,应使用string::const_iterator的格式

    同样的针对第一种[]遍历,

    image-20221101110348803

    也有2种,const和非const。

    	const string str("abcdefg");
    	for (size_t i = 0; i < str.size(); i++)
    	{
    		str[i] += 1;
    		cout << str[i] << endl;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    当给定的是常量字符串对象,[]就会调用const类型的,如果试图修改字符串,就会报错。

    image-20221101110554711

    8.2.2:迭代器和[]的结论

    针对只读的功能函数,设计的时候提供const版本即可

    针对只写的功能函数,设计的时候提供非const版本即可

    针对读写的功能函数,设计的时候提供const和非const版本

    8.3范围for

    	string str("abcd");
    	for (auto ch : str)
    	{
    		cout << ch;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    9:push_back

    9.1:功能

    尾插一个字符,会扩容

    9.2:测试

    	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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    image-20221101113643080

    每次大概是1.5倍的扩容,

    10:append

    10.1:用法

    image-20221101114221684

    在字符串末尾添加字符串

    10.2:测试

    	string str("abcd");
    	str.append("hello");
    	string str2("bit");
    	str.append(str2);
    	cout << str << endl;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    可以追加一个字符串对象,也可以直接追加字符串。

    image-20221101114505273

    11:operator +=

    11.1:用法

    追加字符串

    11.2:测试

    string str("abcd");
    str += "hello bit";
    string str2("abcd");
    str += str2;
    cout << str << endl;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    12:字符串相加例题

    image-20221101115935772

    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;
    }
    };
    
    • 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

    13:reverse

    13.1:用法

    逆置字符串,注意!区间为左闭右开!,所以reverse(a,a+1)没有意义!

    13.2:测试

    	string str("hello wjw");
    	reverse(str.begin(), str.end());
    	cout << str << endl;
    
    • 1
    • 2
    • 3

    image-20221101121415411

    是给2个迭代器,逆置2个迭代器范围内的数据。

    14:find

    14.1:用法

    查找第一个符合的字符,并且返回他的下标

    image-20221101122120680

    image-20221101122159789

    如果没有查找到,返回npos

    image-20221101122224310

    所以在查找的时候一般可以让查找值是否等于-1来判断是否找到

    14.2:测试

    	string str("hello wjw");
    	size_t pos = str.find('j');
    	if (pos != -1)
    	{
    		cout << str[pos] << endl;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    设置下一次查找的位置在第一次找到的pos之后一个位置即可。

    15:rfind

    15.1:用法

    反向查找第一个符合的字符,并且返回其下标。

    16:substr

    16.1:用法

    返回pos位置开始的后续的len个字符

    image-20221101123711605

    如图len是给了缺省值npos的,npos是size_t类型,因此如果我们不传参len的话,是相当于直接返回pos开始直到字符串结束的所有字符

    因此比如我们想取一个文件的后缀名的时候,就可以使用这个

    	string file("test.cpp");
    	size_t pos = file.rfind('.');
    	string str = file.substr(pos);
    	cout << str << endl;
    
    • 1
    • 2
    • 3
    • 4

    17:find_first_of

    17.1:用法

    image-20221101124352994

    在string里面查找与给定的字符c或者字符串相符合的 string里面的第一个字符的下标。

    17.2:测试

    	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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    image-20221101125030273

    18:erase

    18.1:用法

    删除pos位置的字符

    image-20221101125134275

    第一个是删除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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在数据结构中顺序表我们学过,头删是要挪动数据的,因此我们尽量少用。时间复杂度为O(N^2)

    19:c_str

    19.1:用法

    返回一个c语言中的数组,该数组为c字符串,包含\0

    19.2:测试

    string str("wo ai ni ge chou sha bi");
    char* str1 = new char[str.size() + 1];
    strcpy(str1, str.c_str());
    cout << str1 << endl;
    
    • 1
    • 2
    • 3
    • 4

    image-20221101130326691

    20:assign

    20.1:用法

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

    image-20221101133216624

    20.2:测试

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

    image-20221101132501685

    如果是变小,容量不变

    	string str("wo ai ni ge chou sha bi");
    	cout << str.capacity()<< endl;
    	str.assign("abcdadsadasdsadasdsadsadsadasd123213123213213213123213213213");
    	cout << str.capacity() << endl;
    
    • 1
    • 2
    • 3
    • 4

    image-20221101132556549

    如果是变大,扩容。

    assign相当于赋值拷贝

    	string str("wo ai ni ge chou sha bi");
    
    • 1

    21:replace

    21.1:用法

    image-20221101132706914

    从pos位置开始的len个字符替换成str

    21.2:测试

    	string str("wo ai ni ge chou sha bi");
    	str.replace(3, 2, "buai");
    	cout << str << endl;
    
    • 1
    • 2
    • 3

    如图就是把从第一个a开始的2个字符替换成buai。

    image-20221101133617805

    模拟实现string

    #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;
    	};
    }
    
    • 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
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
  • 相关阅读:
    mapstruct进阶属性
    Pushgateway的场景使用
    web前端期末大作业:JavaScript大作业——福五鼠动漫网页制作(6页)带轮播图效果 学生个人单页面网页作业 学生网页设计成品 静态HTML网页单页制作
    Windows系统管理一:操作系统概述
    机器学习中常用的不等式
    面试中经常问道的问题二
    postgresql 表、索引的膨胀率监控
    mysql远程连接 Communications link failure失败问题
    JSP物业服务管理系统myeclipse开发sql数据库BS模式java编程MVC结构
    Flutter 在项目中使用动画(不使用包)
  • 原文地址:https://blog.csdn.net/phenix_417/article/details/127632126