std::string详解

之所以抛弃char*的字符串而选用C++标准程序库中的string类,是因为他和前者比较起来,不必 担心内存是否足够、字符串长度等等,而且作为一个类出现,他集成的操作函数足以完成我们大多数情况下(甚至是100%)的需要。我们可以用 = 进行赋值操作,== 进行比较,+ 做串联(是不是很简单?)。我们尽可以把它看成是C++的基本数据类型。

标准模板库(STL)提供了一个std::string类,其是std::basic_string的一个特化,它是一个容器类,可把字符串当作普通类型来使用,并支持比较、连接、遍历、STL算法、复制、赋值等等操作,这个类定义在头文件中。#include //注意这里不是string.h string.h是C字符串头文件
实现原理

1.声明一个C++字符串,初始化

std::string类的构造函数声明一个字符串变量很简单:string Str;
这样我们就声明了一个字符串变量,但既然是一个类,就有构造函数和析构函数。上面的声明没有传入参数,所以就直接使用了string的默认的构造函数,这个函数所作的就是把Str初始化为一个空字符串。String类的构造函数和析构函数如下:

  1. string s(); //生成一个空字符串s
  2. string s(str) //拷贝构造函数 生成str的复制品string(const string& str)
  3. string s(const char *s) //将C字符串作为s的初值
  4. string s(const char* cstr, size_type n) //使用字符串str的前n个字符初始化作为字符串s的初值。
  5. string s(str,stridx) //将字符串str内”始于位置stridx”的部分当作字符串的初值
  6. string s(const string& str, size_type pos,strlen) //将字符串str内”始于pos且长度顶多strlen”的部分作为字符串的初值
  7. string s(int num,char c) //生成一个字符串,包含num个c字符
  8. string s(begin,end) //以区间beg;end(不包含end)内的字符作为字符串s的初值,即迭代器间的值。
  • s.~string() //销毁所有字符,释放内存
  • 注意:当构造的string太长而无法表达时会抛出length_error异常
  • 当指定拷贝的长度大于被拷贝字符串的长度时,是将字符串对应位置中剩余的内容全部拷贝。

2.字符串操作函数1.赋以新值 = ,assign( )

第一个赋值方法当然是使用操作符=,新值可以是string(如:s=ns) 、c_string(如:s=”gaint”)甚至单一字符(如:s=’j’)。
还可以使用成员函数assign(),这个成员函数可以使你更灵活的对字符串赋值。注意和string的构造初始化赋值等函数基本类似,只是这个是赋予新值。

 s.assign(str);  s.assign(str,1,3);//如果str是"iamangel" 就是把"ama"赋给字符串 s.assign(str,2,string::npos);//把字符串str从索引值2开始到结尾赋给s s.assign("gaint");  s.assign("nico",3);//把’n’ ‘I’ ‘c’赋给字符串 s.assign("nicoafdad",2,5);//从二开始数五个 s.assign(5,’x’);//把五个x赋给字符串

2.两个字符串关系:交换swap( ) 比较字符串 ==,!=,< , , >= , ,compare( )

