string类的模拟实现

文章目录

  • string类的模拟实现
    • string基本框架的实现
    • operator+=的实现
    • string常用函数的实现


string类的模拟实现

前文对于string的常用函数做了讲解,由于string是一个面试官常考的点,总喜欢让模拟实现string类,下面来模拟实现一下string,赋予基本的功能,且逐步完善函数实现方式。

string基本框架的实现

string类的基本框架,比如构造函数,拷贝构造,析构函数,成员变量,起码的push_back等一些能正常使得string运行的函数的实现。

namespace String {
	class string {
	public:
		//迭代器: string中的迭代器实际上就是指针

		typedef char* iterator;
		typedef const char* const_iterator;
		iterator begin()
		{
			//begin 表示的是string的首元素地址
			return _str;
		}
		iterator end()
		{
			//end 返回string最后一个元素的下一个位置,也就是'\0'
			return _str + _size;
		}
		const_iterator begin() const
		{
			//begin 表示的是string的首元素地址
			return _str;
		}
		const_iterator end() const
		{
			//end 返回string最后一个元素的下一个位置,也就是'\0'
			return _str + _size;
		}
		//默认构造和带参构造合并,使用缺省参数

		string(const char* str = "")  //""字符串自带'\0'
			:_str(new char[strlen(str) + 1])
			, _size(strlen(str))
			, _capacity(strlen(str))
		{
			//存储字符串
			strcpy(_str, str);
		}

		//拷贝构造
		//string(const string& s)
		//{
		//	//深拷贝,就是创建一个大小一样的空间
		//	_str = new char[s._capacity + 1];
		//	strcpy(_str, s._str);
		//	_size = s._size;
		//	_capacity = s._capacity;
		//}
		string(const string& s)
		{
			_str = new char[s._capacity + 1];
			memcpy(_str, s._str, s._size + 1);
			_size = s._size;
			_capacity = s._capacity;
		}
		//析构函数
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		void Print() {
			cout << _str << "\t" << _size << "\t" << _capacity << endl;
		}
		//reserve  保留容量 可以扩容
		void reserve(size_t n)  //只是改变capacity 不改变size
		{
			if (n > _capacity)
			{
				//新建一个字符数组
				cout << "reserve->" << n << endl;
				char* new_str = new char[n + 1];
				//更改容量
				//strcpy(new_str, _str);
				memcpy(new_str, _str, _size + 1);
				delete[] _str;
				_str = new_str;
				_capacity = n;
			}
		}
		//push_back	
		void push_back(char ch)
		{
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			//加入字符
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}
		void append(const char* str)
		{
			if (_size + strlen(str) > _capacity)
			{
				reserve(_size + strlen(str));//至少保留_size + strlen(str)
			}
			//加入字符串
			memcpy(_str + _size, str, strlen(str) + 1);//在'\0'位置(就是_str末尾)+str
			_size += strlen(str);
		}

		//实现+=  也是使用push_back 和append函数
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}
		//返回size
		size_t size() const  //const表示修饰this指针,也就是说只读,如果是const对象,也可以访问,普通用户相当于权限的缩小也可也访问
		{
			return _size;
		}
		size_t capacity()
		{
			return _capacity;
		}
	private:
		char* _str;
		int _size;
		int _capacity;
	};
}

构造函数和拷贝构造

默认构造函数和带参构造函数合并,使用缺省参数

拷贝构造函数,我们要使用深拷贝,因为如果是浅拷贝,仅仅是将数值传给新的string对象,但是两者对应一个地址一个空间,当析构一个string对象后,另一个对象再次析构就会报错。

string(const char* str = "")  //""字符串自带'\0'
	:_str(new char[strlen(str) + 1])   //因为我们底层用的数组,所以一定要多开一位空间存放'\0'
	, _size(strlen(str))
	, _capacity(strlen(str))
{
	//存储字符串
	strcpy(_str, str);  //传入字符串的时候,一般都是结尾为'\0',中间有'\0'的都是我们为string对象增加的。所以这个地方还是使用strcpy即可
}

