十七、C++字符串(二)

1、字符串的应用

需求:设计一个程序,用户输入属性id或者pass或者role可以把对应的内容显示出来,给定字符串如下:

string str{"id=user;pass=632105;role=郝英俊;"};


//设计一个程序,用户输入属性id或者pass或者role可以把对应的内容显示出来
#include <iostream>
#include <string>
using std::string;
int main()
{
    string str{ "id=user;pass=632105;role=郝英俊;" };
    string strIn;
    string outPut;

    while (true)
    {
        std::cout << "请输入你要查找的属性:";
        std::cin >> strIn;
        int lfind;    //find()函数查找的值是一个整数,不是字符串
        lfind = str.find(strIn + "=");
        if (lfind == std::string::npos)std::cout << "你查找的属性不存在";
        else
        {
            int lend = str.find(";",lfind);
           outPut = str.substr(lfind + strIn.length() + 1, lend - lfind - strIn.length() - 1);
            std::cout << "你查找的" << strIn << "属性的值为:" << outPut << std::endl;
        }
    }
}

2、string函数补充

1)string插入字符串

.insert()是string类型的一个方法,可以在string字符串的指定位置插入另一个字符串,基本语法如下:

//插入字符串基本语法
.insert(要插入的位置,要插入的字符串);
string id{"id=;"}
id.insert(3,"testid");    //在第3个字符的位置插入字符串testid,最后id的值为id=testid

//插入字符串的高级用法
.insert(要插入的位置,要插入的字符个数,要插入的字符);
string id{"id=;"}
id.insert(3,6,'*');   //在第3个位置插入6个*号,最后id的值为id=******
//insert()字符串插入函数基本用法
#include <iostream>
#include <string>
using std::string;

int main()
{
	string strA { "id=;" };
	strA.insert(3, "user");
	std::cout << "字符串strA插入后的字符串为:" << strA << std::endl;

	string strB{ "id=;" };
	strB.insert(3,6,'*');  //此处插入的是字符,不是字符串,所以要使用单引号
	std::cout << "字符串strB插入后的字符串为:" << strB << std::endl;

}

2)insert插入字符串的扩展用法

//insert()扩展用法语法
.insert(要插入的位置,要插入的字符串,要插入的字符串的起始位置,要插入的大小);
string id{"id=;"};
id.insert(3,"killertestid123456",6,6);  //id=testis

.insert(要插入的位置,要插入的字符串,要插入的大小);
string id{"id=;"};
id.insert(3,"killertestid123456",6);  //id的值为id=killer  从开始位置截取6个字符
//insert()扩展用法示例
#include <iostream>
#include <string>
using std::string;

int main()
{
	string strA{ "id=;" };
    //在原字符串的第三个位置开始插入,插入的内容为此处字符串的第6个位置开始截取,截取6个
	strA.insert(3, "killertestid123456", 6, 6);
	std::cout << "字符串strA插入后的字符串为:" << strA << std::endl;

	string strB{ "id=;" };
     //在原字符串的第三个位置开始插入,插入的内容为此处字符串的第0个位置开始截取,截取6个
	strB.insert(3, "killertestid123456", 6);  //此处插入的是字符,不是字符串,所以要使用单引号
	std::cout << "字符串strB插入后的字符串为:" << strB << std::endl;

}

3、指针数组字符串

1)string字符串不是一个单纯的指针或者数组

#include <iostream>
#include <string>
using std::string;
int main()
{
	string str{ "12345" };
	std::cout << "字符串中第1个字符的值为:" << str[0] << std::endl;
	std::cout << "字符串中第2个字符的值为:" << str[1] << std::endl;
	std::cout << "字符串中第3个字符的值为:" << str[2] << std::endl;
	std::cout << "字符串的地址为:" << (int)&str << std::endl;
	std::cout << "str[0]的地址为:" << (int)&str[0] << std::endl;
	std::cout << "str[0]的地址为:" << (int)&str[1] << std::endl;
	
	str = str + "abcdesdajhdhjakd";
	std::cout << "字符串长度增加后的地址为:" << (int)&str << std::endl;
	std::cout << "字符串长度增加后str[0]的地址为:" << (int)&str[0] << std::endl;
	std::cout << "字符串长度增加后str[0]的地址为:" << (int)&str[1] << std::endl;
}
/*结果显示:
如果str的地址和str[0]的地址一致,说明str相当于一个数组
但是str的地址和str[0]的地址不一样,说明str不是一个简单的数组,
从str[0]的地址开始,地址才是连续的
当字符串长度增加后,str的地址不变,但是str[0]开始,后面的每个字符的地址都会变化
*/

