STL中的string类

本文介绍了C++ STL中的string类,包括其作为basic_string模板类实例的特性,构造函数,容量操作如size(), length(), clear(), resize()和reserve()的方法,以及访问与遍历、修改操作。特别强调了预估容量以提高效率和避免使用低效的insert和erase操作。" 102680030,8459088,Maven项目导入jstl标签库常见问题与解决方案,"['Maven', 'jstl', 'servlet-api', '配置错误', 'Web开发']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


一、string类对象是什么?

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

template<class T>
class basic_string
{
private:
T* _str;//可以是char、wchar_t等
}

string类是basic_string模板类的一个实例,虽然大多数时候我们使用的string类型都是用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数,但是考虑到编码的问题,除了char还有wchar_t(两字节)、char16_t、char32_t等,所以string也只是basic_string模板类的一个实例
在这里插入图片描述

二、string的构造函数:

在这里插入图片描述

// string constructor
#include <iostream>
#include <string>

int main ()
{
  std::string s0 ("Initial string");

  // constructors used in the same order as described above:
  std::string s1;//调用默认构造函数
  std::string s2 (s0);//调用拷贝构造函数
  std::string s3 (s0, 8, 3);//截取,从给定的字符串对象的第8个位置往后截取三个长度
  //如果不写后面的3就默认截取到字符串结束 npos是string里的一个静态变量,代表整形的最大值
  std::string s4 ("A character sequence");//from c-string 拷贝一个常量字符串
  std::string s5 ("Another character sequence", 12);//从给定c-string的开始位置截取12个长度
  std::string s6a (10, 'x');//fill 用10个x fill对象
  std::string s6b (10, 42);      // 42 is the ASCII code for '*' 也是fill
  std::string s7 (s0.begin(), s0.begin()+7);//用某个对象的迭代器区间初始化,从初识位置往后7个长度

  std::cout << "s1: " << s1 << "\ns2: " << s2 << "\ns3: " << s3;
  std::cout << "\ns4: " << s4 << "\ns5: " << s5 << "\ns6a: " << s6a;
  std::cout << "\ns6b: " << s6b << "\ns7: " << s7 << '\n';
  return 0;
}

在这里插入图片描述


三、string类对象的容量操作

在这里插入图片描述

#include <iostream>
#include <string>

int main ()
{
  std::string str ("Test string");
  
  std::cout << "The size of str is " << str.size() << " bytes.\n";
  //size函数,求字符串长度 这里是11 包含空格但是没有'\0',计算的是有效字符的长度
   
   std::cout << "The size of str is " << str.length() << " bytes.\n"
   //length函数,与size函数功能一样,推荐使用size()函数,因为别的STL容器也是用的size()函数
   
   std::cout << "max_size: " << str.max_size() << "\n";
   //输出字符串的最长长度,没什么意义,一般max_size:是4294967291

	std::cout << "capacity: " << str.capacity() << "\n";
	//输出字符串可以存储的有效字符的容量,比如一个字符串的容量是16(包含最后的'\0'),但是可能只有5个有效字符,size()就是5,但是capacity是15(去掉了最后的'\0'),capacity>=size 
 	
 	str.clear();
 	//直接清空str字符串 str变成空串,但是它的capacity还是存在的,只是清除了数据

	str.empty();
	//判断str的size是否为0,是返回true,不是返回false;
	
	str.shrink_to_fit();
  	std::cout << "3. capacity of str: " << str.capacity() << '\n';
  	//将str的容量变得和size一样长 也就是缩减多余的capacity
	
	str.reserve(20);
	//reserve改变string对象的容量,也就是为string对象预留20个字节的空间
	//这里的20是至少申请20,具体是多少取决于编译器,因为这里涉及对齐的问题
	//预留足够的空间可以减少频繁增容
	//如果str是空串,这时str的size还是0 但是其capacity至少是20

	str.resize (100,'+');
	//resize就是重新分配空间,并初始化,如果只填数字就不初始化
	//如果resize中的数字大于原来字符串的有效长度,原字符串内容保留,不然就会缩减
	//如果str是空串,这时它的size变为100,并且每个元素都是'+',capacity至少是100
	//如果str不为空,就会在原串后面追加'+'直到100	
  return 0;
}
  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不会改变容量大小