//拷贝构造
string(const string& s)
{
	//深拷贝,就是创建一个大小一样的空间
	_str = new char[s._capacity + 1];
	strcpy(_str, s._str);
	_size = s._size;
	_capacity = s._capacity;
}

push_back和append的实现

想要实现push_back和append函数,都要在底层中考虑是否需要扩容,那么我们就顺势要写出reserve函数,让其来判断是否需要扩容。

//reserve  保留容量 可以扩容
void reserve(size_t n)  //只是改变capacity 不改变size
{
	if (n > _capacity)
	{
		//新建一个字符数组
		cout << "reserve->" << n << endl;
		char* new_str = new char[n + 1];
		//更改容量
		//strcpy(new_str, _str);
		memcpy(new_str, _str, _size + 1);
		delete[] _str;
		_str = new_str;
		_capacity = n;
	}
}		
void push_back(char ch)
{
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}
	//加入字符
	_str[_size] = ch;
	++_size;
	_str[_size] = '\0';
}
void append(const char* str)
{
	if (_size + strlen(str) > _capacity)
	{
		reserve(_size + strlen(str));//至少保留_size + strlen(str)
	}
	//加入字符串
	memcpy(_str + _size, str, strlen(str) + 1);//在'\0'位置(就是_str末尾)+str
	_size += strlen(str);
}

在这里插入图片描述

注意:为什么拷贝字符串内容的时候用memcpy而不是strcpy,这是因为,string中可能中间会有'\0',memcpy是根据第三个参数来定要拷贝的字符长度,而strcmpy,是根据要拷贝的字符串的'\0'出现的位置,所以使用memcpy更加合适。

strcpy和memcpy的对比

  • char * strcpy ( char * destination, const char * source );
  • void * memcpy ( void * destination, const void * source, size_t num );

operator+=的实现

我们实现了push_back和append之后就可以直接复用这两个函数,实现operator+=

string& operator+=(char ch)
{
	push_back(ch);  //插入一个字符的时候
	return *this;
}
string& operator+=(const char* str)
{
	append(str); //插入字符串的时候
	return *this;
}//因为string对象在该函数之后不会释放空间,所以传引用返回,提高效率

string常用函数的实现

下面主要是对于insert、find、erase、substr、resize、一系列重载运算符等的实现

insert的实现

我们主要实现两种insert函数 1.在pos位置上插入n个字符c 2.在pos位置上插入字符串str

string& insert(size_t pos, size_t n, char c)
{
	//1.先判定pos是否正确
	assert(pos <= _size);
			
	//2.扩容
	reserve(_size + n);
			
	//3.在pos位置上开始挪动n个字符
	size_t end = _size;
	//因为如果pos为0的时候,无符号整型0减去1,end >= pos比较 为一个巨大值,使得该循环无法停止
	//npos是static变量,定义为-1;
	while (end >= pos && end != npos)
	{
		_str[end + n] = _str[end];
		--end;
	}
			
	//4.添加n个字符
	for (int i = 0; i < n; i++)
	{
		_str[pos + i] = c;
	}
	_size += n;
	return *this;
}
string& insert(size_t pos, const char*str)
{
	//1.pos的判定
	assert(pos <= _size);

	//2.扩容
	int len = strlen(str);
	reserve(_size + len);
			
	//3.在pos位置上移动len个字符
	size_t end = _size;
	while (end >= pos && end != npos)
	{
		_str[end + len] = _str[end];
		--end;
	}

	//4.将str字符串的字符依次输入
	for (int i = 0; i < len; i++)
	{
		_str[pos + i] = str[i];
	}

	//5.最后_size增加
	_size += len;
	return *this;
}

在这里插入图片描述

注意:npos的使用,是为了防止size_t无符号整型在于整型pos比较时候的强制转换,整型提升,得到一个巨大值,造成无限循环。

erase的实现

主要就是判断len是否等于npos,或者pos+len>=_size,分两种情况,是否从pos删除完,实际上就是在pos位置上加上'\0'即可,反之就是间隔len个距离向前移动字符,直到pos+len<=_size,最后_size-=len