2)获取string字符串的指针

​ 在内存里的布局,与C字符串(char*)不同的是,C字符串以0结尾,而string字符串由于专门记录其长度的属性,在实现的时候,并没有严格要求是否以0结尾。但是在C++11标准推出后,要求string字符串也以0结尾

​ 通过.c_str()方法可以得到一个const char*的指针指向str储存字符数据的内存空间(返回的是一个常量指针,只能写不能读)

​ 通过.data()方法可以得到一个const char*的指针指向str储存字符数据的内存空间

​ 在c++17标准下,.data()方法得到的是一个char*的指针

#include <iostream>
#include <string>
using std::string;
int main()
{
	string str{ "12345" };

	const char* baseStrA = str.c_str();
	const char* baseStrB = str.data();
	std::cout << "字符串的地址为:" << (int)&str << std::endl;
	std::cout << "通过c_str获取的字符串地址为为:" << (int)baseStrA << std::endl;
	std::cout << "通过c_str获取的字符串地址为为:" << (int)baseStrB << std::endl;

}

3)string替换字符串的内容

​ .replace是string类型提供的一种方法,可以替换string字符串中的内容。语法如下

//replace()字符串替换语法
.replace(要替换的内容起始位置,要替换的程度,"替换后的内容");
string str{"id=user;"};
str.replace{3,4,"zhangsan"}; //从原字符串的第三个位置开始,替换四个字符,替换内容为新的字符串

//replace()其他语法
.replace(要替换的内容起始位置,要替换的长度,替换后的字符长度,'字符');
str.replace(3,4,6,'*'); //str的内容为:id=******
//replace()字符串替换函数基本用法
#include <iostream>
#include <string>
using std::string;

int main()
{
	string strA{ "id=user;" };
	//在原字符串的第3个位置开始替换,替换后面的4个字符
	strA.replace(3, 4, "zhangsan");
	std::cout << "字符串strA替换后的字符串为:" << strA << std::endl;

	string strB{ "id=user;" };
	//在原字符串的第3个位置开始替换,替换后面的4个字符,并替换为6个*号
	strB.replace(3, 4, 6, '*');  //此处插入的是字符,不是字符串,所以要使用单引号
	std::cout << "字符串strB替换后的字符串为:" << strB << std::endl;
}

4)replace()函数的扩展用法

//replace()函数的扩展语法
.replace(要替换的内容起始位置,要替换的长度,"替换后的内容",替换后内容的节选长度);
string str{"id=user;"};
str.replace(3,4,"zhangsan;pass=255;",8); //str=zhangsan

//replace()函数的扩展语法
.replace(要替换的内容起始位置,要替换的长度,"替换后的内容",替换后内容的起始位置,替换后内容的节选长度);
string str{"id=user;"};
str.replace(3,4,"zhangsan;pass=255;",5,3); //str=san;
#include <iostream>
#include <string>
using std::string;

int main()
{
	string strA{ "id=user;" };
	//在原字符串的第3个位置开始替换,替换后面的4个字符
	strA.replace(3, 4, "zhangsan;pass=255;", 8); 
	std::cout << "字符串strA替换后的字符串为:" << strA << std::endl;

	string strB{ "id=user;" };
	strB.replace(3, 4, "zhangsan;pass=255;", 5, 3);  
	std::cout << "字符串strB替换后的字符串为:" << strB << std::endl;
}

5)字符串的删除

​ erase()是string类型提供的一种方法,可以删除string字符串中的内容,语法如下:

//字符串的删除erase()函数语法一
.erase(要删除的起始位置,要删除的起始长度);
string str{"id=user;"};
str.erase(3,4);   //std为id=;

//从起始位置开始删除所有内容
.erase(要删除的起始位置);
string str{"id=user;"};
str.erase(3);   //std为id=

.earse();删除字符串所有内容
.clear();删除字符串所有内容
4、字符串补充

1)字符的存储及读取

​ 字符串再C/C++的学习中一直是一个比较麻烦的点,但是只要知道"计算机中万物皆数字",则所有的问题都可以迎刃而解。

​ 字符的存储:先通过编码表转化为数子,再把数字转化为二进制进行存储(字符->编码表->二进制数字)

​ 字符的读取:先把二进制数字读取出来,再根据编码表转化为字符(二进制数字->编码表->字符)

​ 字符编码表:ASCII、GBK、UNICODE

2)练习

​ 需求:设计一个程序,要求用户输入中英文混合内容,正确的计算出长度并显示出来。比如用户输入:你好,我的世界。结果输出为7。

