深入篇【C++】string类的常用接口介绍:标准库中的string类 【万字总结】

深入篇【C++】string类的常用接口介绍:标准库中的string类

  • Ⅰ.string类介绍
  • Ⅱ.string类的常用接口
      • ①.string类对象的常用构造
          • 1.string()
          • 2.string(const char*ch)
          • 3.string(const string& str)
          • 4.string(size_t n,char c)
          • 5.string(const string& str,size_t pos,size_t len=npos)
        • 【总结】
      • ②.string类对象的容量操作
          • 1.size
          • 2.capacity
          • 3.clear
          • 4.empty
          • 5.reserve
          • 6.resize
        • 【总结】
      • ③.string类的对象的访问与遍历
          • 1.opeartor[]
          • 2.begin/end
          • 3.rbegin/rend
          • 4.范围for
        • 【总结】
      • ④.string类对象查看与修改操作
          • 1.push_back
          • 2.append
          • 3.operator+=
          • 4.insert
          • 5.erase
          • 6.replace
          • 7.c_str
          • 8.substr
          • 9.find
          • 10.rfind
        • 【总结】
      • ⑤.string类非成员函数
  • Ⅲ.牛刀小试:练习string类
    • 1.字符串相加
    • 2.字符串最后一个单词的长度

Ⅰ.string类介绍

string类文档介绍----cplusplus
在这里插入图片描述

1.C语言中,strxxx 是系列库函数而在C++中string是一个类。
2.string是管理字符数组的类
3.标准的字符串类提供了对此类对象的字符类型,其接口类似于标准字符容器的接口,但添加了专门操作单字节字符字符串的设计特性。
4.string类其实是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数。
5.这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字节的序列,这个类的所有成员以及它的迭代器,将仍然按照字节来操作,而不是实际编码的字符。

总结:
1.string其实表示字符串的字符串类
2.string在底层其实是basic_string模板的实例化。

typedef basic_string<char, char_traits, allocator> string;

3.在使用string类时必须要包含头文件和using namespace std;

Ⅱ.string类的常用接口

①.string类对象的常用构造

在这里插入图片描述

1.string()

无参构造函数,不需要参数就可以构造出一个对象,该对象其实就是一个空字符串。
所以可以用空字符串来构造string对象。

int main()
{
	//无参构造函数
	string s1;
	cout << s1 << endl;
}

在这里插入图片描述

2.string(const char*ch)

该构造函数的参数是一个字符串。这说明我们可以用一个字符串来构造string对象。

int main()
{
	string s2("小陶");//用字符串来构造对象
	string s3("hello world");
	cout << s2 << endl;
	cout << s3 << endl;
}

在这里插入图片描述

3.string(const string& str)

该构造函数的参数是string类对象,这说明我们可以用一个string对象来构造string对象。

int main()
{
	string s2("小陶");
	string s3("hello world");
	string s4(s2);
	cout << s4 << endl;
	string s5(s3);
	cout << s5 << endl;
}

在这里插入图片描述

4.string(size_t n,char c)

该构造函数的参数有两个,一个是正数n一个是字符c。
该构造函数实现的功能是用n个字符c构造对象。这说明我们可以用n个字符c来构造string对象。

int main()
{
	
	string s4(10, '*');
	//用10个'*'来构造对象s4
	cout << s4 << endl;

	string s5(10, 'x');
	//用10个'x'来构造对象s4
	cout << s5 << endl;
}

在这里插入图片描述

5.string(const string& str,size_t pos,size_t len=npos)

从参数上我们就可以推断出该构造函数的功能是什么了。
从字符串str第pos位置上拷贝长度为len的字符串用来构造对象
注意最后一个参数给了缺省值npos,npos是一个很大的数值,当我们给定长度时,就从pos位置截取len长度,当我们不给定长度时,就默认从pos位置一直往后截取完。

int main()
{
	string s3("hello world");//字符串
	
    string s6(s3, 6, 5);///从某个字符串某个位置拷贝n各字符
	//从字符串s3第六个位置往后拷贝五个字符
	cout << s6 << endl;

	//当给定长度为5时,就截取5个字符,当不给定长度时,从pos位置一直往后截取
	//因为缺省参数是一个很大的数值。
	
	string s7(s3, 6);
	cout << s6 << endl;
	//
}