string& erase(size_t pos = 0, size_t len = npos)
{
	assert(pos <= _size);
	if (len == npos || pos + len >= _size)
	{
		//表示从pos位置删完
		_str[pos] = '\0';
		_size = pos;
	}
	else {
		//从pos位置删除len个字符
		//向前挪动
		size_t end = pos;
		while (end+len <= _size)  
		{
			_str[end] = _str[end + len];
			end++;
		}
		//此时end==_size
		//_str[end] = '\0';
		_size -= len;
	}
	return *this;
}

在这里插入图片描述

find的实现

find的实现,就是遍历找到符合条件的下标,并返回

size_t find(char ch, size_t pos = 0) const
{
	assert(pos < _size);
	for (size_t i = pos; i < _size; i++)
	{
		if (_str[i] == ch)
		{
			return i;
		}
	}
	return npos;  //没有找到返回-1;
}
size_t find(const char* s, size_t pos = 0) const
{
	//使用strstr
	assert(pos < _size);
	const char* str = strstr(_str+pos, s);  //使用str函数,进行比较是否有对应的字符串
	if (str)
	{
		return str-_str;//两个指针相减,得到的是地址的偏移量
	}
	else {
		return npos;
	}
}

substr的实现

substr的实现,就是判断要解决的n的数值,然后新建一个string字符串,将从pos位置开始的n个字符依次添加到这个新字符串中,最后返回新字符串

//substr的实现
string substr(size_t pos = 0, size_t len = npos)
{
	assert(pos < _size);

	size_t n = len;
	//如果缺省len=npos 或者是截取的范围大于_size
	if (len == npos || pos + len >= _size)
	{
		n = _size - pos;  
	}
	//创建一个新的字符数组
	string new_str;
	new_str.reserve(n);

	for (size_t i = pos; i < n + pos; i++)
	{
		new_str += _str[i];
	}
	return new_str;
}

在这里插入图片描述

resize的实现

resize底层是有reserve的,即需要判断是否需要扩容,满足_size<=_capacity

//实现resize
void resize(size_t n, char ch = '\0')
{
	//两种情况,1.n<_size 直接赋值'\0' 2.判断是否扩容 
	if (n < _size)
	{
		_size = n;
		_str[_size] = '\0';
	}
	else
	{
		reserve(n);//让reserve来判断是否是需要扩容
		for (size_t i = _size; i < n; i++)
		{
			_str[i] = ch;
		}
		_size = n;
		_str[_size] = '\0';
	}
}

opeartor重载运算符

重载运算符,只要实现一两个就能实现其他,下面我们实现的是operator< 和operator== 然后通过调用这两个函数,来实现其他operator

		//字符串比较按照ascii比较
		//bool operator<(const string& s)
		//{
		//	int num=memcmp(_str, s._str, _size > s._size?s._size : _size);
		//	//如果在最小长度下,前面数值小于后者 num返回的是负数
		//	
		//	return num == 0 ? _size < s._size : num < 0;
		//	//如果如果num为0,说明等于,且前者长度小于后者,返回真值,反之返回
		//}
bool operator<(const string& s)
{
	size_t i1 = 0;
	size_t i2 = 0;
	int num = _size > s._size ? s._size : _size;  //得到两者最小的长度
	while (i1 < num && i2 < num)
	{
		if (_str[i1] < s._str[i2])		//只要不相等就返回
		{
			return true;
		}
		else if(_str[i1] > s._str[i2]) {
			return false;
		}
		else
		{
			++i1;		//该字符相当,那么继续++
			++i2;
		}
	}
	return _size < s._size;  //现在退出循环,说明前num个字符都相等,如果此时_size<s._size 那么返回真,反之返回假
}
bool operator==(const string& s)
{
	return _size == s._size && memcmp(_str, s._str, _size > s._size ? s._size : _size) == 0;  //两者字符长度相等,且通过memcpy返回值是否为0来判断函数返回值
}
//我们把 _size == s._size 放在前面,那么后面只需要memcpy(_str,s._str,_size)==0即可
bool operator<=(const string& s)
{
	return *this < s || *this == s;
}
bool operator>(const string& s)
{
	return !(*this <= s);
}
bool operator>=(const string& s) 
{
	return !(*this < s);
}
//访问指定下标的字符
//operator[]的实现
// 
char& operator[](size_t pos)
{
	//可读写   pos表示下标
	assert(pos < _size);
	return _str[pos];  //返回的是单个字符所以用char  且_str变量离开该函数依旧存在,可以使用&返回
}
string& operator=(const string& s)
{
	if (this != &s)
	{
		//调用拷贝构造函数 将s的数据给tmp
		string tmp(s);
				
		std::swap(_str, tmp._str);
		std::swap(_size, tmp._size);
		std::swap(_capacity, tmp._capacity);
		//进行交换,交换之后tmp在函数结束之后就会释放空间,但是其通过拷贝构造函数生成的新的string对象中的数值留给了*this对象
	}
	return *this;
}