​ 思路:因为字符的ASCII码范围为0-127,只要超过127就表明是一个中文字符

#include <iostream>
#include <string>
using std::string;
int main()
{
	string strIn{};
	std::cout << "请输入计算长度的字符串:";
	std::cin >> strIn;
	int length{};
	for (int i = 0; strIn[i]; i++)
	{
		if (strIn[i] < 0)i++;
		length++;
	}
	std::cout << "你输入的字符串长度为:" << length << std::endl;
}

2)string字符串转化为数字

通过std::to_string(数字)可以将数字转化为字符串。通过一下函数可以将字符串转化为数字

作用 语法 用法
将字符串转化为int std::stoi(字符串) int a=stoi("123")
将字符串转化为long std::stol(字符串) long a=stol("123")
将字符串转化为long long std::stoll(字符串) long long a=stoll("123");
将字符串转化为unsigned long std::stoul(字符串) unsigned long a=stoul("123")
将字符串转化为unsigned long long std::stoull(字符串) unsigned long long a=stoull("123")
将字符串转化为float std::stof(字符串) float a=stof("123")
将字符串转化为double std::stod(字符串) double a=stod("123")
将字符串转化为long double std::stold(字符串) long double a=stold("123")

将字符串转化为数字后,可以进行计算

#include <iostream>
#include <string>
using std::string;
int main()
{
	string strIn{};
	std::cout << "请输入计算长度的字符串:";
	std::cin >> strIn;
	int length{};
	for (int i = 0; strIn[i]; i++)
	{
		if (strIn[i] < 0)i++;
		length++;
	}
	std::cout << "你输入的字符串长度为:" << length << std::endl;
}

3)stringstream流

​ std::stringstream流定义在<sstream>头文件中,std::cout组织数据的形式有时候要比string字符串要方便。std::stringstream可以像std::cout一样组织数据。std::stringstream.str()方法将会返回一个string,里面是std::stringstream的内容,即将流中的内容提取出来

//stringstream流语法
std::stringstream str;
str<<"你好"<<123;
std::string _str=str.str();  //_str的内容为"你好123"
//stringstream流简单使用
#include <iostream>
#include <string>
#include <sstream>
using std::string;

int main()
{
	std::stringstream strA;
	strA << "你好 [" << std::hex << 500 << "]   "<<std::dec<<123;
	string strB = strA.str();
	std::cout << strB;
}

5、游戏麟江湖排行榜设计

​ 需求:我们通过网络获得了麟江湖游戏玩家的数据,现在我们要为我们的游戏设计一个战力(exp)排行榜系统,排序贵u则为战力由高到低,当战力相同时,根据ID的字母顺序进行排序(如A要排在B的前面)数据如下:

"id=TomyClare;exp=9523;id=Sunny;exp=9523;id=DyBaby;exp=25301;id=Simple;exp=25301;id=Bkacs11;exp=2100;id=DumpX;exp=36520;"

​ 根据这个数据生成玩家排行榜!

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

typedef struct Role 
{
	string Id;
	int Exp;
}*PROLE;

int main()
{
	string strData{ "id=TomyClare;exp=9523;id=Sunny;exp=9523;id=DyBaby;exp=25301;id=Simple;exp=25301;id=Bkacs11;exp=2100;id=DumpX;exp=36520;" };
	int istart{}, iend{}, icount{};
	for (int i = 0; i < strData.length(); i++)
	{
		if (strData[i] == ';')
		{
			icount++;
			i += 3;
		}
	}
	PROLE pRole = new Role[icount / 2];
	icount = 0;
	do 
	{
		istart = strData.find("id=", istart);
		if (istart == std::string::npos) break;
		iend = strData.find(";", istart + 3);
		pRole[icount].Id = strData.substr(istart + 3, iend - istart - 3);
		istart = iend + 1;
		istart = strData.find("exp=", istart);
		iend = strData.find(";", istart + 4);
		pRole[icount++].Exp = std::stoi(strData.substr(istart + 4, iend - istart - 4));
		istart = iend + 1;
	} while (true);
	
	//冒泡排序
	for (int i = 1; i < icount; i++)
	{
		for (int j = 1; j < icount; j++)
		{
			if (pRole[j].Exp > pRole[j - 1].Exp)
			{
				Role tmp = pRole[j - 1];
				pRole[j- 1] = pRole[j];
				pRole[j] = tmp;

			}
		}
	}
	for (int i = 1; i < icount; i++)
	{
		std::cout << pRole[i].Id << "  " << pRole[i].Exp << std::endl;
	}
}