在这里插入图片描述

【总结】

常见构造函数功能
string()用空来构造string对象,即空字符串
string(const char* ch)用C-string来构造string对象
string(const string& str)用string类对象来构造string对象,即拷贝构造函数
string (size_t n,char c)对象中包含n个字符c,即用n个字符c来构造对象
string(const stirng&str,size_t pos,size_t len=npos)从对象str的pos位置上截取长度为len的字符串来构造

②.string类对象的容量操作

在这里插入图片描述

1.size

用来计算对象的有效长度。它是string类的成员函数,所以调用它只需要对象即可。
C++中其实还有一个成员函数length也是用来计算对象长度的,与size功能是完全一样,只是名字不一样。
类似的成员函数还有max_size,这是用来计算对象可以拥有的最大长度。
在这里插入图片描述


int main()
{
	string s1("hello xiaotao");
	cout << s1.size() << endl;
	cout << s1.length() << endl;
	cout << s1.max_size() << endl;
}

在这里插入图片描述

2.capacity

用来计算对象的容量大小,它是string类的成员函数,对象可以直接调用。

int main()
{
	string s1("hello xiaotao");
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;

	string s2;
	cout << s2.size() << endl;
	cout << s2.capacity() << endl;

}

在这里插入图片描述

我们可以发现对象s1和对象s2的容量都为15,这说明对象的起始容量就为15。
当一个对象被创建时,系统就会给它申请15大小的空间。而当我们的对象的长度大于对象本身容量的大小时,就需要对象扩容。
那string类对象的扩容方式是如何的呢?每次对象扩容多少呢?

int main()
{
	string s1;
	cout <<"大小:"<< s1.size() << endl;

	cout <<"起始容量:"<< s1.capacity() << endl;

	size_t old = s1.capacity();
	for (size_t i = 0; i < 100; i++)
	{
		s1 += 'x';
		if (old != s1.capacity())
		{
			cout << "扩容" << s1.capacity() << endl;
			old = s1.capacity();
		}
	}
}

在这里插入图片描述
我们可以看到在VS下string对象的每次扩容申请的空间都是不一样的,比较奇怪。
第一次扩容16大小,第二次扩容26大小,第三次扩容23大小,第4次扩容35大小。

3.clear

用来清空对象的有效字符
要注意clear只是清空了有效字符的大小(size)变成0,而容量空间大小(capacity)是不变的。

int main()
{
	string s1("hello xiaotao");
    cout << s1.size() << endl;
    cout << s1.capacity() << endl;
     s1.clear();
     //清理数据,但内存空间还在
     cout << s1.size() << endl;
     cout << s1.capacity() << endl;
}

在这里插入图片描述

4.empty

用来检查字符串是否被释放成空串,如果是空串则返回true、否则返回false。

int main()
{
	string s1("hello xiaotao");
    
	cout << s1.empty() << endl;
     s1.clear();
     //清理数据,但内存空间还在
     
	 cout << s1.empty() << endl;
}

在这里插入图片描述

5.reserve

用来为对象预留空间。
可以提前为对象申请n大小的空间。
当n比原空间(capacity)大时,就会进行扩容。
当n比原空间(capacity)小时,不一定会缩容,这取决于不同的平台。

而reserve的好处就是当我们知道需要多少空间时,就可以提前将空间开好,这样就可以避免不断扩容,就不存在扩容问题了。

int main()
{
	string s1("hello world");
	//开空间
	s1.reserve(100);
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
}

在这里插入图片描述
用reserve提前开辟的空间不一定准确,可能会开大点,但不可能开小的。就比如reserve(100)提前申请100个空间,系统分配了111个空间给它。

void TestPushBackReserve()
{
	string s;
	s.reserve(100);//提前开辟100大小空间
	size_t sz = s.capacity();
	cout << "now capacity:" << sz << '\n';

	cout << "making a grow:'\n";
	for (int i = 0; i < 100; i++)
	{
		s.push_back('c');
		if (sz != s.capacity())
		cout << "capacity changed:" << sz << '\n';
	}
	
	cout << "later capacity:" << sz << '\n';
	s.reserve(10);
	cout << s.capacity() << endl;
}