流插入>>和流提取<<

//流提取
ostream& operator<<(ostream& out, const String::string& s)
{
	//就是将s字符串中的每一个字符都加载到out中
	for (auto ch : s)
	{
		out << ch;
	}
	return out;
}
//流插入
istream& operator>>(istream& in, String::string& s)
{
	//判断一个字符是否结束 按照空格或者\0来判断
	s.clear();
	char buff [128];
	
	char ch = in.get();//get 字符
	int i = 0;
	while (ch == ' '|| ch == '\n')
	{
		ch = in.get();  //处理缓冲区前面的空格和换行
	}
	while (ch != ' ' && ch != '\n')
	{
		buff[i++] = ch;
	//如果输入的数值在127之外
		if (i == 127)  //先i++  相当于从1到127
		{
			//留出来一个空间给\0 所以只能这样
			buff[i] = '\0';
			s += buff;
			i = 0;//要重置i
		}
		ch = in.get();
	}

	if (i != 0)
	{
		//如果i不0的话,那就是说数值长度在127之内 直接扩容
		buff[i] = '\0';
		s += buff;
	}
	return in;
}

总结:

  • 有关于插入以及添加字符、字符串的函数,都需要考虑容量的问题所以底层会有reserve,比如push_back、append、resize、insert、+=等函数
  • find的实现就是遍历,以及使用strstr函数,快速方便的得到下标
  • erase、insert等函数都会涉及到移动数组元素,erase向前移动,insert向后移动
  • 重载运算符的实现,可以方便我们使用string类

最后附上完整模拟实现string类的代码

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<assert.h>
#include<math.h>
//模拟实现string
namespace String {
	
	class string {
	public:

		//迭代器: string中的迭代器实际上就是指针

		typedef char* iterator;
		typedef const char* const_iterator;
		iterator begin()
		{
			//begin 表示的是string的首元素地址
			return _str;
		}
		iterator end()
		{
			//end 返回string最后一个元素的下一个位置,也就是'\0'
			return _str + _size;
		}
		const_iterator begin() const
		{
			//begin 表示的是string的首元素地址
			return _str;
		}
		const_iterator end() const
		{
			//end 返回string最后一个元素的下一个位置,也就是'\0'
			return _str + _size;
		}
		//默认构造函数
		//string() 
		//	:_str(new char[1])
		//	,size(0)
		//	,capacity(0)
		//{
		//	_str[0] = '\0';
		//}

		//带参构造函数  字符串
		//string(const char* str)
		//	:_str(new char[strlen(str)])
		//	,size(strlen(str))
		//	,capacity(strlen(str))
		//{
		//	//存储字符串
		//	strcpy(_str, str);
		//}

		//默认构造和带参构造合并,使用缺省参数

		string(const char* str="")  //""字符串自带'\0'
			:_str(new char[strlen(str)+1])
			, _size(strlen(str))
			, _capacity(strlen(str))
		{
			//存储字符串
			strcpy(_str, str);
		}

		//拷贝构造
		//string(const string& s)
		//{
		//	//深拷贝,就是创建一个大小一样的空间
		//	_str = new char[s._capacity + 1];
		//	strcpy(_str, s._str);
		//	_size = s._size;
		//	_capacity = s._capacity;
		//}
		string(const string& s)
		{
			_str = new char[s._capacity + 1];
			memcpy(_str, s._str,s._size + 1);
			_size = s._size;
			_capacity = s._capacity;
		}
		//析构函数
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		void Print() {
			cout << _str <<"\t" << _size <<"\t"<< _capacity << endl;
		}
		//size_t  无符号整型
		const char* c_str()
		{
			return _str;
		}

