String类(C++)详解与应用

 

目录

1. 标准库中的string类

 1.1 string类

1.2 string类的常用接口说明

1. string类对象的常见构造

 2. string类对象的容量操作

3. string类对象的访问遍历

4. string类对象的修改操作

5. string类非成员函数

2. 关于string类的OJ题

(1) 917.仅仅反转字母

(2) 387.字符串中的唯一字符

(3) HJ1字符串最后一个单词的长度

(4) 125. 验证回文串

(5)415.字符串相加

(6)541.反转字符串II

(7)557. 反转字符串中的单词 III

(8)43. 字符串相乘

(9)HJ59找出字符串中第一个只出现一次的字符

3. string类的模拟实现


1. 标准库中的string类

 1.1 string类

http://www.cplusplus.com/reference/string/string/?kw=stringicon-default.png?t=N7T8http://www.cplusplus.com/reference/string/string/?kw=string1. 字符串是表示字符序列的类
2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。
4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
5. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

总结:

1. string是表示字符串的字符串类

2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。

3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>string;

4. 不能操作多字节或者变长字符的序列。

使用string类时,必须包含#include头文件以及using namespace std或者指定命名空间;

1.2 string类的常用接口说明
1. string类对象的常见构造

cplusplus.com/reference/string/string/string/icon-default.png?t=N7T8https://cplusplus.com/reference/string/string/string/

 (constructor)函数名称功能说明
string() 构造空的string类对象,即空字符串
string(const char* s) 用C-string来构造string类对象
string(size_t n, char c) string类对象中包含n个字符c
string(const string&s) 拷贝构造函数
void Teststring()
{
    string s1; // 构造空的string类对象s1
    string s2("hello bit"); // 用C格式字符串构造string类对象s2
    string s3(s2); // 拷贝构造s3
}
 2. string类对象的容量操作

函数名称功能说明
size返回字符串有效字符长度
length返回字符串有效字符长度
capacity返回空间总大小
empty检测字符串释放为空串,是返回true,否则返回false
clear清空有效字符
reserve为字符串预留空间**
resize将有效字符的个数该成n个,多出的空间用字符c填充

【注意】
1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一
致,一般情况下基本都是用size()。

2. clear()只是将string中有效字符清空,不改变底层空间大小。

3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大
小,如果是将元素个数减少,底层空间总大小不变。

4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于
string的底层空间总大小时,reserver不会改变容量大小。

3. string类对象的访问遍历
函数名称功能说明
operator[]返回pos位置的字符,const string类对象调用
begin+ endbegin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
rbegin + rendbegin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
范围for(auto)C++11支持更简洁的范围for的新遍历方式
4. string类对象的修改操作
函数名称功能说明
push_back 在字符串后尾插字符c
append在字符串后追加一个字符串
operator+=在字符串后追加字符串str
c_str返回C格式字符串
find + npos从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
rfind从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
substr 在str中从pos位置开始,截取n个字符,然后将其返回

【注意】

1. 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。

2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。

5. string类非成员函数
函数功能说明
operator+ 尽量少用,因为传值返回,导致深拷贝效率低
operator>> 输入运算符重载
operator<<输出运算符重载
getline 获取一行字符串
relational operators大小比较

string类中还有一些其他的操作,在需要用到时侯查文档即可。

2. 关于string类的OJ题

(1) 917.仅仅反转字母
class Solution {
public:
    string reverseOnlyLetters(string s) {
        size_t begin = 0;  // 设定字符串开始位置
        size_t end = s.size() - 1;  // 设定字符串结束位置
        while (begin < end) {  // 当开始位置小于结束位置时循环
            while (begin < end && !isalpha(s[begin])) {  // 找到第一个字母
                begin++;
            }
            while (begin < end && !isalpha(s[end])) {  // 找到最后一个字母
                end--;
            }
            swap(s[begin], s[end]);  // 交换两个字母的位置
            begin++;  // 开始位置后移
            end--;  // 结束位置前移
        }
        return s;  // 返回翻转后的字符串
    }
};
(2) 387.字符串中的唯一字符
class Solution {
public:
    int firstUniqChar(string s) {
        int cout[26];  // 用于记录每个字母出现的次数
        for (auto ch : s)
            cout[ch - 'a']++;  // 统计每个字母出现的次数
        for (int i = 0; i < s.size(); i++) {  // 遍历字符串
            if ((cout[s[i] - 'a']) == 1)  // 找到第一个只出现一次的字母
                return i;  // 返回该字母的索引
        }
        return -1;  // 如果没有只出现一次的字母,返回-1
    }
};
(3) HJ1字符串最后一个单词的长度
#include <iostream>
using namespace std;