在这里插入图片描述
可以通过上面的代码知道reserve确实为对象开辟的空间,并且当开辟空间小于原空间大小时,原空间大小是不变的。

6.resize

在这里插入图片描述

将对象原来的有效字符的个数改成n个,多出来的空间用字符c来填充。
该成员函数有两种重载形式,一种是resize(size_t n)一种是resize(size_t n,char c);
这两个区别在于多出来的空间,resize(size_t n)是用0来填充
resize(size_t n,char c)是用字符c来填充。

int main()
{
	string s1("hello world");
	s1.resize(20);
	cout << s1 << endl;

	string s2("hello world");
	s2.resize(20,'x');
	cout << s2 << endl;
}

reserve和resize都是可以操纵空间大小的,那它们有什么区别呢?
1.reserve是单纯开空间
2.resize是开空间+填值初始化
3.reserve是只能影响到capacity容量大小,影响不到size的大小。
而resize既能影响capacity容量大小,又能影响size的大小。
因为当resize中的n大于size时,则会扩大size的大小至n,并且容量capacity也会跟着变化。
但当resize中的n小于size时,则只会缩数据个数,不会缩空间大小,也就是只缩小size大小,不改变capacity的大小。

int main()
{
	string s1("hello world");
	//单纯开空间
	s1.reserve(100);
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;

	//开空间+填值初始化
	s1.resize(200, 'x');
	cout << s1 << endl;
	//当resize在改变元素个数时,如果元素的个数增多,可能会改变
	//底层容量的大小,如果是将元素个数减少。底层空间总大小不变
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;

	s1.resize(20);
	cout << s1.size() << endl;//这个数据大小改变
	cout << s1.capacity() << endl;//但容量不会改变
}

【总结】

成员函数功能
size计算字符串的字符长度
capacity计算空间总大小
clear清空对象数据
empty判断字符串是否被清空
reserve为字符串预留空间
resize将字符串的大小更改成n

1.size()和length()底层实现原理是一样的,出现size()原因是为了于其他容器的接口保持一致,一般都用size().
2.clear()只是清空数据的个数,不会改变capacity的大小的。
3.resize(n)和resize(n,char c)都是用来更改字符大小的。不同的是,当n大于size时,多出来的空间rezie(n)用0初始化,resize(n,char c)用字符c来初始化。当n小于size时,size的大小会改变,但capacity的大小不会改变
4.想单纯的开空间就使用reserve、要想即开空间又初始化,那就用resize。

③.string类的对象的访问与遍历

1.opeartor[]

该成员函数其实就是对[]进行运算符重载。返回pos位置的字符,使用方法跟数组是一样的。都是根据下标来进行访问。

int main()
{
	char ch[] = "hello world";
	cout << ch[2] << endl;
	string s1("hellor world");
	cout << s1[2] << endl;
}

在这里插入图片描述
虽然看起来很像,但它们的底层实现是不一样的,不能混成一块。
char ch[2]的底层实现的原理是*(ch+2).
而s1[2]底层实现的原理是s1.operator[](2).

//如何遍历string对象呢?
  cout << s1.size() << endl;
	for (int i = 0; i < s1.size(); i++)
	{
		s1[i]++;
	}
	
	s1[0]--;//可以根据下标一样来修改string对象
	//下标+[]
	for (int i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << endl;;
	}

注意:下标+[ ]这种访问方式只能用于string和vector,list是无法使用的,因为只有顺序表类型的才可以用这种方式访问,而链表类型的是无法使用该方式访问。但是使用迭代器访问是通用的,什么类型都可以使用迭代器进行访问。

这样还要注意一点,当使用下标访问时,如果访问越界了,即非法访问了。它是会断言报错的。

int main()
{
	string s1("hello world");
	
	for (int i = 0; i < 20; i++)
	{
		s1[i]++;
	}
	
}

在这里插入图片描述

