C++入门--string类的实现

news2025/1/11 2:44:14

目录

  • 1.string类常用函数实现
    • (1)string类成员变量定义
    • (2) string类默认构造函数实现
    • (3) string类拷贝构造函数实现
    • (4)string类析构函数
    • (5)string类c_str()函数实现
    • (6)string类size()函数实现
    • (7)string类operator[]函数实现
    • (8)string类迭代器的实现
    • (9)string类reserve函数实现
    • (10)string类push_back函数实现
    • (11)string类append函数实现
    • (12)string类operator+=运算符重载函数实现
    • (13)string类insert函数实现
    • (14)string类erase函数实现
    • (15)string类find函数实现
    • (16)string类substr函数实现
    • (17)string类resize函数模拟实现
    • (18)string类流插入operator<<非成员函数模拟实现
    • (19)string类string类流提取operator>>非成员函数模拟实现
    • (20)string类operator=运算符重载函数
    • (21)string类关系运算符重载
      • operator<函数
      • operator==函数
      • 函数复用实现
  • 2.string常用接口类函数实现源码
    • string.h文件

1.string类常用函数实现

(1)string类成员变量定义

namespace llh
{
	class string
	{
	private:
		size_t size;//string类的大小
		size_t capacity;//string类的存储容量
		char* _str;
	};
}

我们定义string类的成员变量有sizecapacity_str,配合我们实现插入、删除、扩容等修改操作函数,并且把我们自己实现的string类成员函数封装在命名空间里面,避免和标准库里面的string类类型、函数冲突。

(2) string类默认构造函数实现

		//默认构造函数
		string(const char* str = "")
		{
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

我们在实现默认构造函数时,①使用""空字符串作为缺省参数,常量字符串末尾有'\0',是跟库里面string类默认构造函数无参调用以及string类其他变量初始化配合,_str = new char[_capacity + 1];capacity+1是不传参调用时存储'\0';②构造函数使用初始化列表进行初始化,初始化列表中成员的初始化顺序与成员变量声明顺序应保持一致。

(3) string类拷贝构造函数实现

		//拷贝构造函数
		//拷贝构造函数
		string(const string& s)
		{
			if (this != &s)
			{
				_str = new char[s._capacity + 1];
				_size = s._size;
				_capacity = s._capacity;
				//strcpy(_str, s._str);
				//拷贝构造的string类中包含'\0',且'\0'后还有字符
				//strcpy只能拷贝到'\0'位置,剩余字符不能拷贝
				memcpy(_str, s._str, _size + 1);
			}
		}

使用string类对象s进行拷贝构造时(1)如果进行浅拷贝(即值拷贝),编译器只是将对象中的值拷贝过来,这时候两个string类对象会使用同一块空间,此时发生的危害:①相互影响。一个string类对象修改空间的数据会影响另一个string类对象的数据内容②同一块空间会被析构两次,程序崩溃;(2)所以string类要进行深拷贝,_str = new char[s._capacity + 1];开辟相同的空间,memcpy(_str, s._str, _size + 1);把数据拷贝到新开辟得到空间中,内置类型变量_size_capacity直接进行赋值,这时候两个对象使用不同的空间,其他数据相同。

(4)string类析构函数

		//析构函数
		~string()
		{
			_size = _capacity = 0;
			delete[] _str;
			_str = nullptr;
		}

使用delete操作符释放string类对象指向的空间,_size = _capacity = 0使用string类大小和存储容量清除为0。

(5)string类c_str()函数实现

		//string类返回字符串指针的函数
		const char* c_str()const
		{
			return _str;
		}

_str为私有成员变量,需要通过函数调用才能进行访问。

(6)string类size()函数实现

		size_t size()const
		{
			return _size;
		}

_size为私有成员变量,需要通过函数调用才能进行访问。

(7)string类operator[]函数实现

		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}
		const char& operator[](size_t pos)const
		{
			assert(pos < _size);
			return _str[pos];
		}