int main() {
    string s;
    // 从标准输入流中读取一行字符串
    getline(cin, s);
    // 查找字符串中最后一个空格的位置
    size_t pos = s.rfind(' ');
    // 输出字符串末尾单词的长度
    cout << s.size() - (pos + 1) << endl;
    return 0;
}
(4) 125. 验证回文串
class Solution {
public:
    // 判断给定字符串s是否为回文串
    bool isPalindrome(string s) {
        string s1; // 存储去除非字母数字字符后的字符串
        string s2; // 存储s1的逆序字符串
        // 将s中的字母数字字符转为小写字符并存入s1中
        for (int i = 0; i < s.size(); i++)
            if (isalnum(s[i]))
                s1 += tolower(s[i]);
        s2 = s1; // 复制s1到s2
        reverse(s2.begin(), s2.end()); // 反转s2
        // 检查s1和s2是否相等,若不相等则不是回文串,返回false;否则返回true
        for (int i = 0; i < s1.size(); i++) {
            if((s1[i] != s2[i]))
                return false;
        }
        return true;
    }
};
(5)415.字符串相加
class Solution {
public:
    string addStrings(string num1, string num2) {
        int end1 = num1.length() - 1; // 初始化 num1 最后一位的索引
        int end2 = num2.length() - 1; // 初始化 num2 最后一位的索引
        string strResult; // 初始化存储结果的空字符串
        int next = 0; // 初始化进位值

        // 从右到左遍历数字
        while (end1 >= 0 || end2 >= 0) {
            // 获取当前位的数字值,如果索引越界则设为0
            int val1 = end1 >= 0 ? num1[end1--]-'0' : 0;
            int val2 = end2 >= 0 ? num2[end2--]-'0' : 0;
            int ret = (val1 + val2) + next; // 计算当前位的和
            int rets = ret % 10; // 取个位数
            next = ret / 10; // 更新进位值
            strResult += rets + '0'; // 将当前位的数字转为字符并添加到结果字符串
        }
        if (next != 0)
            strResult += next + '0'; // 如果最后还有进位,添加到结果字符串
        reverse(strResult.begin(), strResult.end()); // 反转结果字符串
        return strResult;
    }
};
(6)541.反转字符串II
class Solution {
public:
    string reverseStr(string s, int k) {
        // 每隔2k个字符对前k个字符进行翻转
        for (int i = 0; i < s.size(); i += (2 * k)) {
            // 如果当前位置+i+k小于字符串长度,则翻转前k个字符
            if (k + i < s.size())
                reverse(s.begin() + i, s.begin() + k + i);
            // 如果当前位置+i+k大于等于字符串长度,则翻转剩余所有字符
            else
                reverse(s.begin() + i, s.begin() + s.size());
        }
        // 返回翻转后的字符串
        return s;
    }
};
(7)557. 反转字符串中的单词 III
class Solution {
public:
    // 函数用于反转字符串中的单词
    string reverseWords(string s) {
        // 获取输入字符串的长度
        int length = s.length();
        int i = 0;
        // 遍历字符串
        while (i < length) {
            int start = i;
            // 找到下一个单词的起始位置
            while (i < length && s[i] != ' ') {
                i++;
            }
            int left = start;
            int right = i - 1;
            // 反转单词的字符
            while (left < right) {
                swap(s[left], s[right]);
                left++;
                right--;
            }
            // 跳过单词之间的空格
            while (i < length && s[i] == ' ') {
                i++;
            }
        }
        return s;
    }
};
(8)43. 字符串相乘
class Solution {
public:
    string addStrings(string num1, string num2) {
        int end1 = num1.length() - 1; // 获取num1的最后一位索引
        int end2 = num2.length() - 1; // 获取num2的最后一位索引
        string strResult; // 存储结果的字符串
        int next = 0; // 用于进位的变量

        // 从最后一位开始逐位相加
        while (end1 >= 0 || end2 >= 0) {
            int val1 = end1 >= 0 ? num1[end1--] - '0' : 0; // 获取num1当前位的值,若超出范围则为0
            int val2 = end2 >= 0 ? num2[end2--] - '0' : 0; // 获取num2当前位的值,若超出范围则为0
            int ret = (val1 + val2) + next; // 当前位相加并加上进位
            int rets = ret % 10; // 取个位数
            next = ret / 10; // 更新进位
            strResult += rets + '0'; // 将个位数转为字符并添加到结果字符串中
        }

        if (next != 0)
            strResult += next + '0'; // 若最高位有进位则添加到结果字符串中
        reverse(strResult.begin(), strResult.end()); // 反转结果字符串
        return strResult;
    }

