有时需要处理字符串,c语言在string.h中提供了字符串函数,早期的c++处理字符串提供了类。
string类是由头文件string支持的,要使用类,要知道它的公有接口,而string类包含大量的方法,其中包含了若干构造函数,用于将字符串赋给变量,合并字符串,比较字符串和访问各个元素的重载运算符以及用于在字符串查找字符和子字符串的工具。
构造字符串
下面的程序对应上面的七个构造函数:
使用构造函数时都进行了简化,即隐藏了这样一个事实:string实际上是模板具体化basic_string<char>
的一个typedef,同时省略了与内存管理相关的参数。size_type是一个依赖于实现的整型,是在头文件string中定义的。string类将string::npos定义为字符串的最大长度,通常为unsigned int 的最大值。
#include <iostream>
#include<string>
using namespace std;
int main()
{
string one("Lottery Winner!");
cout<<one<<endl;//重载<<
string two(20,'$');
cout<<two<<endl;
string three(one);
cout<<three<<endl;
one+='Oops!';//重载+=
cout<<one<<endl;
two="Sorry! That was";
three[0]='p';//重载[]
string four;//默认构造函数创建一个以后可对其进行赋值的空字符串
four=two+three;//重载+,=
cout<<four<<endl;
char alls[]="All's well that ends well";
string five(alls,20);//将一个c-风格字符串和一个整数作为参数,整数表示参数要复制多少字符,如果字符数超过c-风格字符串的长度,将会将无用的字符复制
cout<<five<<"!\n";
string six(alls+6,alls+10);
cout<<six<<",";
string seven(&five[6],&five[10]);
cout<<seven<<"...\n";
string eight(four,7,16);//从four的第8个字符开始,将16个字符复制到eight
cout<<eight<<"in motion!"<<endl;
return 0;
}
程序还使用重载+=运算符,它将一个字符串附加到另一个字符串的后面;重载的=运算符用于将字符串赋给另一个字符串;重载<<运算符用于显示string对象;重载的[]运算符用于访问字符串中的各个字符。
将一个c-风格字符附加到一个string对象的后面。但+=运算符被多次重载,以便能够附加string对象和单个字符:
one+=two;添加字符串对象(不在程序中)
one+='!';添加一个char类型的值(不在程序中)
同样,=运算符也被重载,以便可以将string对象,c-风格字符串或char值赋给string对象:
two="Sorry! That was";
two=one;//添加字符串对象(不在程序中)
two='?';//添加char类型的值(不在程序中)
下面的构造函数有一个模板参数:
string six(alls+6,alls+10);
template<class Iter>string (Inter begin,Iter end);
begin和end将像指针那样,指向内存中两个位置。构造函数将使用begin和end指向的位置之间的值,对string对象进行初始化。由于数组名相当于指针,所以alls+6和alls+10的类型都是char*,因此使用模板时,将用类型char*替换Iter。第一个参数指向数组alls中第一个w,第二个参数指向后面的空格。
假设要用这个构造函数将对象初始化为另一个string对象,则下面的语句不管用:
string seven(five+6,five+10);
因为对象名不会被看作是对象的地址,因此five不是指针,所以five+6是没有意义的。然而,five[6]是一个char值,所以&five[6]是一个地址,因此可被用作该构造函数的一个参数。
string seven(&five[6],&five[10]);
c++11新增的构造构造函数
构造函数string(string && str)类似于复制构造函数,导致新创建的string为str的副本。但与复制构造函数不同的是,它不保证将str视为const。这种构造函数被称为移动构造函数。
构造函数string(initializer_lsitil)能够将列表初始化语法用于string类,它使得下面这样的声明是合法的:
string piano_man={
'L','i','s','z','t'};
string comp_lang{
'L','i','s','p'};
就string类而言,可能用处不大,因为使用C-风格字符串更容易,但确实让列表初始化语法普遍实用的意图。
string类输入
对于类,有帮助的一点是,知道有哪些输入方式可用。对于C-风格字符串,有三种方式:
char info[100];
cin>>info;//读一个词
cin.getline(info,100);//读一行,抛弃\n
cin.get(info,100);、//读一行,离开\n,排队
对于string对象,两种方式:
string stuff;
cin>>stuff;//读一个
getline(cin,stuff);//读一行
两个版本的getline()都有一个可选参数,用于指定使用哪个字符来确定输入的边界:
cin.getline(info,100,':');//读到:,抛弃:
getline(stuff,':');//读到:,抛弃:
在功能上,它们之间的主要区别在于,string版本的getline()将自动调整目标string对象的大小,使之更好能够存储输入的字符:
char fname[10];
string lname;
cin>>fname;
cin>>lname;
cin.getline(fname,10);
getline(cin,fname);
自动调整大小的功能让string版本的getline()不需要指定读取多少个字符的数值参数。
在设计上,读取C-风格字符串的函数是istream类的方法,而string版本是独立的函数。这就是对于C-风格字符串输入,cin是调用对象;而string对象输入,cin是一个函数参数的原因。这种规则也适用于>>形式,如果使用函数形式来编写代码,这将显而易见:
cin.operator>>(fname);
operator>>(cin,lname);
两个函数都自动调用目标string的大小,使之与输入匹配,但存在限制:
- string对象的最大允许长度,由常量string::npos指定,这通常是最大的unsigneed int值,因此对于普通的交换式输入,这不会带来实际的限制;如果将整个文件的内容读取到单个string对象中,会成为限制因素。
- 程序可以使用的内存量。
string版本的getline()函数从输入中读取字符,并将其存储到目标string中,直到发生下列三种情况:
- 到达文件尾,在这种情况下,输入流的eofbit将被设置,这意味着方法fail()和eof()都将返回true.
- 遇到分界字符(默认为\n),在这种情况下,将把分界字符从输入流中删除,但不保存。
- 读取的字符数达到最大允许值,将设置输入流的failbit,这意味着方法fail()将返回true。
例:
#include <iostream>
#include<fstream>
#include<string>
#include<cstdlib>
using namespace std;
int main()
{
ifstream fin;
fin.open("tobuy.txt");
if(fin.is_open()==false)
{
cout<<"Can't open file. Bye.\n";
exit(EXIT_FAILURE);
}
string item;
int count=0;
getline(fin,item,':');
while(fin)
{
++count;
cout<<count<<": "<<item<<endl;
getline(fin,item,':');
}
cout<<"Done\n";
fin.close();
return 0;
}
使用字符串
string还可以比较字符串,String类对全部6个关系运算符都进行了重载。如果排列顺序列中,一个对象位于另一个对象的前面,则前者被视为小于后者,如果为ADCII码,则数字将小于大写字符,则大写字符小于小写字符。对于每个关系运算符,都以三种方式被重载,以便能够将string对象与另一个string对象,C-风格字符串进行比较,并能够将字符串与string对象进行比较:
string snake1("cobra");
string snake2("coral");
char snake3[20]="anaconda";
if(snake1<snake2)
...
if(snake1==snake3)
...
if(snake3 !=snake2)
....
可以确定字符串的长度。size()和length()成员函数都返回字符串中的字符数:
if(snake1.length()==snake2.size())
cout<<"Both strings have the same length.\n"
可以以多种不同的方式在字符串中搜索给定的子字符串或字符。下面find()方法的四个版本。string::npos是字符串可以存储的最大字符数,通常是无符合int或无符合long的最大值。
string库还提供了相关的方法:rfind(),find_first_of(),find_last_of,find_first_not_off()和find_last_not_of(),重载函数特征标与find()方法相同。
rfind()方法查找子字符串或字符最后一次出现的位置。
find_first_of()方法查找字符首次出现位置;finf_last_if()查找最后出现一次。
find_first_not_of()方法查找第一个不包含在参数中的字符。
例:猜单词字母,6次输了算输。
#include <iostream>
#include<string>
#include<cstdlib>
#include<ctime>
#include<cctype>
using namespace std;
const int NUM=26;
const string wordlist[NUM]={
"apiary","beetle","cereal",
"danger","ensign","florid","garage","health","insult",
"jackal","keeper","loaner","manage","nonce","onset",
"plaid","quilt","remote","stolid","train","useful",
"valid","whence","xenon","yearn","zippy"
};
int main()
{
srand(::time(0));
char play;
cout<<"Will you paly a word game?<y/n>";
cin>>play;
play=tolower(play);
while(play=='y')
{
string target=wordlist[rand()%NUM];
int length=target.length();
string attempt(length,'-');
string badchars;
int guesses=6;
cout<<"Guess my secret word. It has"<<length
<<"letters,and you guess\n"
<<"one letter at a time. You get"<<guesses
<<"wrong guesses.\n";
cout<<"Your word:"<<attempt<<endl;
while(guesses>0&&attempt !=target)
{
char letter;
cout<<"Guess a letter:";
cin>>letter;
if(badchars.find(letter) !=string::npos || attempt.find(letter) !=string::npos)//npos是string类的静态成员。它的值是string对象能存储的最大字符数。从索引0开始,比最大索引值大1,表示没有查找到字符
{
cout<<"You already guessed that.Try again.\n";
continue;
}
int loc=target.find(letter);//检查选择的字符是否位于被测的单词中开始的
if(loc == string::npos)
{
cout<<"Oh,bad guess!\n";
--guesses;
badchars+=letter;//+=运算符重载使能够将一个字符附加到字符串里
}
else
{
cout<<"Good guess!\n";
attempt[loc]=letter;//如果loc是个有效值,则可以将字符放在答案字符串的相应位置
loc=target.find(letter,loc+1);//使用find查询字符,开始从lco找到的,所以下一从loc+1开始
while(loc !=string::npos)
{
attempt[loc]=letter;
loc=target.find(letter,loc+1);
}
}
cout<<"Your word:"<<attempt<<endl;
if(attempt !=target)
{
if(badchars.length()>0)
cout<<"Bad choices:"<<badchars<<endl;
cout<<guesses<<"bad guesses left\n";
}
}
if(guesses>0)
cout<<"That's right!\n";
else
cout<<"Sorry,the word is"<<target<<".\n";
cout<<"Will you play anther?<y/n>";
cin>>play;
play=tolower(play);
}
cout<<"Bye\n";
return 0;
}
string还提供哪些功能
- 删除字符串的部分或全部内容,用一个字符串内容替换另一个字符串的内容。
- 将数据插入字符串中或删除字符串的数据;将字符串部分与另一个字符串比较。
- 提取子字符串;复制到另一个字符串;交换两个字符内容。
例:程序将附加字符串末尾时,会将字符串加大,内存被占用。如果执行大量这样的操作,系列很低。因此很多c++实现分配一个比实际字符串大的内存块,为字符串提供增大空间。如果字符串不断增大,超过内存大小,程序将分配一个大小为两倍的心内存。避免不断分配新内存。方法capacity()返回当前分配给字符串的内存块的大小,而reserve()方法能请求内存块的最小长度:
#include <iostream>
#include<string>
using namespace std;
int main()
{
string empty;
string small="bit";
string larger="Elephants are a girl's best friend";
cout<<"Sizes:\n";
cout<<"\tempty:"<<empty.size()<<endl;
cout<<"\tsmall:"<<small.size()<<endl;
cout<<"\tlarger:"<<larger.size()<<endl;
cout<<"Capacities:\n";
cout<<"\tempty:"<<empty.capacity()<<endl;
cout<<"\small:"<<small.capacity()<<endl;
cout<<"\tlarger:"<<larger.capacity()<<endl;
empty.reserve(50);
cout<<"Capacity after empty.reserve(50):"
<<empty.capacity()<<endl;
return 0;
}
注:该实现使用的最小容量为15个字符,这比标准容量选择小1.其它实现可能做出不同的选择。
如果有string对象,但需要C-风格字符串,该如何办?例如:可能打开名称存储在string对象中的文件:
string filename;
cout<<"Enter file name:";
cin>>filename;
ofstream fout;
不幸的是,open()方法要求使用一个C-风格字符串作为参数:幸运是,c-str()方法返回一个指向C-风格字符串的指针,该C-风格字符串的内容与用与调用c-str()方法的string对象相同。因此可以这样做:
fout.open(filename.c_str());
字符串种类
将string类看作是基于char类型的,事实上,前面指出的,string库实际上是基于一个模板类的:
template<class charT,class traits = char_traits<charT>,
class Allocator = allocator<charT> >
basic_string{
...};
模板basic_string有4个具体化,每个具体化都有一个typedef名称:
typedef basic_string<char>string;
typedef basic_string<wchar_t>wstring;
typedef basic_string<char16_t> u16string;
typedef basic_string<char32_t>u32string;
这能够使用基于类型wchar_t,char16_t,char32_t和char的字符串。甚至可以开发某种类似字符的类,并对它使用basic_string类模板。traits类描述关于选定字符类型的特点情况,如何对值进行比较。对于wchar_t,char16_t,char32_t和char类型,有预定义的char_traits模板具体化,它们都是traits的默认值,Allocator是一个管理内存分配的类。对于各种字符类型。都有预定义的allocator模板具体化,它们都是默认的,它们使用new和delete.
string的构造和赋值练习
#include <iostream>
#include<string>
using namespace std;
/*
string 构造函数
string();//创建一个空的字符串 例如: string str;
string(const string& str);//使用一个string对象初始化另一个string对象
string(const char* s);//使用字符串s初始化
string(int n, char c);//使用n个字符c初始化 v
string基本赋值操作
string& operator=(const char* s);//char*类型字符串 赋值给当前的字符串
string& operator=(const string &s);//把字符串s赋给当前的字符串
string& operator=(char c);//字符赋值给当前的字符串
string& assign(const char *s);//把字符串s赋给当前的字符串
string& assign(const char *s, int n);//把字符串s的前n个字符赋给当前的字符串
string& assign(const string &s);//把字符串s赋给当前字符串
string& assign(int n, char c);//用n个字符c赋给当前字符串
string& assign(const string &s, int start, int n);//将s从start开始n个
*/
void test01()
{
//string(const char* s);//使用字符串s初始化
string str1("hello string");
cout<<str1<<endl;//"hello string"
//string(int n, char c);//使用n个字符c初始化
string str2(10,'H');
cout<<str2<<endl;//"HHHHHHHHHH"
string str3 = str2;
cout<<str3<<endl;//"HHHHHHHHHH"
string str4;
//string& operator=(const string &s);//把字符串s赋给当前的字符串
str4 = str1;
cout<<str4<<endl;//"hello string"
//string& operator=(const char* s);//char*类型字符串 赋值给当前的字符串
string str5;
str5 = "hello str5";
cout<<str5<<endl;//"hello str5"
//string& operator=(char c);//字符赋值给当前的字符串
string str6;
str6 ='H';
cout<<str6<<endl;//"H"
//string& assign(const char *s);//把字符串s赋给当前的字符串
string str7;
str7.assign("hello str7");
cout<<str7<<endl;//"hello str7"
//string& assign(const char *s, int n);//把字符串s的前n个字符赋给当前的字 符串
string str8;
str8.assign("hello str8", 5);
cout<<str8<<endl;//"hello"
//string& assign(const string &s);//把字符串s赋给当前字符串
string str9;
str9.assign(str8);
cout<<str9<<endl;//"hello"
//string& assign(int n, char c);//用n个字符c赋给当前字符串
string str10;
str10.assign(10,'W');
cout<<str10<<endl;//"WWWWWWWWWW"
//string& assign(const string &s, int start, int n);//将s从start开始n个
string str11;
str11.assign("hehehahahaxixi", 4, 6);
cout<<str11<<endl;//"hahaha"
}
int main()
{
test01();
return 0;
}
string字符的存取练习
#include <iostream>
#include<string>
using namespace std;
/* 2 3.1.2.3 string存取字符操作
3 char& operator[](int n);//通过[]方式取字符
4 char& at(int n);//通过at方法获取字符
5 */
void test01()
{
string str1="hello string";
cout<<str1[1]<<endl;//'e'
cout<<str1.at(1)<<endl;//'e'
str1[1]='E';
cout<<str1<<endl;//"hEllo string"
str1.at(7) = 'T';
cout<<str1<<endl;//"hEllo sTring"
//[]和at的区别
try
{
//str1[1000]='G';//越界 []不抛出异常
str1.at(1000)='G';//越界 at会抛出异常
} catch(exception &e)
{
cout<<"异常:"<<e.what()<<endl;
}
}
int main()
{
test01();
return 0;
}
字符串的拼接练习
#include <iostream>
#include<string>
using namespace std;
/* 2 3.1.2.4 string拼接操作
* 3 string& operator+=(const string& str);//重载+=操作符
* 4 string& operator+=(const char* str);//重载+=操作符
* 5 string& operator+=(const char c);//重载+=操作符
* 6 string& append(const char *s);//把字符串s连接到当前字符串结尾
* 7 string& append(const char *s, int n);//把字符串s的前n个字符连接到当前字符串 结尾
* 8 string& append(const string &s);//同operator+=()
* 9 string& append(const string &s, int pos, int n);//把字符串s中从pos开始的n个 字符连接到当前字符串结尾
* 10 string& append(int n, char c);//在当前字符串结尾添加n个字符c
* 11 */
void test01()
{
string str1="hello";
string str2=" string";
//string& operator+=(const string& str);//重载+=操作符
str1 += str2;
cout<<str1<<endl;//"hello string"
string str3="hello";
//string& operator+=(const char* str);//重载+=操作符
str3 += " string";
cout<<str3<<endl;//"hello string"
string str4="hello";
//string& append(const char *s, int n);//把字符串s的前n个字符连接到当前字 符串结尾
str4.append("hehehaha",4);
cout<<str4<<endl;//"hellohehe"
//string& append(const string &s, int pos, int n);//把字符串s中从pos开始 的n个字符连接到当前字符串结尾
string str5="hello";
string str6="hehehahaha";
str5.append(str6,4,6);
cout<<str6<<endl;//"hellohahaha"
}
int main()
{
test01();
return 0;
}
字符串的查找练习
#include <iostream>
#include<string.h>
using namespace std;
/* 2 3.1.2.5 string查找和替换
* 3 int find(const string& str, int pos = 0) const; //查找str第一次出现位置,从p os开始查找
* 4 int find(const char* s, int pos = 0) const; //查找s第一次出现位置,从pos开始 查找
* 5 int find(const char* s, int pos, int n) const; //从pos位置查找s的前n个字符 第一次位置
* 6 int find(const char c, int pos = 0) const; //查找字符c第一次出现位置
* 7 int rfind(const string& str, int pos = npos) const;//查找str最后一次位置,从 pos开始查找
* 8 int rfind(const char* s, int pos = npos) const;//查找s最后一次出现位置,从po s开始查找
* 9 int rfind(const char* s, int pos, int n) const;//从pos查找s的前n个字符最后 一次位置
* 10 int rfind(const char c, int pos = 0) const; //查找字符c最后一次出现位置
* 11 string& replace(int pos, int n, const string& str); //替换从pos开始n个字符 为字符串str
* 12 string& replace(int pos, int n, const char* s); //替换从pos开始的n个字符为 字符串s
* 13 */
void test01()
{
//int find(const string& str, int pos = 0) const; //查找str第一次出现位 置,从pos开始查找
string str1="hehe:haha:xixi:haha:heihei";
//从str1中找haha
string tmp="haha";
cout<<str1.find(tmp)<<endl;//5
cout<<str1.find(tmp,10)<<endl;//15
//int find(const char* s, int pos = 0) const; //查找s第一次出现位置,从pos 开始查找
cout<<str1.find("haha")<<endl;//5
str1.replace(5,4,"###");
cout<<str1<<endl;//"hehe:###:xixi:haha:heihei"
string str2="www.sex.117114.sex.person.77.com";
//需求:将字符串中的所有"sex"用***屏蔽
int ret = 0;
while((ret = str2.find("sex")) < str2.size())
{
str2.replace(ret,strlen("sex"),"***");
}
cout<<str2<<endl;
}
int main()
{
test01();
return 0;
}
字符串比较练习
#include<iostream>
#include<string>
using namespace std;
/* 2 3.1.2.6 string比较操作
* 3 compare函数在>时返回 1,<时返回 ‐1,==时返回 0。
* 4 比较区分大小写,比较时参考字典顺序,排越前面的越小。
* 5 大写的A比小写的a小。
* 67 int compare(const string &s) const;//与字符串s比较
* 8 int compare(const char *s) const;//与字符串s比较
* 9 */
void test01()
{
string str1="hehe";
string str2 = "haha";
cout<<str1.compare(str2)<<endl;//1
cout<<str1.compare("lala")<<endl;//‐1
cout<<str1.compare("hehe")<<endl;//0
}
int main()
{
test01();
return 0;
}
字符串提取练习
include<iostream>
#include<string>
using namespace std;
/* 2 3.1.2.7 string子串
3 string substr(int pos = 0, int n = npos) const;//返回由pos开始的n个字符组成 的字符串
4 */
void test01()
{
string str1="hehehe:ha:xixixi:lalala:heihei";
//cout<<str1.substr(5,4)<<endl;
//案例:将:分割的所有字符串提取出来
int pos = 0;
while(1)
{
int ret = str1.find(":",pos);
if(ret < 0)
{
string tmp = str1.substr(pos, str1.size()‐pos);
cout<<tmp<<endl;
break;
}
string tmp = str1.substr(pos, ret‐pos);
cout<<tmp<<endl;
pos = ret+1;
}
}
int main()
{
test01();
return 0;
}
字符串插入删除练习
#include<iostream>
#include<string>
using namespace std;
/*
* string插入和删除操作
* 3 string& insert(int pos, const char* s); //插入字符串
* 4 string& insert(int pos, const string& str); //插入字符串
* 5 string& insert(int pos, int n, char c);//在指定位置插入n个字符c
* 6 string& erase(int pos, int n = npos);//删除从Pos开始的n个字符
* 7 */
void test01()
{
string str1="hello world";
str1.insert(5,"hehe");
cout<<str1<<endl;//"hellohehe world
str1.erase(5,4);//删除字符串中hehe
cout<<str1<<endl;//"hello world"
//清空字符串 str1.size()得到字符串的总大小
str1.erase(0,str1.size());
cout<<str1.size()<<endl;//0
}
int main()
{
test01();
return 0;
}
string和c-风格的字符串转换练习
#include<iostream>
#include<string>
using namespace std;
void test01()
{
string str1;//对象
char *str2 ="hello str";
//将char * 转成 string (直接完成)
str1 = str2;
cout<<str1<<endl;//hello str
string str3="hello str3";
//不能直接将string 转换成 char * 必须借助string中的c_str方法完成
//char *str4 = str3;//err
char *str4 = const_cast<char *> (str3.c_str());
cout<<str4<<endl;//"hello str3"
}
int main()
{
test01();
return 0;
}
转载:https://blog.csdn.net/weixin_50866517/article/details/116891058