int main()
{
	try {
		string s1("hello world");
		s1.at(0) = 'x';//在某个位置修改
		cout << s1 << endl;
		s1[0] = 'h';
		cout << s1 << endl;
		//s1[15];//如果越界非法访问了,这样会assert警告的。
		///暴力处理
		s1.at(15);//温和的错误处理
	}//会抛异常
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}
}
2.begin/end

begin可以获得第一个字符的迭代器,end可以获得最后一个字符下一个位置的迭代器。
什么是迭代器呢?
迭代器(iterator)就像一个指针,我们可以暂且将它看作指针类型,但有时候不一定是指针类型。
使用迭代器的方法:

int main()
{
	string s1("hello xiaotao");
	//迭代器--->指针
	string::iterator it = s1.begin();
	while (it != s1.end())
	{
		//写
		(*it)--;
		++it;//类似于指针
	}
	while (it != s1.end())
	{
		//读
		cout << *it << " ";
		++it;
	}
}

begin和end又称为正向迭代器,是从开头到结尾进行访问的。

3.rbegin/rend

rbegin是获取最后一个字符的迭代器,rend是获取第一个字符前一个位置的迭起器。
rbegin和rend可以用来进行从后往前遍历,所以又称为反向迭代器。

int main()
{
	string s1("hello world");
	string::reverse_iterator rit = s1.rbegin();//反向迭代器
	while (rit != s1.rend())
	{
		
		cout << (*rit) << endl;//反向遍历
		++rit;
	}
}

在这里插入图片描述

4.范围for

范围for是C++11支持更简洁的新遍历方式。

int main()
{
	string s1("hello xiaotao");
	for (auto ch : s1)
	{
		cout << ch << endl;
	}
}

它会依次将s1中的字符赋给ch,ch会自动识别类型。

1.范围for的底层本质上其实就是替换成迭代器。如果不支持迭代器的用法就不能支持范围for。
为什么范围for可以使用呢?
因为任何容器都支持迭代器的用法,并且用法都是类似的。

	任何容器都支持迭代器,并且用法是类似的。
	vector<int> v;
	vector<int>::iterator vit = v.begin();
	while (vit != v.end())
	{
		cout << *vit << endl;
		++vit;
	}
	
	list<int> lit;
	list<int>::iterator lit = lit.beign();
	while (lit != lit.end())
	{
		cout << (*lit) << endl;
		++lit;
	}

2.所以迭代器(iterator)提供了一种统一的方式进行访问和修改容器的数据。
3.迭代器可以跟算法进行配合。
因为数据封装在容器里面。算法是无法对容器进行修改的,所以要利用迭代器。这样算法就可以通过迭代器去处理容器中的数据,比如reverse和sort。

	//迭代器跟算法进行配合
	reverse(s1.begin(), s1.end());
	sort(s1.beign(), s1.end());

4.迭代器的类型有多种。
比如const修饰的对象就无法使用普通的迭代器来遍历。
而需要使用const迭代器。