四、string类对象的访问与遍历

string类对象的遍历

//方式1 普通for循环
for(int i = 0;i<s.size();i++)
{
	cout<<s[i]<<" ";
}
cout<<endl;

//方式2 迭代器 内嵌类型
string::iterator it = s.begin();
while(it!=s.end())
{
	cout<<*it<<" ";//可读
	*it+=1;//可写
	++it;
}
cout<<endl;
//同理还有vector<int>::iterator、list<int>::iterator

//方式3 范围for 自动往后迭代,自动判断结束 底层就是迭代器
for(auto& e:s)//加上&可读可写,不加就是只读
{
	cout<<e<<" ";
	e-=1;
}
cout<<endl;

迭代器有多种

string s("hello world");
//正着遍历
string::iterator it = s.begin();
while(it!=s.end())
{
	cout<<*it<<" ";//可读
	*it+=1;//可写
	++it;
}
cout<<endl;

//倒着遍历 反向迭代器
string::reverse_iterator rit = s.rbegin();//直接写auto it = s.rbegin();
while(rit!=s.rend())
{
	cout<<*rit<<" ";//可读
	*rit+=1;//可写
	++rit;
}

//普通迭代器可读可写,但是const迭代器只读不可写
void f(const string& str)
{
	//此时遍历就需要const迭代器
	string::const_iterator it = s.begin();
	while(it!=s.end())
	{
		cout<<*it<<" ";//可读不可写
		++it;
	}
	cout<<endl;
}
//c++11中cbegin和cend迭代器就是const类型迭代器

在这里插入图片描述>在这里插入图片描述
一个可以修改,一个不能修改

#include <iostream>
#include <string>

int main ()
{
  std::string str ("Test string");
  for (int i=0; i<str.length(); ++i)
  {
    std::cout << str[i];//返回str对象i位置的字符 就和数组一样访问数组中的元素
    //但这里是函数调用 等价于str.operator[](i) 取的是第i个位置字符的引用
   
    //因为是引用,所以除了读还可以写(修改),这里的引用返回不是为了减少拷贝,而是为了支持修改返回对象
    str[i]+=1;
	//如果是const那就是只读的 不可以修改

	s1.at(i)+=1;
	//at和[]是一样的,返回的也是引用,可以访问可以修改,同样也有const(只读不能写)
	//[]和at检查越界的方式不一样,[]使用assert断言,at是抛异常
  }
  return 0;
}

在这里插入图片描述

#include <iostream>
#include <string>

int main ()
{
  std::string str ("hello world.");
  str.back() = '!';//因为是引用,所以可以访问对象最后一个元素,也可以修改对象最后一个元素
  std::cout << str << '\n';//hello world!这里就将.修改为了!

  str.front() = 'T';
  //front也是一样,访问首元素,可读可修改 这里就变为Tello world!
  return 0;
}

c_str

string s("hello");
cout<<s<<endl;
cout<<s.c_str<endl;//输出const char*,也就是常量字符串
//应用场景:
string file("text.txt");
FILE* fout = fopen(file.c_str(),"2");//这里就需要使用c_str将string类型变为常量字符串类型进行输出
//取文件的后缀名
int pos  =  file.find('.');
if(pos!=string::npos)
{
	string suffix = file.substr(pos,file.size()-pos);
	//substr就是从str中的pos位置往后截取n个字符并将其返回
	cout<<suffix<<endl;
}
//rfind也和find一样只不过是倒着找

五、string类对象的修改操作

在这里插入图片描述

在这里插入图片描述

#include <iostream>
#include <string>