    string multiply(string num1, string num2) {
        if (num1 == "0" || num2 == "0")
            return "0"; // 若其中一个乘数为0,则结果为0

        string strResult; // 存储结果的字符串
        int end1 = num1.size() - 1;
        int end2 = num2.size() - 1;

        // 逐位进行乘法运算
        for (int i = end2; i >= 0; i--) {
            string curr; // 存储当前乘法结果的字符串
            int next = 0; // 用于进位的变量

            // 为当前位补0,模拟乘法中的进位
            for (int j = end2; j > i; j--) {
                curr.push_back(0);
            }

            int y = num2.at(i) - '0'; // 获取num2当前位的值

            // 乘法运算
            for (int j = end1; j >= 0; j--) {
                int x = num1.at(j) - '0'; // 获取num1当前位的值
                int ret = x * y + next; // 当前位乘法结果加上进位
                curr.push_back(ret % 10); // 取个位数
                next = ret / 10; // 更新进位
            }

            // 处理最高位的进位
            while (next != 0) {
                curr.push_back(next % 10);
                next /= 10;
            }

            reverse(curr.begin(), curr.end()); // 反转当前乘法结果字符串

            // 将数字转为字符
            for (auto& c : curr) {
                c += '0';
            }

            // 将当前乘法结果与之前的结果相加
            strResult = addStrings(strResult, curr);
        }

        return strResult;
    }
};
(9)HJ59找出字符串中第一个只出现一次的字符
#include <iostream>  
using namespace std;  

int main() {
    string s;  // 定义字符串变量s
    cin >> s;  // 从标准输入流中读取字符串赋值给s
    int count[26] = {0};  // 定义长度为26的整型数组count并初始化为0

    // 遍历字符串s中的每个字符,统计每个字符出现的次数
    for (auto ch : s)
        count[ch - 'a']++;

    // 遍历字符串s中的每个字符
    for (int i = 0; i < s.size(); i++) {
        // 如果字符出现次数为1,输出该字符并换行,然后结束程序
        if ((count[s[i] - 'a']) == 1) {
            cout << s[i] << endl;
            return 0;
        }
    }

    // 如果没有出现次数为1的字符,输出-1并换行
    cout << -1 << endl;
    return 0;
}

3. string类的模拟实现

传统写法实现string类(简单模拟实现部分接口):

namespace MyString {
	class  string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;
		string::iterator  begin()
		{
			return _str;
		}
		string::const_iterator  begin()const
		{
			return _str;
		}
		string::iterator  end()
		{
			return _str+_size;
		}
		string::const_iterator  end()const
		{
			return _str+_size;
		}