		//返回size
		size_t size() const  //const表示修饰this指针,也就是说只读,如果是const对象,也可以访问,普通用户相当于权限的缩小也可也访问
		{
			return _size;
		}

		//operator[]的实现
		// 
		char& operator[](size_t pos)
		{
			//可读写   pos表示下标
			assert(pos < _size);
			return _str[pos];  //返回的是单个字符所以用char  且_str变量离开该函数依旧存在,可以使用&返回
		}
		//对于const对象
		const char& operator[](size_t pos) const   //修饰const对象,前面const修饰的话,表示不可以被修改,后面const对象就修饰this指针  const String::string* this
		{
			//只读
			assert(pos < _size);
			return _str[pos];
		}
		//reserve  保留容量 可以扩容
		void reserve(size_t n)  //只是改变capacity 不改变size
		{
			if (n > _capacity)
			{
				//新建一个字符数组
				cout << "reserve->" << n << endl;
				char* new_str = new char[n + 1];
				//更改容量
				//strcpy(new_str, _str);
				memcpy(new_str, _str, _size + 1);
				delete[] _str;
				_str = new_str;
				_capacity = n;
			}
		}
		//push_back	
		void push_back(char ch)
		{
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			//加入字符
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}
		void append(const char* str)
		{
			if (_size + strlen(str) > _capacity)
			{
				reserve(_size + strlen(str));//至少保留_size + strlen(str)
			}
			//加入字符串
			memcpy(_str + _size, str, strlen(str)+1);//在'\0'位置(就是_str末尾)+str
			_size += strlen(str);
		}

		//实现+=  也是使用push_back 和append函数
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		string& insert(size_t pos, size_t n, char c)
		{
			//1.先判定pos是否正确
			assert(pos <= _size);
			
			//2.扩容
			reserve(_size + n);
			
			//3.在pos位置上开始挪动n个字符
			size_t end = _size;
			//因为如果pos为0的时候,无符号整型0减去1,end >= pos比较 为一个巨大值,使得该循环无法停止
			//npos是static变量,定义为-1;
			while (end >= pos && end != npos)
			{
				_str[end + n] = _str[end];
				--end;
			}
			
			//4.添加n个字符
			for (int i = 0; i < n; i++)
			{
				_str[pos + i] = c;
			}
			_size += n;
			return *this;
		}
		string& insert(size_t pos, const char*str)
		{
			//1.pos的判定
			assert(pos <= _size);

			//2.扩容
			int len = strlen(str);
			reserve(_size + len);
			
			//3.在pos位置上移动len个字符
			size_t end = _size;
			while (end >= pos && end != npos)
			{
				_str[end + len] = _str[end];
				--end;
			}

			//4.将str字符串的字符依次输入
			for (int i = 0; i < len; i++)
			{
				_str[pos + i] = str[i];
			}

			//5.最后_size增加
			_size += len;
			return *this;
		}