a.swap(b); //结果为 b=”12345678″; a=”ABCD”;swap会导致迭代器失效
C ++字符串支持常见的比较操作符(>,>=,<,<=,==,!=),甚至支持string与C-string的比较(如 str,>=,<,<=这些操作符的时候是根据"当前字符特性"将字符按字典顺序进行逐一得 比较。字典排序靠前的字符小,比较的顺序是从前向后比较,遇到不相等的字符就按这个位置上的两个字符的比较结果确定两个字符串的大小。同时,string ("aaaa") <string(aaaaa)。
另一个功能强大的比较函数是成员函数compare()。他支持多参数处理,支持用索引值和长度定位子串来进行比较。他返回一个整数来表示比较结果,返回值意义如下:0-相等 〉0-大于 <0-小于。举例如下:

  string字符串使用方法都类似  string s("abcd");  s.compare("abcd"); //返回0  s.compare("dcba"); //返回一个小于0的值  s.compare("ab"); //返回大于0的值  s.compare(s); //相等  auto number = b.compare(0,4,"1234efgh",4,4); //结果为 number=0; //字符串b从下标为0的地方开始的后四个字符是efgh,字符串"1234efgh"从下标为4的字符开始的后4个字符是efgh,所以相等  s.compare(1,2,"bcx",2); //用"bc"和"bc"比较。

3.添加插入:在尾部添加字符 += , append() ,push_back( ) 插入字符insert( ) 串联字符串 +

append和asign和string初始化都差不多,这个是追加,+=和等于也类似的关系

  s+=str;//加个字符串  s+="my name is jiayp";//加个C字符串  s+=’a’;//加个字符  s.append(str);  s.append(str,1,3);//不解释了 同前面的函数参数assign的解释  s.append(str,2,string::npos)//不解释了  s.append("my name is jiayp");  s.append("nico",5);  s.append(5,’x’);  s.push_back(‘a’);//这个函数只能增加单个字符

也许你需要在string中间的某个位置插入字符串,这时候你可以用insert()函数,这个函数需要你指定一个安插位置的索引,被插入的字符串将放在这个索引的后面。

  string a = "1234";  string b = "5678"; 1.在string字符串某一个位置上插入另一个(string)字符串  insert(int,string&);  a.insert(0, b); //结果为 a="56781234";  a.insert(2, b); //结果为 a="12567834";  insert(int,const char*);  a.insert(3,"abcd");//结果为 a="123abcd4"; 2.在string字符串某一个位置上插入另一个字符串的前n个字符  insert(int,const char*,int);  a.insert(1,"abcd",2); //结果为 a="1ab234"; 3.在string字符串某一位置上插入另一个string字符串(从下标为n的位置开始到结束)  insert(int,string&,int);  a.insert(1,b,2); //结果为 a="178234"; 4.在string字符串某一位置上插入另一个(string)字符串(从下标为n的位置开始连续m个字符)  insert(int,string&,int,int);  a.insert(2,b,1,2); //结果为 a="126734";  a.insert(0,b,0,3); //结果为 a="5671234";  insert(int,const char*,int,int);  a.insert(2,"5678",1,2); //结果为 a="126734"; 5.在字符串中插入n个字符  insert(int,int,const char);  a.insert(2,5,'k'); //结果为 a="12kkkkk34";  insert(iterator,const char);  a.insert(a.begin()+3,'k'); //结果为 a="123k4";  insert(iterator,int,const char);  a.insert(a.begin()+3,10,'k'); //结果为 a="123kkkkkkkkkk4"; 6.在字符串某个位置插入另一个字符串的某一个区间字符  insert(iterator,const_iterator_first,const_iterator_last);  a.insert(a.begin() + 1, b.begin() + 1, b.end() - 1);//结果为 a="167234";

4.取改 单一字符 [ ], at() 替换字符replace()

可以使用下标操作符[]和函数at()对元素包含的字符进行访问。但是应该注意的是操作符[]并不检查索引是否有效(有效索引0~str.length()),如果索引失效,会引起未定义的行为。而at()会检查,如果使用 at()的时候索引无效,会抛出out_of_range异常。
1.获取string字符串某一个字符 auto s=a.at(1); 2.修改string字符串某一个字符 a.at(2)=’1′;
使用[]同理;还有stl的迭代器和front(),back()也可以
不赞成类似于下面的引用或指针赋值:

  char& r=s[2];  char* p= &s[3];

因为一旦发生重新分配,r,p立即失效。避免的方法就是不使用。
遍历所有字符,这可由C风格的索引或STL迭代子来完成(如果无需修改,应使用const_iterator)。

 std::string name = "marius"; for(size_t i = 0; i < name.length(); ++i) std::cout << name[i]; for(std::string::const_iterator cit = name.begin(); cit != name.end(); ++cit) std::cout << *cit; for(std::string::iterator it = name.begin();it != name.end(); ++it) *it = toupper(*it);
     string s="il8n";     s.replace(1,2,"nternationalizatio");//从索引1开始的2个替换成后面的C_string

std :: string类的替换函数

函数1:std::string & replace ( (size_type pos1, size_type n1, const std::string & str, size_type pos2 = 0, size_type n2 = npos )) ;

 s1.replace(1, 3, "123456", 2, 4);  //用 "123456" 的子串(2,4) 替换 s1 的子串(1,3)

该函数的作用:使用str字符串从位置pos2开始的n2个字符,替换当前字符串从pos1位置开始处的n1个字符。即该函数将当前字符串从pos1开始的n1个字符全部删除,然后再用str整个字符串或者str从pos2开始的n2个字符,从pos1位置开始填入到当前字符串中。
提醒:如果n1或者n2的数值超出了对应字符串的长度,以实际长度为准,不会出现访问越界的情况。
注意:函数(1~4)
a、如果pos1指定的位置超出当前字符串的范围,抛出std::out_of_range异常,不捕捉将导致coredump。
b、如果pos2指定的位置超出替换字符串str的范围,抛出std::out_of_range异常,不捕捉将导致coredump。

函数2:std::string& replace(size_type pos, size_type n1, const char * s, size_type n2);
该函数的作用:使用字符串s的前n2个字符,替换当前字符串从pos位置开始处的n1个字符。即函数将当前字符串从pos开始的n1个字符全部删除,然后再用字符串s的前n2个字符填入到当前字符串中。类似于函数1的pos2等于0,必须指定n2的这种情况,但也有一点的差别,下面会注意里描述这种差别。
a.replace(6, 3, “THJKL”, 2); //结果为 a=”1234abTH678efgh”;

函数3:std::string& replace(size_type pos, size_type n1, const char* s);

 s2.replace(n, 5, "XXX");  //将子串(n,5)替换为"XXX"

该函数的作用:使用以”为结尾的字符串s,替换当前字符串从pos位置开始处的n1个字符。即函数将当前字符串从pos开始的n1个字符全部删除,然后再用字符串s从开始到以”结束的所有字符,从pos位置开始填入到当前字符串中。

函数4:std::string& replace(size_type pos, size_type n1, size_type n2, char c);
该函数的作用:使用n2个c表示的字符,替换当前字符串从pos位置开始处的n1个字符。即函数将当前字符串从pos开始的n1个字符全部删除,然后再用n2个c字符,从pos位置开始填入到当前字符串中。

 s2.replace(2, 3, 5, '0');  //用 5 个 '0' 替换子串(2,3)

函数5:std::string& replace((iterator i1, iterator i2, const std:: string& str));
该函数的作用:使用字符串str,替换当前字符串[i1,i2)之间的字符。
a.replace(a.begin()+5,a.end()-3,b); //结果为 a=”1234aABCDfgh”;

函数6:std::string& replace(iterator i1, iterator i2, const char* s, size_type n);
该函数的作用:使用字符串s的前n个字符,替换当前字符串[i1,i2)之间的字符。
a.replace(a.begin() + 6, a.end() – 3, “THJKL”, 2);//结果为 a=”1234abTHfgh”;

函数7:std::string& replace(iterator i1, iterator i2, const char* s);
该函数的作用:使用以”结尾的字符串s,替换当前字符串[i1,i2)之间的字符。
a.replace(a.begin()+3,a.begin()+6,”替换”); //结果为 a=”123替换cd5678efgh”;

函数8:std::string& replace(iterator i1, iterator i2, size_type n, char c);
该函数的作用:使用n个c表示的字符,替换当前字符串[i1,i2)之间的字符。
a.replace(a.begin()+6, a.end()-3, 5, ‘>’);

5.将某值写入和写出stream >>,getline(),<<

1.>> 从输入流读取一个string
2.<< 把一个string写入输出流。
3. 另一个函数就是getline(),他从输入流读取一行内容,直到遇到分行符或到了文件尾。istream& getline (istream& is, string& str);std::getline(std::cin, s);getline就可以实现输入整行字符串,如果需要输入多行,用一个while嵌套即可:while( getline() )

6.删除全部字符clear() 删除nStart—nEnd位置字符erase(int nStart,int nEnd)

把字符串清空的方法有三个:s=””;s.clear();s.erase();
在字符串末尾删除一个字符 a.pop_back(); //结果为 a=”12″;

1.删除所有字符 a.erase(); //结果为 a=""; 2.从字符串的某一个位置开始删除 a.erase(n) //从字符串的第n个字符开始删除  a.erase(3); //结果为 a="123";  a.erase(5); //结果为 a="12345";  a.erase(0); //等同于a.erase() a=""; 3.从字符串的某一个位置开始,向后删除m个字符 a.erase(n,m); //从字符的第n个字符开始删除m个字符 a.erase(2,3); //结果为 a="126789"; a.erase(4,1); //结果为 a="12346789"; 4.删除迭代器位置处的字符,并返回下一个字符的迭代器 auot iter=a.erase(a.begin()+1); //结果为 a="13456789";  cout<<*iter<<endl; //结果为 *iter=3 5.删除迭代器所指向的区间,并返回下一个字符的迭代器 auto iter=a.erase(a.begin()+1,a.end()-2);//结果为 a="189"; cout<<*iter<,endl; //结果为 *iter=8; 6.删除字符时常常与find()函数配合使用(find()函数的用法会在以后写出) a.erase(a.find("56"),2); //结果为 a="1234789";

7.stl相关begin() end() rbegin() rend() front和back

  std::string name = "marius";  // 使字符串全为大写  std::transform(name.begin(), name.end(), name.begin(),toupper);  std::string name = "marius";  // 升序排列字符串  std::sort(name.begin(), name.end());  std::string name = "marius";  // 反转字符串  std::reverse(name.begin(), name.end());  bool iswhitespace(char ch){  returnch == ' ' || ch == 't' || ch == 'v' ||  ch == 'r' || ch == 'n';  }  std::string name = " marius";  // 删除空白字符  std::string::iterator newend = std::remove_if(name.begin(), name.end(), iswhitespace);  name.erase(newend);
string a="abcd";1.获取字符串最后一个字符auto b=a.back(); //结果为 b='d';2.修改字符串最后一个字符a.back()='!'; //结果为 a="abc!";3.获取字符串第一个字符auto b=a.front(); //结果为 b='a';4.修改字符串第一个字符a.front()='!'; //结果为 a="!bcd";string::reverse_iterator rit = s.rbegin();//取开头是调用rbegin接口

8.查找函数和返回某个子字符串substr()

find(),rfind(),find_first_of(),find_last_of(),find_first_not_of(),find_last_not_of()
这些函数返回符合搜索条件的字符区间内的第一个字符的索引,没找到目标就返回npos。所有的函数的参数说明:
第一个参数是被搜寻的对象。第二个参数(可有可无)指出string内的搜寻起点索引,第三个参数(可有可无)指出搜寻的字符个数。

find()与rfind()

  string a="123456789abcdefgab";  string b="789abc";  如果找不到则返回的值为string::npos  /*if(a.find('k')==string::npos){cout<<"没有找到"<<endl;}*/  1.在字符串中查找某一个字符     (1).从字符串开始位置开始查找     auto s=a.find('a'); //结果为 s=9;      //表明a在字符串中从左向右第一次出现的位置的下标为9     (2).从字符串某一个位置开始查找     auto s=a.find('a',11); //结果为 s=16     //从字符串下标为11的地方开始查找字符    2.在字符串中查找某一个子串     (1).从字符串开始位置开始查找     auto s=a.find("9a");//结果为 s=8;      //表明9a子串的第一个字符在字符串中从左向右第一次出现的位置下标为8     auto s=a.find(b); //结果为 s=6;     (2).从字符串某一个位置开始查找     auto s=a.find("ab",11); //结果为 s=16     //从字符下标为11的地方开始向后查找    3.在字符串中查找子串的前n个字符   auto s=a.find("abcd",11,2); //结果为 s=16;   //解释:在字符串a中查找,子串"abcd"的前2个字符即在字符串a中查找"ab"   //注意 在这个重载函数中,第一个参数只能是char* 类型,而不能是string类型    4.find()与rfind()的区别  find()是字符串从前向后查找,rfind()是字符串从后向前查找,其他的用法与find()函数类似

find_first_of()与find_last_of()

  string a="123456789abcdefgh";  1.在字符串中查找某一个字符  auto s=a.find_first_of('5');//结果为 s=4;  auto s=a.find_first_of('5',5); //没有查找到 s=string::npos;  //如果查找某一个字符,与find()函数类似    2.在字符串中查找子串  //此时与find()函数不同,find()函数是查找子串,  //而find_first_of()函数是查找字符串a中含有的任意子串的字符  auto s=a.find_first_of("8a"); //结果为 s=7;  auto s=a.find_first_of("8a",1); //结果为 s=7;  //在字符串a中查找最早出现的字符'8'或者'a';  auto s=a,find("8a"); //结果是找不到  auto s=a,find("8a",1); //结果是找不到  //find()函数查找子串必须是相连的,而find_first_of()不需要,只要字符串中含有子串的字就可以  /*重要   *find_first_of()函数在字符串中查找子串中出现的任意字符,比如在字符串 "12abc"中查找子串"1k",1在字符串"12abc"中出现过,所以可以找到   *find()函数在字符串"12abc"中查找"1k",则查找不到,它查找的必须是"1k"这两个相连的字符   */  3.find_first_of()与find_last_of()的区别  find_first_of()是从前向后查找,而find_last_of()是从后向前查找。其他的用法与find_first_of()函数类似。

find_first_not_of()与find_last_not_of()

      find_first_not_of()与find_first_of()功能正好相反。不懂得可以先看我写的find_first_of()函数功能详解  find_firat_of()函数是在字符串中查找子串出现过的任意字符,也可以所字符串与子串都有的字符。  find_first_not_of()函数是在字符串中查找子串没有出现过的任意字符,也可以说是,字符串中有而子串中没有的字符  //以上查找的结果都是最先出现的那个字符的下标  //例  string a="12345";  auto s=a.find_first_not_of("1238"); //结果为 s=3;  //a字符串中有,而子串没有的是"45",而'4'字符是最先出现的,它的下标为3  find_first_not_of(str,n);  //str是子串,n是从下标为n的字符开始查找  find_first_not_of(str,n,m);  //str是子串,n是从下标为n的字符开始查找  //m是只看str子串的前m位字符  find_first_not_of()与find_last_not_of()查找顺序正好相反,  find_first_not_of()是从前向后,find_last_not_of()是从后向前查找  //如果以上看不懂的话建议先从find(),rfina()函数看起,之后再看find_first_of()与find_last_of(),最后看find_first_not_of()与find_last_not_of()

最后再说说npos的含义,string::npos的类型是string::size_type,所以,一旦需要把一个索引与npos相比,这个索引值必须是string::size)type类型的,更多的情况下,我们可以直接把函数和npos进行比较(如:if(s.find(“jia”)== string::npos))。

 s.substr();//返回s的全部内容 s.substr(11);//从索引11往后的子串 s.substr(5,6);//从索引5开始6个字符
    // 获取file的后缀  void Teststring1(){      string file("string.cpp");      size_t pos = file.rfind('.');      string suffix = file.substr(pos);      cout << suffix << endl;  }   // 取出url中的域名  void Teststring2(){      string url("http://www.cplusplus.com/reference/string/string/find/");      cout << url << endl;      size_t start = url.find("://");      //如果没有://,说明是无效协议      if (start == string::npos) {          cout << "invalid url" << endl;          return;      }      else{          string tmp = url.substr(0, start);//取出协议          cout << tmp << endl;      }      //找到域名的起始位置      start += 3;      //查找域名的结尾      size_t finish = url.find('/', start);      //取出域名      string address = url.substr(start, finish - start);      cout << address << endl;  }