void Func(const string& s)
{
	string::const_iterator it = s.begin();
	while (it !=s.end())
	{
		cout << *it << endl;
		++it;
	}
void Func(const string& s)
{
	string::const_reverse_iterator rit = s.rbegin();
	auto rit = s.rbegin();
	while (rit != s.rend())
	{
		cout << *rit << endl;
		++rit;
	}
}

在这里插入图片描述

【总结】

成员函数功能
operator[ ]返回pos位置的字符,利用下标进行访问和遍历
begin/end正向迭代器,可以用来遍历和访问
rbegin/rend反向迭代器,可以倒着遍历
范围forC++11支持的更简洁的遍历方式,任何容器都可以使用

④.string类对象查看与修改操作

1.push_back

在字符串后面尾插一个字符
在这里插入图片描述

int main()
{
	//增操作
	string s1("helllo");
	//尾插一个字符
	s1.push_back('6');
}
2.append

在字符串后面尾插一个字符串
在这里插入图片描述

int main()
{
	//增操作
	string s1("helllo");
	//尾插一个字符
	s1.push_back('6');
	//尾插一个字符串
	s1.append("world");
}
3.operator+=

operator+=是对+=运算符重载
它比push_back和append尾插更方便。而且接口很多
可以尾插一个字符,一个字符串,一个stirng对象等。


int main()
{
	//增操作
	string s1("helllo");
	//尾插一个字符
	s1.push_back('6');
	//尾插一个字符串
	s1.append("world");
	//push_back是用来尾插字符的
	//append是用来插入字符串的
	//但是还有一种方法直接尾插,+=
	s1 += '6';
	s1 += "6666";

问题: 如何将整形转化成string类型呢?

int main()
{	//要求将x转化为string对象?
	size_t x = 0;
	cin >> x;

	string xstr;
	while (x)
	{
		size_t val = x % 10;
		xstr += (val + '0');
		x /= 10;
	}
	//逆转
	reverse(xstr.begin(), xstr.end());
	cout << xstr << endl;
}
4.insert

在字符串的头部插入字符/字符串
在这里插入图片描述

int main()
{
	string s1("hello world");
	//往头部插入10个’x
	s1.insert(0, 10, 'x');
	//insert(位置,个数,字符);
	cout << s1 << endl;
	
	//从第五个位置插入world
	s1.insert(5, "world");
	//insert(位置,字符串)
	cout << s1 << endl;
	
	s1.insert(0, 10, 'x');
	//从第十个位置插入10个y
	s1.insert(s1.begin() + 10, 10, 'y');
	cout << s1 << endl;
}
5.erase

删除字符串
在这里插入图片描述
默认从第第一个位置开始删除。
erase(位置,删除的长度),注意删除的长度给了缺省值npos,这说明当不给定长度时,会默认将pos位置后面的字符全部删除掉。
erase也可以使用迭代器进行删除。删除的是迭代器位置上的字符。

int main()
{
	string s1("hello world");
	s1.erase(5, 1);
	//从第5个位置删除1个字符
	cout << s1 << endl;

	
	//从第五个位置往后全部删除
	s1.erase(5);
	cout << s1 << endl;
	//erase(位置,n个字符=nps缺省值很大的数

	string s2("hello world");
	s2.erase(0, 1);//相当于头删了
	cout << s2 << endl;
	
	s2.erase(s2.begin());
	//删除这个迭代器位置上的字符
	cout << s2 << endl;

  
}
6.replace

替换字符串中的字符
replace(替换的位置,要替换的长度,替换成的字符串)
在这里插入图片描述

int main()
{
	//将world替换成xxxxxxxxxxxx
	string s1("hello world hello xiaotao");
	s1.replace(6, 5, "xxxxxxxxxxxxxxxxxxx");
	//replace(位置,替换的个数,替换成的字符串)
	s1.replace(6, 20, "666");
	cout << s1 << endl;

	//将s2中所以空格全部替换成20%
	string s2 = "hello world hello xiaotao";
	string s3;
	for (auto ch : s2)
	{
		if (ch != ' ')
		{
			s3 += ch;
		}
		else
		{
			s3 += "20%";
		}
	}
	s2 = s3;
	cout << s2 << endl;
}
7.c_str

返回C格式的字符串
这个接口有什么用呢?为什么要返回C格式的字符串呢?
因为在做项目时我们要和C的一些接口函数进行配合才可以使用。
比如某个函数必须要使用C格式的字符串,那么就需要将string类型的字符串转化成C格式的字符串

int main()
{
	//适用于一些C的一些接口函数配合
	string filename = "test.cpp";
	filename += ".zip";
	FILE* fout = fopen(filename.c_str(), "r");//
	//调用这个函数必须要C的字符类型
}
8.substr

从字符串pos位置开始截取n个字符,如何将其返回
substr(位置,长度)注意长度参数给了缺省值,所以如果不给定长度,则默认从pos位置往后一直截取字符。

在这里插入图片描述

int main()
{
	string  s1("hello xiaotao");
	string s2, s3;
	s2 = s1.substr(6);
	//不给位置,则默认从该位置往后一直截取字符,直到截取完毕
	cout << s2 << endl;
	s3 = s1.substr(6, 7);
	//从第6个位置截取7个字符
	cout << s3 << endl;

}

在这里插入图片描述

9.find

从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置。
find(字符,位置),如果不给位置,默认从第一个位置开始往后找。

在这里插入图片描述

int main()
{
	string s1("hello xiaotao");
	cout << s1.find('x') << endl;
	//不给位置,则默认从第一个位置开始往后面找

	cout << s1.find('o', 5);
	//从第5个位置往后找字符'o'找到后返回该字符的位置
}

在这里插入图片描述

我们利用一个题目来深入理解find。
【切割字符串】
将任何一个网站的协议,域名,资源名分割处理:


int main()
{
	string url = "ftp://www.baidu.com/?tn=65081411_1_oem_dg";

	//协议  域名  资源名

	size_t pos1 = url.find("://");
	string protocol;
	if (pos1 != string::npos)
	{
		protocol = url.substr(0, pos1);
		//从位置0这个地方截取pos1长度的字符串赋给protocol
	}
	cout << protocol << endl;
	size_t pos2 = url.find('/', pos1 + 3);
	string domain;//域名
	string uri;//资源名
	if (pos2 != string::npos)
	{
		domain = url.substr(pos1 + 3, pos2 - (pos1 + 3));
		uri = url.substr(pos2+1);
	}
	cout << domain << endl;
	cout << uri << endl;
}

在这里插入图片描述

10.rfind

与find类似但不同的是find是从前往后找,而rfind是从后往前找。
从字符串pos位置往前找字符c,找到了返回该字符所在位置。

如果不给位置,默认从最后一位开始往前找。
在这里插入图片描述

int main()
{
	string s1("hello xiaotao");
	cout << s1.rfind(' ') << endl;
	//找到' '返回该字符的位置
}

【总结】

成员函数功能
push_back在字符串后面尾插一个字符
append在字符串后面追加一个字符串
operatro+=在字符串后面追加str
insert头插字符/字符串
erase尾删字符/字符串
replace替换字符串中的字符
c_str返回C格式的zifc
substr从pos位置截取长度为n的字符串
find从pos开始往后查找字符c,返回该字符的位置
rfind从pos位置开始往前查找字符c,返回该字符的位置

⑤.string类非成员函数

非成员函数功能
operator+对运算符+的重载,尽量少用,因为传值返回,深拷贝效率低
operator<<输入运算符符重载,使string类对象可以直接输入
operator>>输出运算符重载,使string类对象可以直接输出
getline可以获取一行字符串,坚持一行不结束,遇到换行才结束
relational operators各种大小比较运算符重载,使string对象可以直接比较
to_string将其他类型转化为字符串类型

在这里插入图片描述

int main()
{
	//将整形转化成字符类型
	string stri = to_string(1234);
	//将浮点型转化成字符类型
	string strd = to_string(6.11);
}

Ⅲ.牛刀小试:练习string类

1.字符串相加

字符串相加—力扣
第一种:头插方式

class Solution {
public:
    string addStrings(string num1, string num2) {
     int end1=num1.size()-1,end2=num2.size()-1;
     int carry=0;
     string strRet;
     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;
        carry=ret/10;
        ret%=10;

      strRet.insert(strRet.begin(),ret+'0');
       --end1;
       --end2;
     }
     if(carry==1)
     {
         strRet.insert(strRet.begin(),'1');
     }
     return strRet;
    }
};

第二种:尾插+逆转

class Solution {
public:
    string addStrings(string num1, string num2) {
     int end1=num1.size()-1,end2=num2.size()-1;
     int carry=0;
     string strRet;
     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;
        carry=ret/10;
        ret%=10;

       strRet+=ret+'0';
      
       --end1;
       --end2;
     }
     if(carry==1)
     {
         strRet+='1';
     }
     reverse(strRet.begin(),strRet.end());
     return strRet;
    }
};