		string(const char* str);
		string(const string& s);
		string& operator = (const string& s);
		const char* c_str();
		const char* c_str()const;
		~string();
		size_t size() const;
		size_t capacity() const;
		char& operator[](size_t pos);
		const char& operator[](size_t pos) const;

		void resize(size_t n, char ch);
		void reserve(size_t n);
		void push_back(char ch);
		void append(const char* str);
		string& operator+= (char ch);
		string& operator+= (const char* str);
		void insert(size_t pos, char ch);
		void insert(size_t pos, const char* str);
		void erase(size_t pos, size_t len);
		void swap(string& s);
		size_t find(char c, size_t pos)const;
		size_t find(const char* sub, size_t pos)const;
		string substr(size_t pos, size_t len)const;
		void clear();

	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
#if 0
	string::string()
		:_str(new char[1])
		, _size(0)
		, _capacity(0)
	{
		_str[0] = '\0';
	}

	string::string(const char* str)
		:_size(strlen(str))
		, _str(new char[strlen(str) + 1])
		, _capacity(strlen(str))
	{
		strcpy(_str, str);
	}//这个不好!因为要频繁调用strlen();
	//在不传参的情况下,不能用string::string(const char* str = nullptr) 二合一 :原因strlen不能计算nullptr

#endif
	//string::string(const char* str = "\0") 常量字符串后面自动加"\0";
	string::string(const char* str = "")
		:_size(strlen(str))
	{
		_capacity = _size;
		_str = new char[_capacity + 1];
		strcpy(_str, str);
	}
	string::string(const string& s)
	{
		_str = new char[s._capacity + 1];
		strcpy(_str, s._str);
		_size = s._size;
		_capacity = s._capacity;
	}
	string& string::operator = (const string& s)
	{
		char* temp = new char[s._capacity + 1];
		strcpy(temp, s._str);
		delete[] _str;
		_str = temp;
		_size = s._size;
		_capacity = s._capacity;
		return *this;
	}
	const char* string::c_str()
	{
		return _str;
	}
	const char* string::c_str()const
	{
		return _str;
	}
	string::~string()
	{
		delete[] _str;
		_str = nullptr;
		_size = _capacity = 0;
	}

	size_t string::size() const
	{
		return _size;
	}
	size_t string::capacity() const
	{
		return _capacity;
	}
	char& string::operator[](size_t pos)
	{
		assert(pos <= _size);
		return _str[pos];
	}
	const char& string::operator[](size_t pos) const
	{
		assert(pos < _size);
		return _str[pos];
	}
	void string::resize(size_t n, char ch = '\0')
	{
		if (n < _size)
		{
			_str[n] = '\0';
			_size = n;
		}
		else
		{
			reserve(n);
			for (size_t i = _size; i < n; i++)
			{
				_str[i] = ch;
			}
			_str[n] = '\0';
			_size = n;
		}
	}
	void string::reserve(size_t n)
	{
		if (n > _capacity)
		{
			char* temp = new char[n + 1];
			strcpy(temp, _str);
			delete[] _str;
			_str = temp;
			_capacity = n;
		}
	}
	void string::push_back(char ch)
	{
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : 2 * _capacity);
		}
		_str[_size] = ch;
		_size++;
		_str[_size] = '\0';
	}
	void string::append(const char* str)
	{
		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			reserve(_size + len);
		}
		strcpy(_str + _size, str);
		_size += len;
	}
	string& string::operator+= (char ch)
	{
		push_back(ch);
		return *this;
	}
	string& string::operator+= (const char* str)
	{
		append(str);
		return *this;
	}
	void string::insert(size_t pos, char ch)
	{
		assert(pos <= _size);
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : 2 * _capacity);
		}
		size_t end = _size + 1;
		while (end > pos)
		{
			_str[end] = _str[end - 1];
			--end;
		}
		_str[pos] = ch;
		_size++;
	}