9.返回字符数量size(),length() 返回字符的可能最大个数max_size() 判断字符串是否为空 empty() 返回重新分配之前的字符容量capacity() 保留一定量内存以容纳一定数量的字符reserve()

auto s=a.empty(); //结果为 s=false;

  1. size()和length(),他们等效。Empty()用来检查字符串是否为空。
  2. max_size() 这个大小是指当前C++字符串最多能包含的字符数,很可能和机器本身的限制或者字符串所在位置连续内存的大小有关系。我们一般情况下不用关心他,应该大小足够我们用的。
  3. capacity()重新分配内存之前 string所能包含的最大字符数。这里另一个需要指出的是reserve()函数,这个函数为string重新分配内存。重新分配的大小由其参数决定, 默认参数为0,这时候会对string进行非强制性缩减。
  4.    a.reserve(20);    reserve(size_t);可以调整string的空间。 如果size_t大于capacity的容量,则capacity按n*16-1扩大到比size_t 如果size_t小于capacity,则capacity不变
  5.    a.resize(40);    resize(size_t);可以调整string的空间。 可以改变string size的大小,并且capacity随size变大而变大,但是不会减小,当size缩小后,字符超出的部分会被裁剪掉。    a.resize(40,'A'); 扩充size后可以输入预定字符进行填充 //结果为 a="1234AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";