2.字符串最后一个单词的长度

字符串最后一个单词的长度—牛客

#include <iostream>
using namespace std;
#include <string>

int main()
{
    string s1;
    while(getline(cin,s1))
    {
       size_t pos=s1.rfind(' ');
       cout<< s1.size()-(pos+1)<<endl;
    }
    return 0;
}

不能使用流输入cin来输入s1,因为cin和scan遇到空格和换行都会结束,无法完全读取成功,还有一部分会留在缓冲区。所以必须要使用getline来获取完整的一行字符串。

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

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

相关文章

第四章 Linux网络编程 4.1 网络结构模式 4.2MAC地址、IP地址、端口

第四章 Linux网络编程 4.1 网络结构模式 C/S结构 简介 服务器 - 客户机&#xff0c;即 Client - Server&#xff08;C/S&#xff09;结构。C/S 结构通常采取两层结构。服务器负责数据的管理&#xff0c;客户机负责完成与用户的交互任务。客户机是因特网上访问别人信息的机器…

Seata客户端的启动过程 学习记录

Seata客户端的启动过程 1.自动装配4个配置类 将在SpringBoot启动时往容器中添加4个类 1. 自动配置类 SeataAutoConfiguration SeataAutoConfiguration将会往容器中添加两个bean failureHandler 事务处理失败执行器globalTransactionScanner failureHandler failureHandle…