		//从pos位置开始,删除len长度字符
		string& erase(size_t pos = 0, size_t len = npos)
		{
			assert(pos <= _size);
			if (len == npos || pos + len >= _size)
			{
				//表示从pos位置删完
				_str[pos] = '\0';
				_size = pos;
			}
			else {
				//从pos位置删除len个字符
				//向前挪动
				size_t end = pos;
				while (end+len <= _size)  
				{
					_str[end] = _str[end + len];
					end++;
				}
				//此时end+len==_size
				//_str[end] = '\0';
				_size -= len;
			}
			return *this;
		}
	//find
		size_t find(char ch, size_t pos = 0) const
		{
			assert(pos < _size);
			for (size_t i = pos; i < _size; i++)
			{
				if (_str[i] == ch)
				{
					return i;
				}
			}
			return npos;  //没有找到返回-1;
		}
		size_t find(const char* s, size_t pos = 0) const
		{
			//使用strstr
			assert(pos < _size);
			const char* str = strstr(_str+pos, s);
			if (str)
			{
				return str-_str;//两个指针相减,得到的是地址的偏移量
			}
			else {
				return npos;
			}
		}
		//substr的实现
		string substr(size_t pos = 0, size_t len = npos)
		{
			assert(pos < _size);

			size_t n = len;
			//如果缺省len=npos 或者是截取的范围大于_size
			if (len == npos || pos + len >= _size)
			{
				n = _size - pos;  
			}
			//创建一个新的字符数组
			string new_str;
			new_str.reserve(n);

			for (size_t i = pos; i < n + pos; i++)
			{
				new_str += _str[i];
			}
			return new_str;
		}
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}
		//实现resize
		void resize(size_t n, char ch = '\0')
		{
			//两种情况,1.n<_size 直接赋值'\0' 2.判断是否扩容 
			if (n < _size)
			{
				_size = n;
				_str[_size] = '\0';
			}
			else
			{
				reserve(n);//让reserve来判断是否是需要扩容
				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;
				}
				_size = n;
				_str[_size] = '\0';
			}
		}
		//字符串比较按照ascii比较
		//bool operator<(const string& s)
		//{
		//	int num=memcmp(_str, s._str, _size > s._size?s._size : _size);
		//	//如果在最小长度下,前面数值小于后者 num返回的是负数
		//	
		//	return num == 0 ? _size < s._size : num < 0;
		//	//如果如果num为0,说明等于,且前者长度小于后者,返回真值,反之返回
		//}
		bool operator<(const string& s)
		{
			size_t i1 = 0;
			size_t i2 = 0;
			int num = _size > s._size ? s._size : _size;
			while (i1 < num && i2 < num)
			{
				if (_str[i1] < s._str[i2])
				{
					return true;
				}
				else if(_str[i1] > s._str[i2]) {
					return false;
				}
				else
				{
					++i1;
					++i2;
				}
			}
			return _size < s._size;
		}
		bool operator==(const string& s)
		{
			return _size == s._size && memcmp(_str, s._str, _size) == 0;
		}
		bool operator<=(const string& s)
		{
			return *this < s || *this == s;
		}
		bool operator>(const string& s)
		{
			return !(*this <= s);
		}
		bool operator>=(const string& s) 
		{
			return !(*this < s);
		}
		//string& operator=(const string& s)
		//{
		//	if (this != &s)
		//	{
		//		//如果不是同一个string
		//		//深拷贝
		//		char* new_str = new char[s._capacity + 1];
		//		memcpy(new_str, s._str, s._size);
		//		//删除原来地址
		//		delete[] _str;
		//		//新指向一个new_str
		//		_str = new_str;
		//		//更改容量和size
		//		_capacity = s._capacity;
		//		_size = s._size;
		//	}
		//	return *this;
		//}
		string& operator=(const string& s)
		{
			if (this != &s)
			{
				//调用拷贝构造函数 将s的数据给tmp
				string tmp(s);
				
				std::swap(_str, tmp._str);
				std::swap(_size, tmp._size);
				std::swap(_capacity, tmp._capacity);
				//进行交换,交换之后tmp在函数结束之后就会释放空间,但是其通过拷贝构造函数生成的新的string对象中的数值留给了*this对象
			}
			return *this;
		}
		size_t capacity()
		{
			return _capacity;
		}
		size_t size()
		{
			return _size;
		}
		//无穷递归的问题:反复调用堆栈
		// std::swap(*this,tmp)
		//定义属性
	private:
		char* _str;
		int _size;
		int _capacity;
	public:
		const static size_t npos;
	};
	const size_t string::npos = -1;
}
//流提取
ostream& operator<<(ostream& out, const String::string& s)
{
	//就是将s字符串中的每一个字符都加载到out中
	for (auto ch : s)
	{
		out << ch;
	}
	return out;
}
//流插入
istream& operator>>(istream& in, String::string& s)
{
	//判断一个字符是否结束 按照空格或者\0来判断
	s.clear();
	char buff [128];
	
	char ch = in.get();//get 字符
	int i = 0;
	while (ch == ' '|| ch == '\n')
	{
		ch = in.get();  //处理缓冲区前面的空格和换行
	}
	while (ch != ' ' && ch != '\n')
	{
		buff[i++] = ch;
	//如果输入的数值在127之外
		if (i == 127)  //先i++  相当于从1到127
		{
			//留出来一个空间给\0 所以只能这样
			buff[i] = '\0';
			s += buff;
			i = 0;//要重置i
		}
		ch = in.get();
	}

	if (i != 0)
	{
		//如果i不0的话,那就是说数值长度在127之内 直接扩容
		buff[i] = '\0';
		s += buff;
	}
	return in;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Alluxio技术分析

Alluxio技术分析 Alluxio: A Virtual Distributed File System Alluxio主要解决的基于磁盘的分布式存储层性能低下的问题&#xff0c;通过alluxio提供的分布式内存来加速数据分析。 Alluxio的这种通过内存加速数据的想法其实是有明确的使用场景的&#xff1a; Immutable da…

【WEB开发】Java获取高德POI(关键词搜索法)实现数据展示

前言 该篇文章是关键词搜索法获取高德poi&#xff0c;但鉴于无法突破200条记录的上限&#xff0c;所以采用了本方法进行区/县循环检索。开始之前我们首先需要明白一些常识 poi是兴趣点&#xff0c;它本身除了经纬度&#xff0c;还记录了一些信息&#xff0c;如名称、地址、联…

opencv-19 图像色彩空间转换函数cv2.cvtColor()

cv2.cvtColor() 函数是 OpenCV 中用于图像颜色空间转换的函数。它允许你将图像从一个色彩空间转换为另一个色彩空间。在 Python 中&#xff0c;你可以使用这个函数来实现不同色彩空间之间的转换。 函数的基本语法为&#xff1a; cv2.cvtColor(src, code[, dst[, dstCn]])参数…

消息队列(一)-- RabbitMQ入门(4)

RabbitMQ 其他知识点 幂等性 消息重复消费 消费者在消费MQ 中的消息时&#xff0c;MQ 已经把消息发送给消费者&#xff0c;消费者在给 MQ 返回 ack 时网络中断&#xff0c;故MQ 未收到确认消息&#xff0c;该消息会重新发给其他消费者&#xff0c;或网络重新连接后再次发给该消…

计算机科学cs/电子信息ei面试准备——数学基础/线性代数复习

1. 中值定理 中值定理是反映函数与导数之间联系的重要定理&#xff0c;也是微积分学的理论基础&#xff0c;在许多方面它都有重要的作用&#xff0c;在进行一些公式推导与定理证明中都有很多应用。中值定理是由众多定理共同构建的&#xff0c;其中拉格朗日中值定理是核心&…

登录和注册页面 - 验证码功能的实现

目录 1. 生成验证码 2. 将本地验证码发布成 URL 3. 后端返回验证码的 URL 给前端 4. 前端将用户输入的验证码传给后端 5. 后端验证验证码 1. 生成验证码 使用hutool 工具生成验证码. 1.1 添加 hutool 验证码依赖 <!-- 验证码 --> <dependency><groupId…

Android Studio Flamingo Logcat使用方式

旧版Android Studio突然打不开了&#xff0c;安装了新的Flamingo。习惯用Log.e看日志&#xff0c;突然发现logcat没有筛选下拉了。o(╥﹏╥)o 还是需要查看官方文档&#xff1a;https://developer.android.google.cn/studio/debug/logcat?hlzh-cn &#xff08;不知道为啥&…

jdk,jre和jvm三者的关系和区别

目录 一、三者的关系 二、JDK的概念 三、JRE的概念 四、JVM的概念 五、三者区别 一、三者的关系 从图中可以清楚地看到&#xff0c;他们之间的关系是JDK包含JRE, JRE又包含JVM。 因此&#xff0c;JDK包含JRE和JVM。 JDK JRE Java 开发工具包 [Java,Javac,Javadoc,Javap…

VS下c++解析pcap文件

一、pcap文件格式 https://www.cnblogs.com/Chary/articles/15716063.html 接口协议&#xff08;四&#xff09;&#xff1a;以太网&#xff08;Ethernet&#xff09;学习&#xff08;一&#xff09;&#xff1a;协议_以太网协议_QNee的博客-CSDN博客 二、代码 pcapParser.h #…

自然语言处理实战项目13-基于GRU模型与NER的关键词抽取模型训练全流程

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下自然语言处理实战项目13-基于GRU模型与NER的关键词抽取模型训练全流程。本文主要介绍关键词抽取样例数据、GRU模型模型构建与训练、命名实体识别(NER)、模型评估与应用&#xff0c;项目的目标是通过训练一个GRU模型…

npm i babel-plugin-import -D之后报错

替换modules/.bin/XX文件 1.vue-cli-service #!/bin/sh basedir$(dirname "$(echo "$0" | sed -e s,\\,/,g)")case uname in*CYGWIN*) basedircygpath -w "$basedir";; esacif [ -x "$basedir/node" ]; then"$basedir/node"…

Audio Clip

Unity支持的音频格式&#xff1a; aiff/wav&#xff1a;适用于较短声音片段 mp3/OGG:适用于较长的音乐片段 多声道强制转为单声道&#xff0c;减小所占内存。 勾选后会对声音有优化 在后台加载声音 Load Type&#xff1a; 第一个&#xff0c;以不压缩的形式存在内存&#…

深度学习(二)

目录 一、神经网络 整体架构: 架构细节: 神经元个数的影响: 神经网络过拟合解决: 卷积网络 整体架构: 卷积层 边缘填充 特征尺寸计算 池化层 特征图变化 递归神经网络 一、神经网络 整体架构: 图中分别为输入层、隐层1、隐层2、输出层 通过输入层输入某数值&#xf…

Java版本企业电子招投标采购系统源码——功能模块功能描述+数字化采购管理 采购招投标

功能模块&#xff1a; 待办消息&#xff0c;招标公告&#xff0c;中标公告&#xff0c;信息发布 描述&#xff1a; 全过程数字化采购管理&#xff0c;打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力&#xff0c;为外部…

FAQ文档的重点注意事项!别踩坑了

在很多优秀的大企业中&#xff0c;FAQ文档是企业运营管理中不可或缺的重要部分。但是也仅限大企业&#xff0c;很多企业目前还是没有这个意识的。一方面原因是因为缺乏这个客户服务的意识&#xff0c;另一方面也和技术水平不足有关。但是其实现在有不少的第三方搭建平台可以帮助…

【Element-ui】学习与使用

网站快速成型工具Element&#xff0c;一套为开发者、设计师和产品经理准备的基于vue2.0的桌面端组件库 安装 npm i element-ui -S 在项目中安装element-ui&#xff0c;安装了以后查看package.json中的依赖中有没有element-ui的版本&#xff0c;如果有&#xff0c;则说明安装成功…

react 在build读取env 数据

默认会读取.env 文件 npm install dotenv --save npm install dotenv-cli --save-dev例如读取.env.test "build:test": "dotenv -e .env.test react-app-rewired build",.env.test REACT_APP_CURRENTMODE devREACT_APP_Public_Path "https://baid…

如何利用JMeter测试带有Token参数的POST接口

JMeter有一个很强大的功能就是可以用来做接口测试。 接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间的相互逻辑依赖关系…

TikTok广告数据不好?收下这份常见问题自查手册!

你是一位跨境卖家吗&#xff1f;你是否在TikTok上投放过广告&#xff1f; 如果你的答案是肯定的&#xff0c;那么你可能遇到过一些困扰。比如&#xff0c;你的广告为什么不起量&#xff1f;为什么突然掉量了&#xff1f;为什么成本上升了&#xff1f;到底是哪里出了问题&#…

基于linux下的高并发服务器开发(第二章)- 2.22 setitimer 定时器函数

#include <sys/time.h> int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value); - 功能&#xff1a;设置定时器&#xff08;闹钟&#xff09;。可以替代alarm函数。精度微妙us&#xff0c;可以实现周期性定时 - 参数&#xff1a; -…