int main ()
{
  std::string name ("John");
  std::string family ("Smith");
  name += " K. ";         // 在尾部插入c-string,一个常量字符串
  name += family;         // 在尾部插入一个string类对象
  name += '\n';           // 在尾部插入一个character,一个字符
	
  std::cout << name;//John K. Smith
  
  name.push_back('a');
  //push_back只能在尾部插入单个字符
  //append()可以在尾部插入字符串
   

  return 0;
}

在这里插入图片描述

#include <iostream>
#include <string>

int main ()
{
  std::string str;
  std::string str2="Writing ";
  std::string str3="print 10 and then 5 more";

  // used in the same order as described above:
  str.append(str2);                       // "Writing ",可以在尾部追加一个string类对象
  str.append(str3,6,3);                   // "10 ",在尾部追加string类对象截取的一部分
  str.append("dots are cool",5);          // "dots ",在尾部追加常量字符串的前n个元素
  str.append("here: ");                   // "here: ",在尾部追加常量字符串
  str.append(10u,'.');                    // "..........",在尾部追加自己创建的n个相同变量初始化的字符串
  str.append(str3.begin()+8,str3.end());  // " and then 5 more",迭代器区间
  str.append<int>(5,0x2E);                // "....."
	

  std::cout << str << '\n';//Writing 10 dots here: .......... and then 5 more.....
  //其实和构造函数很类似
  return 0;
}

在这里插入图片描述
assign就是重新给字符串赋值,赋值的方式和构造函数和append都很类似

// string::assign
#include <iostream>
#include <string>

int main ()
{
  std::string str;
  std::string base="The quick brown fox jumps over a lazy dog.";

  // used in the same order as described above:

  str.assign(base);
  std::cout << str << '\n';

  str.assign(base,10,9);
  std::cout << str << '\n';         // "brown fox"

  str.assign("pangrams are cool",7);
  std::cout << str << '\n';         // "pangram"

  str.assign("c-string");
  std::cout << str << '\n';         // "c-string"

  str.assign(10,'*');
  std::cout << str << '\n';         // "**********"

  str.assign<int>(10,0x2D);
  std::cout << str << '\n';         // "----------"

  str.assign(base.begin()+16,base.end()-12);
  std::cout << str << '\n';         // "fox jumps over"
  return 0;
}

string类对象的查找

在这里插入图片描述

// string::find
#include <iostream>       
#include <string>        

int main ()
{
  std::string str ("There are two needles in this haystack with needles.");
  std::string str2 ("needle");

  // different member versions of find in the same order as above:
  std::size_t found = str.find(str2);
  if (found!=std::string::npos)//如果found的位置不是str的末尾
    std::cout << "first 'needle' found at: " << found << '\n';

  found=str.find("needles are small",found+1,6);//从found+1位置开始找 "needles are small"的前6个字符
  if (found!=std::string::npos)
    std::cout << "second 'needle' found at: " << found << '\n';

  found=str.find("haystack");
  if (found!=std::string::npos)
    std::cout << "'haystack' also found at: " << found << '\n';

  found=str.find('.');
  if (found!=std::string::npos)
    std::cout << "Period found at: " << found << '\n';

  //找到第一个replace的位置,用str2代替
  str.replace(str.find(str2),str2.length(),"preposition");
  std::cout << str << '\n';


	
  return 0;
}

在这里插入图片描述

string也可以插入删除,insert函数和erase函数,但是效率很低,所以尽量不使用

string s("hello world");
s.insert(0,1,'x');//在头部插入x
s.insert(s.begin(),'y');
s.insert(0,"hello");//插入字符串

s.erase(0,2);//从0位置往后删除2个字符,如果不写2默认0位置往后全部删除
s.erase(s.begin(),s.begin()+2)//迭代器区间
  1. 在string尾部追加字符时,s.push_back (‘c’) / s.append(1, c) / s += 'c’三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
  2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好

对于输入字符串中包含空格 可以有以下的处理方式

int main()
{
	string s1;
	//第一种
	char ch = getchar();
	while(ch!='\n')//一个字符一个字符的输入
	{
		s1+=ch;
		ch = getchar();
	}
	//第二种
	getline(cin,s1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值