#if 0
	void string::insert(size_t pos, const char* str)
	{
		assert(pos <= _size);
		while (_size + strlen(str) > _capacity)
		{
			reserve(_capacity == 0 ? 4 : 2 * _capacity);
		}
		size_t end = _size + strlen(str);
		//while (end - 1 > pos + strlen(str))
		while (end >= pos + strlen(str))
		{
			_str[end] = _str[end - strlen(str)];
			--end;
		}
		int index = 0;
		for (int i = pos; i < pos + strlen(str); i++)
		{
			_str[i] = str[index++];
		}
		_size += strlen(str);
	}

#endif
	void string::insert(size_t pos, const char* str)
	{
		assert(pos <= _size);
		while (_size + strlen(str) > _capacity)
		{
			reserve(_capacity == 0 ? 4 : 2 * _capacity);
		}
		size_t end = _size + strlen(str);
		while (end > pos + strlen(str) - 1)
		{
			_str[end] = _str[end - strlen(str)];
			--end;
		}
		strncpy(_str + pos, str, strlen(str));
		_size += strlen(str);
	}
	void string::erase(size_t pos, size_t len = std::string::npos)
	{
		assert(pos < _size);
		if (len == std::string::npos || len >= _size - pos)
		{
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			strcpy(_str + pos, _str + pos + len);
			_size -= len;
		}
	}
	void string::swap(string& s)
	{
		std::swap(_str, s._str);
		std::swap(_size, s._size);
		std::swap(_capacity, s._capacity);
	}
	size_t string::find(char c, size_t pos = 0)const
	{
		assert(pos < _size);
		for (size_t i = pos; i < _size; i++)
		{
			if (_str[i] == c)
			{
				return i;
			}
		}
		return std::string::npos;
	}
	size_t string::find(const char* sub, size_t pos = 0)const
	{
		assert(pos < _size);
		const char* p = strstr(_str + pos, sub);
		if (p)
			return p - _str;
		else
			return std::string::npos;
		return std::string::npos;
	}
	string string::substr(size_t pos = 0, size_t len = std::string::npos)const
	{
		assert(pos < _size);
		string sub;
		if (len >= _size - pos)
		{
			for (size_t i = pos; i < _size; i++)
				sub += _str[i];
		}
		else
		{
			for (size_t i = pos; i < pos + len; i++)
				sub += _str[i];
		}
		return sub;
	}
	void string::clear()
	{
		_size = 0;
		_str[_size] = '\0';
	}

	void swap(string& s1, string& s2)
	{
		swap(s1, s2);
	}
	bool operator==(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret == 0;
	}
	bool operator<(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret < 0;
	}
	bool operator<=(const string& s1, const string& s2)
	{
		return s1 < s2 || s1 == s2;
	}
	bool operator>(const string& s1, const string& s2)
	{
		return !(s1 <= s2);
	}
	bool operator>=(const string& s1, const string& s2)
	{
		return !(s1 < s2);
	}
	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}	
}

ostream& operator<<(ostream& out, const string& s)
{
	for (auto ch : s)
	{
		out << ch;
	}
	return out;
}

istream& operator>>(istream& in, string& s)
{
	s.clear();
	char ch;
	ch = in.get();
	char buff[128];
	size_t i = 0;
	while (ch != ' ' && ch != '\n')
	{
		buff[i++] = ch;
		if (i == 127)
		{
			buff[127] = '\0';
			s += buff;
			i = 0;
		}
		ch = in.get();
	}
	if (i > 0)
	{
		buff[i] = '\0';
		s += buff;
	}
	return in;
}

istream& getline(istream& in, string& s)
{
	s.clear();

	char ch;
	ch = in.get();
	char buff[128];
	size_t i = 0;
	while (ch != '\n')
	{
		buff[i++] = ch;
		if (i == 127)
		{
			buff[127] = '\0';
			s += buff;
			i = 0;
		}

		ch = in.get();
	}
	if (i > 0)
	{
		buff[i] = '\0';
		s += buff;
	}
	return in;
}

最后分享一下小编的gitee网址:XiangChao (RuofengMao) - Gitee.comicon-default.png?t=N7T8https://gitee.com/RuofengMao

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值