普通string类对象使用operator[]运算符重载,获取string类pos位置的字符,可以进行string类修改;const string类对象调用const修饰operator[]运算符,只能读取string类pos`位置的字符,不可以进行string类修改。

(8)string类迭代器的实现

		typedef char* iterator;
		typedef const char* const_iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		const_iterator begin()const
		{
			return _str;
		}
		const_iterator end()const
		{
			return _str + _size;
		}

①string类begin()迭代器为指针,指向string类第一个字符,end()迭代器为指针,指向string类最后一个有效字符的下一个位置②const类型的string类对象对数据内容进行访问,需要使用const类型的迭代器③迭代器封装在类域里面,使用迭代器需要在前面hhl::string::const对string类的数据才能进行访问。

(9)string类reserve函数实现

		void reserve(size_t n = 0)
		{
			//只允许进行扩容
			if (n > _capacity)
			{
				cout << "reserve->" << _capacity << endl;
				char* tmp = new char[n + 1];
				_capacity = n;
				//strcpy(tmp, _str);
			//拷贝构造的string类中包含'\0',且'\0'后还有字符
			//strcpy只能拷贝到'\0'位置,剩余字符不能拷贝
				memcpy(tmp, _str, n + 1);
				delete[] _str;
				_str = tmp;
			}
		}

实现reserve函数的过程:①对n进行判断,只进行扩容不进行缩容②开辟n+1大小的空间,多出的一个位置存储'\0'③把原有的数据拷贝到新的空间,释放掉原来的空间,让_str指向新的空间。

(10)string类push_back函数实现

		void push_back(char c)
		{
			if (_size == _capacity)
			{
				//二倍扩容
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size] = c;
			++_size;
			_str[_size] = '\0';
		}

①在插入字符之前,判断string类字符个数_size是否和存储容量相等,如果相等需要扩容②在扩容的时候,无参构造对象有效字符数为0,需要指定开辟的空间大小,其余情况进行原有容量的二倍扩容③插入字符后,++_size;并且_str[_size] = '\0';在最后添加'\0'进行限制。

(11)string类append函数实现

		void append(const char* str)
		{
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);
			_size += len;
		}

①判断插入str字符串是否需要扩容,这次我们进行按需扩容②使用strcpy函数将字符串str拷贝string类对象末尾,strcpy函数会将str字符串中的'\0'一起拷贝过去,不需要单独进行限制,最后更新string类数据个数。

(12)string类operator+=运算符重载函数实现

		string& operator+=(char c)
		{
			push_back(c);
			return *this;
		}
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

实现operator+=函数的原理push_back函数、append函数的原理是一样的,我们在这里直接进行对push_back 函数、append函数进行复用。

(13)string类insert函数实现

		void insert(size_t pos, size_t n, char c)
		{
			assert(pos <= _size);
			if (_size + n > _capacity)
			{
				//扩容到_size+n
				reserve(_size + n);
			}
			size_t end = _size;
			//挪动数据
			//当插入0位置时,end在结束时为-1,
			//由于end为无符号整型,会整型提升为整数的最大值,会发生越界访问的错误,程序崩溃
			while (end >= pos && end != npos)
			{
				_str[end + n] = _str[end];
				end--;
			}
			//插入n个字符c
			for (int i = 0; i < n; i++)
			{
				_str[i + pos] = c;
			}
			_size += n;
		}
		void insert(size_t pos, const char* str)
		{
			assert(pos <= _size);
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				//扩容到_size+len
				reserve(_size + len);
			}
			size_t end = _size;
			//挪动数据
			//当插入0位置时,end在结束时为-1,
			//由于end为无符号整型,会整型提升为整数的最大值,会发生越界访问的错误,程序崩溃
			while (end >= pos&&end!=npos)
			{
				_str[end + len] = _str[end];
				end--;
			}
			//插入字符串str
			for (int i = 0; i < len; i++)
			{
				_str[pos + i] = str[i];
			}
			_size += len;
		}

增加的成员变量:

//在string里面定义静态共有成员变量
	public:
		static size_t npos;
//在类外面进行初始化
	size_t string::npos = -1;

①判断插入字符数据是否需要扩容,以及pos位置是否合法②在挪动数据的时候,pos位置为0时,end在结束的时候为-1,因为endsize_t类型会整型提升为整型最大值,程序因越界访问直接崩溃。解决办法:①设置end变量类型为int类型,while循环判断框里的pos强转为0(库里面pos变量为无符号整型,同标准库里面的保持一致)③设置一个公有静态成员无符号整型变量npos,并且初始化为-1,附加一个判断条件end!=npos③进行字符数据的插入,_size += len;更新string对象数据个数。

(14)string类erase函数实现

		void erase(size_t pos, size_t len = npos)
		{
			assert(pos < _size);
			//要删除的长度很长,或字符串很短
			if (len == npos || pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				size_t end = pos + len;
				while (end<=_size)
				{
					_str[pos++] = _str[end++];
				}
				_size -= len;
			}
		}

判断pos位置是否合法,如果pos位置之后要删除的数据个数很大,直接在pos位置加'\0'进行限制;或者正常进行挪动数据覆盖进行删除(把‘'\0'一起挪动)。

(15)string类find函数实现

		size_t find(char c, size_t pos = 0)
		{
			assert(pos < _size);
			for (size_t i = 0; i < _size; i++)
			{
				if (_str[i] == c)
				{
					return i;
				}
			}
			return npos;
		}
		size_t find( const char* s,size_t pos = 0)
		{
			assert(pos < _size);
			//pos位置开始
			const char* ptr = strstr(_str+pos, s);
			if (ptr)
			{
				//指针减指针为之间的字符个数,也为子串的起始位置
				return ptr-_str;
			}
			else
			{
				return npos;
			}
		}

查找字符串使用strstr子串查找函数进行查找,若查找成功,返回字符串第一次出现的位置,ptr-_str得到string类的下标位置,若查找失败返回空指针。

(16)string类substr函数实现

		string substr(size_t pos = 0, size_t len=npos)
		{
			assert(pos < _size);
			string tmp;
			//判断拷贝长度
			size_t n = len;
			//if (pos + len > _capacity)
			if (len==npos||pos + len > _size)
			{
				n = _size - pos;
			}
			//把子串的数据拷贝string类tmp
			for (size_t i = 0; i < n; i++)
			{
				tmp += _str[pos + i];
			}
			return tmp;
		}

①判断需要拷贝的string类字符串长度,如果过长,从pos位置拷贝到string类末尾②把子串的数据拷贝string类tmp,并且返回tmp

(17)string类resize函数模拟实现

		void resize(size_t n, char ch = '\0')
		{			
			if (n < _capacity)
			{
				_str[n] = '\0';
				_size = n;
			}
			else
			{
				reserve(n);
				for (size_t i = _size; i <n; i++)
				{
					_str[i] = ch;
				}
				_size = n;
				_str[_size] = '\0';
			}
		}

判断是减少string类数据个数,还是增加数据个数。①若是减少string类数据个数,直接在对应的位置添加'\0'(不用进行缩容,考虑到后面还需要插入数据,反复进行扩容缩容,代价太大),_size = n;更新数据个数②若是增加string类数据个数,先进行扩容(若是存储容量足够,不需要扩容,否则就进行扩容,已在reserve函数中实现),插入字符c_size = n;_str_size] = '\0'; 进行字符串限制和数据更新。

(18)string类流插入operator<<非成员函数模拟实现

	ostream& operator<<(ostream& out, string& s)
	{
		for (int i = 0; i < s.size(); i++)
		{
			out << s[i];
		}
		return out;
	}

①把operator<<函数实现成非成员函数,是为了保证第一个参数ostream& out②在实现该函数时,没有直接out << s.c_str() << endl;输出,string类不看'\0'而终止,而是以_size数据个数为终止输出。

(19)string类string类流提取operator>>非成员函数模拟实现

//定义为类成员函数
	void clear()
	{
		_str[0] = '\0';
		_size = 0;
	}
//定义为非成员函数
	istream& operator>>(istream& in, string& s)
	{
		s.clear();
		char ch = in.get();
		
		while (ch == ' ' ||ch == '\n')
		{
			ch = in.get();
		}
		char buff[128];
		size_t i = 0;
		while (ch != ' ' && ch != '\n')
		{
			//使用一个128字节大小的数组,把读取到的字符存放数组
			//如果buff数组到127字符,一次性加载到string类对象
			//此时还有字符没有读取完,继续存放在buff数组,直到读取完毕
			//最后再把字符串加载到string类末尾
			if (i == 127)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}
			buff[i++] = ch;
			ch = in.get();
		}
		if (i != 0)
		{
		//当读取到buff数组的字符时,大于0小于127时
		//进行最后一次追加拷贝
			buff[i] = '\0';//进行字符串拷贝限制
			s += buff;
		}
	}

①对string类对象输入的情况,需要使用clear函数清除原有string类数据,重新对string类对象输入②istream流不能读取空格和'\n',需要使用istream流中的get函数③考虑到拷贝数据个数太多,一个一个进行对string类拷贝需要,需要进行多次扩容;而不使用reserve函数进行开辟足够的空间,若空间开小了,达不到减少扩容的效果,若空间开大了,提取数据个数较小时,会造成空间浪费,所以选择128字节大小buff字符数组存储,进行适时拷贝。

(20)string类operator=运算符重载函数

传统写法实现:

		//传统写法
		string& operator=(const string& s)
		{
			if (this != &s)
			{
				_str = new char[s._capacity + 1];
				_size = s._size;
				_capacity = s._capacity;
				memcpy(_str, s._str, _size + 1);
			}
			return *this;
		}

开辟和s对象的空间大小,进行数据拷贝
在这里插入图片描述

现代写法版本一:

		//现代写法
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
		string& operator=(const string& s)
		{
			if (this != &s)
			{
				string tmp(s);
				swap(tmp);
			}
			return *this;
		}

现代写法版本二:

		string& operator=(string tmp)
		{
			swap(tmp);
			return *this;
		}

使用拷贝构造函数构造一个临时对象tmp,然后*thistmp进行私有变量值交换,因为tmp是临时对象,在函数销毁时会自动调用析构函数,对tmp现在(this原来指向)的空间进行销毁!

(21)string类关系运算符重载

operator<函数

		//版本一
		bool operator<(const string& s)
		{
			size_t i1 = 0;
			size_t i2 = 0;
			while (i1 < _size && i2 < s._size)
			{
				if (_str[i1++] < s._str[i2++])
				{
					return true;
				}
				else
				{
					return false;
				}
			}
			return _size < s._size;
		}
		//版本二简洁写法
		bool operator<(const string& s)const
		{
			int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);
			return ret == 0 ? _size < s._size : ret < 0;
		}

版本一和版本二原理一样,先比较数据个数较小的string类的部分字符串,若是比较的数据内容相同,则判断数据个数大小,返回真值。

operator==函数

		bool operator==(const string& s)const
		{
			return _size == s._size &&
				memcpy(_str, s._str, _size);
		}

函数复用实现

		bool operator>(const string& s)const
		{
			return !(*this < s && *this == s);
		}
		bool operator<=(const string& s)const
		{
			return !(*this > s);
		}
		bool operator>=(const string& s)const
		{
			return !(*this < s);
		}
		bool operator!=(const string& s)const
		{
			return !(*this == s);
		}

2.string常用接口类函数实现源码

string.h文件

#pragma once
#include<iostream>
#include<string>
#include<string.h>
#include<assert.h>
using namespace std;
namespace hhl
{
	class string
	{
		//friend ostream& operator<<(ostream& out, string& s);
	public:
		typedef char* iterator;
		typedef const char* const_iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		const_iterator begin()const
		{
			return _str;
		}
		const_iterator end()const
		{
			return _str + _size;
		}
		//默认构造函数
		string(const char* str = "")
		{
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
			//memcpy(_str, str, _size + 1);
		}
		//拷贝构造函数
		string(const string& s)
		{
			if (this != &s)
			{
				_str = new char[s._capacity + 1];
				_size = s._size;
				_capacity = s._capacity;
				//strcpy(_str, s._str);
				//拷贝构造的string类中包含'\0',且'\0'后还有字符
				//strcpy只能拷贝到'\0'位置,剩余字符不能拷贝
				memcpy(_str, s._str, _size + 1);
			}
		}
		//传统写法
		string& operator=(const string& s)
		{
			if (this != &s)
			{
				_str = new char[s._capacity + 1];
				_size = s._size;
				_capacity = s._capacity;
				memcpy(_str, s._str, _size + 1);
			}
			return *this;
		}
		//现代写法
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
		string& operator=(const string& s)
		{
			if (this != &s)
			{
				string tmp(s);
				swap(tmp);
			}
			return *this;
		}
		string& operator=(string tmp)
		{
			swap(tmp);
			return *this;
		}
		//string类返回字符串指针的函数
		const char* c_str()const
		{
			return _str;
		}
		size_t size()const
		{
			return _size;
		}
		//析构函数
		~string()
		{
			_size = _capacity = 0;
			delete[] _str;
			_str = nullptr;
		}
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}
		const char& operator[](size_t pos)const
		{
			assert(pos < _size);
			return _str[pos];
		}
		void reserve(size_t n = 0)
		{
			//只允许进行扩容
			if (n > _capacity)
			{
				cout << "reserve->" << _capacity << endl;
				char* tmp = new char[n + 1];
				_capacity = n;
				//strcpy(tmp, _str);
			//拷贝构造的string类中包含'\0',且'\0'后还有字符
			//strcpy只能拷贝到'\0'位置,剩余字符不能拷贝
				memcpy(tmp, _str, n + 1);
				delete[] _str;
				_str = tmp;
			}
		}
		void push_back(char c)
		{
			if (_size == _capacity)
			{
				//二倍扩容
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size] = c;
			++_size;
			_str[_size] = '\0';
		}
		void append(const char* str)
		{
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}
			//for (size_t i = 0; i < len; i++)
			//{
			//	_str[_size + i] = str[i];
			//}
			//strcpy(_str + _size, str);
			memcpy(_str + _size, str, len + 1);
			_size += len;
		}
		string& operator+=(char c)
		{
			push_back(c);
			return *this;
		}
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}
		void insert(size_t pos, size_t n, char c)
		{
			assert(pos <= _size);
			if (_size + n > _capacity)
			{
				//扩容到_size+n
				reserve(_size + n);
			}
			size_t end = _size;
			//挪动数据
			//当插入0位置时,end在结束时为-1,
			//由于end为无符号整型,会整型提升为整数的最大值,会发生越界访问的错误,程序崩溃
			while (end >= pos && end != npos)
			{
				_str[end + n] = _str[end];
				end--;
			}
			//插入n个字符c
			for (int i = 0; i < n; i++)
			{
				_str[i + pos] = c;
			}
			_size += n;
		}
		void insert(size_t pos, const char* str)
		{
			assert(pos <= _size);
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				//扩容到_size+len
				reserve(_size + len);
			}
			size_t end = _size;
			//挪动数据
			//当插入0位置时,end在结束时为-1,
			//由于end为无符号整型,会整型提升为整数的最大值,会发生越界访问的错误,程序崩溃
			while (end >= pos && end != npos)
			{
				_str[end + len] = _str[end];
				end--;
			}
			//插入字符串str
			for (int i = 0; i < len; i++)
			{
				_str[pos + i] = str[i];
			}
			_size += len;
		}
		//void erase(size_t pos, size_t len = npos)
		//{
		//	assert(pos < _size);
		//	//要删除的长度很长,或字符串很短
		//	if (len == npos || pos + len >= _size)
		//	{
		//		_str[pos] = '\0';
		//		//_str[_size] = '\0';
		//		_size=pos;
		//	}
		//	else
		//	{
		//		//进行覆盖移动
		//		for (size_t i = 0; i < _size-(pos+len); i++)
		//		{
		//			_str[pos+i] = _str[pos + i+len];
		//		}
		//		_str[_size - len] = '\0';
		//		_size -= len;
		//	}
		//}
		void erase(size_t pos, size_t len = npos)
		{
			assert(pos < _size);
			//要删除的长度很长,或字符串很短
			if (len == npos || pos + len >= _size)
			{
				_str[pos] = '\0';
				//_str[_size] = '\0';
				_size = pos;
			}
			else
			{
				size_t end = pos + len;
				while (end <= _size)
				{
					_str[pos++] = _str[end++];
				}
				_size -= len;
			}
		}
		size_t find(char c, size_t pos = 0)
		{
			assert(pos < _size);
			for (size_t i = 0; i < _size; i++)
			{
				if (_str[i] == c)
				{
					return i;
				}
			}
			return npos;
		}
		size_t find(const char* s, size_t pos = 0)
		{
			assert(pos < _size);
			//pos位置开始
			const char* ptr = strstr(_str + pos, s);
			if (ptr)
			{
				//指针减指针为之间的字符个数,也为子串的起始位置
				return ptr - _str;
			}
			else
			{
				return npos;
			}
		}
		string substr(size_t pos = 0, size_t len = npos)
		{
			assert(pos < _size);
			string tmp;
			//判断长度
			size_t n = len;
			//if (pos + len > _capacity)
			if (len == npos || pos + len > _size)
			{
				n = _size - pos;
			}
			//把需要的数据拷贝string类tmp
			for (size_t i = 0; i < n; i++)
			{
				tmp += _str[pos + i];
			}
			return tmp;
		}
		void resize(size_t n, char ch = '\0')
		{			
			if (n < _capacity)
			{
				_str[n] = '\0';
				_size = n;
			}
			else
			{
				reserve(n);
				for (size_t i = _size; i <n; i++)
				{
					_str[i] = ch;
				}
				_size = n;
				_str[_size] = '\0';
			}
		}
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}
		//	// "hello" "hello"   false
		//	// "helloxx" "hello" false
		//	// "hello" "helloxx" true
		//版本一
		//bool operator<(const string& s)
		//{
		//	size_t i1 = 0;
		//	size_t i2 = 0;
		//	while (i1 < _size && i2 < s._size)
		//	{
		//		if (_str[i1++] < s._str[i2++])
		//		{
		//			return true;
		//		}
		//		else
		//		{
		//			return false;
		//		}
		//	}
		//	return _size < s._size;
		//}
		//版本二简洁写法
		bool operator<(const string& s)const
		{
			int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);
			return ret == 0 ? _size < s._size : ret < 0;
		}
		bool operator==(const string& s)const
		{
			return _size == s._size &&
				memcpy(_str, s._str, _size);
		}
		bool operator>(const string& s)const
		{
			return !(*this < s && *this == s);
		}
		bool operator<=(const string& s)const
		{
			return !(*this > s);
		}
		bool operator>=(const string& s)const
		{
			return !(*this < s);
		}
		bool operator!=(const string& s)const
		{
			return !(*this == s);
		}
	private:
		char* _str;
		size_t _capacity;//string类的存储容量
		size_t _size;//string类的大小
	public:
		static size_t npos;
		//const static size_t npos = -1;
		//整型可以这样使用,其他类型不可以,不建议这样使用
};
	size_t string::npos = -1;
	ostream& operator<<(ostream& out, string& s)
	{
		//out << s.c_str() << endl;
		for (int i = 0; i < s.size(); i++)
		{
			out << s[i];
		}
		return out;
	}
	istream& operator>>(istream& in, string& s)
	{
		s.clear();
		char ch = in.get();
		
		while (ch == ' ' ||ch == '\n')
		{
			ch = in.get();
		}
		char buff[128];
		size_t i = 0;
		while (ch != ' ' && ch != '\n')
		{
			//使用一个128字节大小的数组,把读取到的字符存放数组
			//如果buff数组到127字符,一次性加载到string类对象
			//此时还有字符没有读取完,继续存放在buff数组,直到读取完毕
			//最后再把字符串加载到string类末尾
			if (i == 127)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}
			buff[i++] = ch;
			ch = in.get();
		}
		if (i != 0)
		{
			buff[i] = '\0';
			s += buff;
		}
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/859301.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux umask:文件访问权限控制预设值

1. umask 定义 在 linux 系统中&#xff0c;umask 被定义在 /etc/profile 配置文件中&#xff0c;有一段 shell 脚本对 umask 是这么定义的。在 shell 会话输入命令&#xff1a; $ cat /etc/profile # 查看 /etc/profile 配置文件的内容 if [ $UID -gt 199 ] &&…

七夕送什么比较好?适合七夕节送的礼物

七夕将至&#xff0c;这是中国传统节日中最浪漫的一天&#xff0c;也是许多情侣们表达爱意的特殊时刻。在这个美好的节日里&#xff0c;送上一份特别的礼物&#xff0c;不仅能让心爱的人感受到你的深情厚意&#xff0c;还能为你们的爱情故事添上浓墨重彩的一笔。还不知道七夕要…

ElasticSearch:项目实战(1)

es环境搭建参考&#xff1a;ElasticSearch&#xff1a;环境搭建步骤_Success___的博客-CSDN博客 需求&#xff1a; 用户输入关键可搜索文章列表 关键词高亮显示 文章列表展示与home展示一样&#xff0c;当用户点击某一篇文章&#xff0c;可查看文章详情 思路&#xff1a; …

【腾讯云 Cloud Studio 实战训练营】深度体验 | 使用腾讯云 Cloud Studio 快速构建 Vue + Vite 完成律师 H5 页面

【腾讯云 Cloud Studio 实战训练营】深度体验 | 使用腾讯云 Cloud Studio 快速构建 Vue Vite 完成律师 H5 页面 写在前面的话一、腾讯云 Cloud Studio 介绍1.1 Cloud Studio 应用场景1.2 Cloud Studio 开发优势 二、沉浸式体验开发快速构建 H5 页面2.1 注册与登录 Cloud Studi…

第7章 通过内网本机IP获取微信code值及其对code值的回调。

在第5章中讲述了怎样通过内网穿透外外网从而获取微信code值&#xff0c;实际上微信测试帐号管理页中也支持通过内网本机IP获取微信code值。 1 重构launchSettings.json "https": { "commandName": "Project", "dotnetRunMessages": t…

代码随想录—力扣算法题:59螺旋矩阵II.Java版(示例代码与导图详解)

版本说明 当前版本号[20230810]。 版本修改说明20230810初版 目录 文章目录 版本说明目录59.螺旋矩阵II思路左闭右开方法左闭右闭方法两种方法的区别总结 59.螺旋矩阵II 力扣题目链接 更多内容可点击此处跳转到代码随想录&#xff0c;看原版文件 给定一个正整数 n&#xf…

BDA初级分析——数据收集、清洗和整理

一、认识数据 什么是数据&#xff1f; 数据是对客观世界的记录&#xff0c;用来记载事物的性质、状态、相互关系等。 有哪些常见的数据类型&#xff1f; 什么是数据集&#xff1f; 数据集&#xff1a;数据的集合&#xff0c;通常以表格形式出现。 二、收集数据 我们都会从哪里…

优思学院|6sigma合格率计算公式

概述 在现代竞争激烈的商业环境中&#xff0c;质量控制对于制造业和服务业都至关重要。其中&#xff0c;一个强大的方法是采用6sigma&#xff08;也称为6σ&#xff09;方法来提高质量和降低缺陷率。本文将深入探讨6sigma合格率计算公式&#xff0c;解释其在质量管理中的应用&…

20款奔驰C260 L(W206)更换内饰最全发光套件,提升车内氛围

原厂1:1设计&#xff0c;免编程匹配&#xff0c;无损安装&#xff0c;可升级项目&#xff1a; 1、碳纤维中控氛围灯&#xff08;阿凡达水滴款&#xff09; 2、发光前风口&#xff1b; 3、发光后风口&#xff1b; 4、发光座椅背气氛灯&#xff1b; 5、中音发光盖板 6、主动…

企业有VR全景拍摄的需求吗?能带来哪些好处?

在传统图文和平面视频逐渐疲软的当下&#xff0c;企业商家如何做才能让远在千里之外的客户更深入、更直接的详细了解企业品牌和实力呢&#xff1f;千篇一律的纸质材料已经过时了&#xff0c;即使制作的再精美&#xff0c;大家也会审美疲劳&#xff1b;但是你让客户远隔千里&…

网络编程基础:Linux网络I/O模型、JavaI/O模型、Netty

文章目录 一、Linux网络I/O模型简介0.文件描述符和系统调用1. 阻塞I/O模型2. 非阻塞I/O模型&#xff08;轮询&#xff09;3. I/O复用模型&#xff08;轮询、事件驱动&#xff09; 二、Java的I/O演进1.BIO&#xff08;阻塞&#xff09;&#xff08;1&#xff09;套接字&#xff…

安达发|疫情影响下的APS排程可以给制造业解决哪些问题

随着市场需求的不断变化&#xff0c;特别是对柔性、小单量多批次的需求日益增长&#xff0c;再加上疫情的影响&#xff0c;企业随时可能面临延期交货的风险。这使得行业供应链管理的复杂性不断加剧&#xff0c;企业对于生产管理高效性的需求也日益显著。 同时&#xff0c;我们…

vm workstation pro安装centos7

官网下载vm workstation pro&#xff0c;参考上一篇文章&#xff1a;https://editor.csdn.net/md/?articleId132208423安装centos7 官网下载的vm&#xff0c;是有一个镜像iso文件的 完成后稍等&#xff1a; 这一步很重要&#xff0c;别急着点开始安装&#xff0c;看到有感叹…

.NET 8 Preview 6 中推出 .NET MAUI: 欢迎使用 VS Code 和 VS for Mac

作者&#xff1a;David Ortinau 排版&#xff1a;Alan Wang .NET 8 Preview 6 推出了.NET MAUI&#xff0c;同时修复了 23 个关键问题&#xff0c;并引入了适用于 iOS 的 Native AOT。此外&#xff0c;您现在可以在 .NET 8 中使用 .NET MAUI&#xff0c;只要安装 Visual Studio…

excel将主信息和明细信息整理为多对多(每隔几行空白如何填充)

excel导出的数据是主信息和明细信息形式。 原始数据图&#xff1a; 最终效果:

UML之四种事物

目录 结构事物 行为事物 分组事物&#xff1a; 注释事物 结构事物 1.类(Class) -类是对一组具有相同属性、方法、关系和语义的对象的描述。一个类实现一个或多个接口 2.接口(interface) -接口描述 了一个类或构件的一个服务的操作集。接口仅仅是定义了一组操作的规范&…

23、springboot日志使用入门-- SLF4J+Logback 实现(springboot默认的日志实现),日志打印到控制台及日志输出到指定文件

springboot日志使用入门 ★ 典型的Spring Boot日志依赖&#xff1a; spring-boot-start.jar -- spring-boot-starter-logging.jar (Spring Boot的日志包&#xff09;-- logback&#xff08;core、classic&#xff09;-- log4j-to-slf4j.jar-- jul-to-slf4j.jar就是springboo…

【el-upload】批量上传图片时在before-upload中添加弹窗判断时的踩坑记录

一、初始代码 1. 初始使用组件代码片段 <!-- 上传 --> <DialogUploadFile ref"uploadFile" success"refresh" />// 上传 const uploadHandle () > {if (selections.value.length ! 1) {onceMessage.warning(请选择一条数据操作)return}u…

并行FIR滤波器

FIR 滤波器原理 FIR 滤波器是有限长单位冲击响应滤波器&#xff0c;又称为非递归型滤波器。FIR 滤波器具有严格的线性相频特性&#xff0c;同时其单位响应是有限长的&#xff0c;因而是稳定的系统。 FIR 滤波器本质上就是输入信号与单位冲击响应函数的卷积&#xff0c;表达式…

MapBox加载不同风格

初始化MapBox地图&#xff1a; var map new mapboxgl.Map({container: map,zoom: 3,center: [105, 34],//此处更改地图风格style: mapbox://styles/mapbox/satellite-v9,hash: false,});1.户外地图&#xff08;mapbox://styles/mapbox/basic-v9&#xff09;新版&#xff1a;&a…