DBA 抓包神器 tshark 测评

想窥探神秘的网络世界的奥秘&#xff0c;tshark 助你一臂之力&#xff01; 作者&#xff1a;赵黎明 爱可生 MySQL DBA 团队成员&#xff0c;熟悉 Oracle、MySQL 等数据库&#xff0c;擅长数据库性能问题诊断、事务与锁问题的分析等&#xff0c;负责处理客户 MySQL 及我司自研 D…

计算机组成原理(六)指令系统

一、指令的基本格式 1.1机器指令的相关概念 指令集(Instruction Set) 某机器所有机器指令的集合 *定长指令集 指令集中的所有指令长度均相同!取指令控制简单*不定长指令集 指令集中的所有指令长度有长、有短 操作码 (1)长度固定 用于指令字长较长的情况RISC 如IBM370操作码8位…

2023 年前端 Web 发展趋势

虽然就个人观点&#xff0c;我觉得 Web 开发在最近几年都没什么进展&#xff08;2016 年至 2021 年&#xff09;&#xff0c;但在刚刚过去的 2022 年中确实又出现了一些新的技术。在本文中&#xff0c;我想跟大家聊聊自己看到的最新 Web 开发的发展趋势。相信这波浪潮会继续激发…

RHCE shell 作业一

1. 设置邮箱 [rootserver ~]# yum install s-nail -y [rootserver ~]# vim /etc/s-nail.rc 编写脚本 [rootserver ~]# vim homework1.sh 设置定时任务 [rootserver ~]# vim /etc/crontab 2. [rootserver ~]# vim homework2.sh 测试&#xff1a; 3. [rootserve…

一文让你了解appium自动化的工作原理

目录 前言&#xff1a; 一、Appium加载的过程图解 二、初步认识appium工作过程 三、bootstrap介绍 四、所使用的技术 五、Capabilities 六、自我理解的工作原理 前言&#xff1a; Appium是一个流行的开源自动化测试框架&#xff0c;支持移动应用程序的自动化测试。 一…

进程管道:父进程和子进程

在接下来的对pipe调用的研究中&#xff0c;我们将学习如何在子进程中运行一个与其父进程完全不同的另外一个程序&#xff0c;而不是仅仅运行一个相同程序。我们用exec调用来完成这一工作。这里的一个难点是&#xff0c;通过exec调用的进程需要知道应该访问哪个文件描述符。在前…

MMPretrain

title: mmpretrain实战 date: 2023-06-07 16:04:01 tags: [image classification,mmlab] mmpretrain实战 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ccTl9bOl-1686129437336)(null)] 主要讲解了安装,还有使用教程.安装教程直接参考官网.下面讲…

pikachu靶场-../../(目录遍历)

目录遍历, 也叫路径遍历, 由于web服务器或者web应用程序对用户输入的文件名称的安全性验证不足而导致的一种安全漏洞&#xff0c;使得攻击者通过利用一些特殊字符就可以绕过服务器的安全限制&#xff0c;访问任意的文件 (可以是web根目录以外的文件&#xff09;&#xff0c;甚至…

pytorch深度学习框架—torch.nn模块(二)