10.与c语言字符串: 将某值赋值为一个C_string,copy() 将内容以C_string返回 c_str() 将内容以字符数组形式返回data()

data()以字符数组的形式返回字符串内容,但并不添加’\0’。c_str()返回一个以‘\0’结尾的字符数组,而copy()则把字符串的内容复制或写入既有的c_string或字符数组内。C++字符串并不以’\0’结尾char*为C编程中最常用的字符串指针,一般以’\0′为结束标志;

 char *str = new char[64];  string a="12345abcdefg6789";  str[a.copy(str,7,5)]='\0'; // 结果为 str="abcdefg"; str[a.copy(str,7)]='\0'; // 结果为 str="12345ab";  delete[]str; /*注意 *copy的第2,3个参数不能大于字符串str所能容纳的最长字符串长度 */ str=a.c_str(); str=a.data();//a改变,str也随之改变 //可以生成一个const char* 的指针,可以指向一个空字符终止的地址。 const char* str=nullptr;

c语言中字符串在stdlib.h中有atoi和strtod等字符串转为数字的函数而string可以自己定义万能转换函数
* 转string * *为i,l,f,d

string *tos(* i){ //改一下函数名,改一下类型,搞定  ostringstream os;  os<>result;  return result;}//将*换成想要的类型就可以执行*转string

*string*

  * sto*(string str){ //改一下函数名,变量类型,搞定  * result;  istringstream is(str);  is >> result;  return result;  }//将*换成想要的类型就可以执行string转*    //也可以重载函数,达到万能函数转换  //记得包含头文件#include   //总结:使用string流和标准io流其实本身就是流,一个原理的,不同调用方法。

注意:前导0,长度
string常见操作

  1. 把字符串转换成整数
     /*首先,这个题不考虑字母*/ /*那就简单多了,只考虑数字,遇到其他情况直接返回-1就行了*/ class Solution { public:     int StrToInt(string str) {         int ans = 0;int isplus = 1;         for(char ch:str){             if(isalpha(ch)){                 return 0;             }if (ch == '+' || ch =='-'){                 isplus = (ch == '+') ? 1 : -1;              }if(isdigit(ch)){                 ans = ans*10+ch-'0';             }         }return isplus*ans;     } };
  2. 字符串相加(“123″+”23″=”146”)
   string addStrings(string num1, string num2) {           string str;           int cur = 0, i = num1.size()-1, j = num2.size()-1;           while (i >= 0 || j >= 0 || cur != 0) {               if (i >= 0) cur += num1[i--] - '0';               if (j >= 0) cur += num2[j--] - '0';               str += to_string (cur % 10);//将int转换位string;string num = to_string(sum);               cur /= 10;           }           reverse(str.begin(), str.end());           return str;
    string addStrings(string num1, string num2) {    int i = num1.length() - 1, j = num2.length() - 1, add = 0;     string ans = "";     while (i >= 0 || j >= 0 || add != 0) {     int x = i >= 0 ? num1[i] - '0' : 0;      int y = j >= 0 ? num2[j] - '0' : 0;      int result = x + y + add;       ans.push_back('0' + result % 10);       add = result / 10; i -= 1; j -= 1;      }    // 计算完以后的答案需要翻转过来     reverse(ans.begin(), ans.end());     return ans;       }    }

const string a;的操作符[]对索引值是a.length()仍然有效,其返回值是’’。其他的各种情况,a.length()索引都是无效的。举例如下:

const string Cstr("const string");string Str("string");Str[3]; //okStr.at(3); //okStr[100]; //未定义的行为Str.at(100); //throw out_of_rangeStr[Str.length()] //未定义行为Cstr[Cstr.length()] //返回 ‘’Str.at(Str.length());//throw out_of_rangeCstr.at(Cstr.length()) throw out_of_range