pytorch深度学习框架—torch.nn模块&#xff08;二&#xff09; 激活函数 pytorch中提供了十几种激活函数&#xff0c;常见的激活函数通常为S形激活函数&#xff08;Sigmoid&#xff09;双曲正切激活函数(Tanh) 和线性修正单元&#xff08;ReLu&#xff09;激活函数等 层对应的…

Linux笔记

版本用的是CentOS7最min版 安装JDK&#xff1a;安装上传工具包&#xff1a;自动安装 yum install lrzsz -y 上传本地文件&#xff1a; rz -be 解压jdk&#xff1a; tar -zxvf jdk-8u371-linux-x64.tar.gz -z 用gzip来压缩/解压缩文件&#xff0c;加上该选项后可以将档案…

关于 vue2 后台管理系统构建 vue2+mock.js 的经典案例

一&#xff0c;初识 Mock.js 1.什么是 mock.js: 主要是模拟数据生成器&#xff0c;可以生成随机数据&#xff0c;拦截器 Ajax 请求 2.为什么要使用 mock.js 由于很多学生在学习过程中&#xff0c;后端还没有做好接口&#xff0c;写好接口文档&#xff0c;有了mock.js 前端就…

如何识别二叉树的“亲戚”?——探秘判断子树的奥妙

本篇博客会讲解力扣“572. 另一棵树的子树”的解题思路&#xff0c;这是题目链接。先来审题&#xff1a; 本题的思路是&#xff1a;使用递归&#xff0c;把大问题化作小问题。 先来思考&#xff1a;如何判断q是不是p的子树呢&#xff1f; q是p的子树有3种情况&#xff0c;分别…

MyBatis操作数据库(查询功能)

目录 一、MyBatis的概念 二、配置MyBits环境 三、 MyBatis连接数据库查询操作&#xff08;示例&#xff09; 创建MySQL数据库表 配置MyBatis 配置连接数据库和MyBatis xml文件 ​编辑 四、添加业务代码 实体类entity 数据持久层mapper 创建接口类 创建xml文件 服务层…

Spring Security--会话管理

就像登录qq一样&#xff0c;一个手机登录会将另外一个手机挤下线&#xff0c;这个就叫会话管理。 这个东西非常简单&#xff0c;在默认情况下可以登录n多次&#xff0c;一旦开启&#xff0c;就不允许登录多个。 什么是一个会话。 我们简单理解就是一个浏览器的同一个用户算一…

汉明码(Hamming Code)底层原理

汉明码&#xff08;Hamming Code&#xff09;底层原理 3Blue1Brown&#xff1a;Hamming Code【Part1】 3Blue1Brown&#xff1a;Hamming Code【Part2】 Hamming Code如何检查错误和定位错误&#xff1f; 检查错误通过奇校验或偶校验确定是否发生错误 定位错误通过依次对行和列…

将递归函数转成非递归函数的通用方法

看到过一道非常不错的面试题&#xff1a;不支持递归的程序语言如何实现递归程序&#xff1f; 之所以说这道题好&#xff0c;是因为&#xff1a; 首先&#xff0c;它不是纯粹考概念和死记硬背&#xff0c;求职者在回答问题之前需要进行一定的思考&#xff1b; 其次&#xff0c…

Debezium系列之:记录一次生产环境SQLServer数据库删除日志文件造成debezium connector数据不采集的解决方法

Debezium系列之:记录一次生产环境SQLServer数据库删除日志文件造成debezium connector数据不采集的解决方法 一、背景二、快速定位问题三、详细的解决步骤四、确认debezium connector恢复对数据库的数据采集五、经验总结一、背景 SQLServer数据库的日志把磁盘打满了,需要删除…

JAVA 实现 Redis 发布订阅

Redis 发布订阅 发布订阅&#xff1a;消息发布者发布消息 和 消息订阅者接收消息&#xff0c;两者之间通过某种媒介联系起来 例如订杂志&#xff0c;当自己订阅了爱格杂志&#xff0c;每个月会发刊一本。到发布的时候派送员将杂志送到自己手上就能看到杂志内容。只有我们订阅了…