第5章字符串(重点考察和流的结合)
STL string 类提供了强大的功能,使得许多烦琐的编程内容用简单的语句就可完成。
string 字符串类减少了C语言编程中三种最常见且最具破坏性的错误:超越数组边界,通过未被初始化或被赋以错误值的指针来访问数组元素;以及在释放了某一数组原先所分配的存储单元后仍保留的“悬挂”指针
5.1 字符串创建及初始化
5.1.1 基本创建方式
#include<string>
using namespace std;
//对象 s1~s4 均是通过构造函数的方式创建新字符串对象
int main(int argc, char * argv[]){
string sl;
//通过类的成员函数能够正确地报告其长度为 0并且没有数据元素
string s2("How are you?") ;
string s3(s2);
//是源串2从偏移量0的字开始连续取三个字符,构成新的字符串对象,因此 s4="How”
string s4(s2,0,3);
//s5 是单一赋值,可直接把 char塑赋给 s5;
string s5="Fine";
//s6 表明可将不同的初始化数据源结合在一起,本即是把一个 string 对象 2个char 型数组结合
string s6=s2+"Fine";//写成 string s6="Fine"十s2 不能编译通过
return 0;}
看出string 对象赋值,右边第一项必须是 string 类型,不能是 char 型数组
5.1.2选代器创建方式
由于可将 string 看做字符的容器对象,因此可以给 string 类的构造函数传递两个选代器,将它们之间的数据复制到新的 string 对象中。
下例中 字符串迭代器创建方式。
#include<string>
using namespace std;
int main(int argc,char * argv[]){
string sl="How are you?";
string s2(s1.begin(), sl.end());//指向的第一个字符,指向的最后一个字符的下一个位置。这样,将包含中的所有字符
//指向中的第5个字符,指向中的第8个字符的下一个位置。这样,将包含中从第5个字符到第7个字符(不包括第8个字符)的子串
string s3(sl.begin()+4,sl.begin()+7);
return 0;}
sl.begin()+4,sl.begin()+7 仅代表了 s1 部分内容"are",因此 s3="are"
5.2 字符串操作
5.2.1 插入操作
#include<string>
#include<iostream>
using namespace std;
int main(int argc,char * argv[]){
string s="do";
cout<<"Inition size is:"<<s.size()<<endl;
//insert 函数,第一个参数表明插人源串的位置,第二个参数表明要插人的字符串,因此利用该函数可实现//串首、串尾及任意位置处的字符串插人功能。
s.intert(0,”how”);
//append 函数,仅有一个输人参数,在源字符串尾部追加该字符串。
s.append("you");
//利用+实现字符串的连接,从而创建新的字符串
s=s+"do?";
cout<<"final size is!"<<s.size()<<endl;
cout<<s;
return 0;
}
size 函数,无输人参数,通过例子可知,它表明字符长度值,即有多少个字符,初始的时候值是2,当完成增加后,值就变成 14了。这说明字符 string 类本身可根据需要自动调节串所在的内存空间大小,编程者无须参与。
5.2.2替换操作
常用的是 replace 函数,有三个输人参数:第一个用于指示从字符串的什么位置开始改写;第二个用于指示从原字符串中删除多少个字符,第三个是替换字符串的值
#include<string>
#include<iostream>
using namespace std;
int main(int argc,char * argv[]){
string s="what's your name?";
cout <<"替换前:"<<s<<endl;
s.replace(7,4,"her");
cout <<"替换后:"<<s<<endl;
return 0;
}
5.3 字符串查询
string::npos:这是 string 类中的一个成员变量,一般应用在判断系统查询函数的返回值上,若等于该值,表明没有符合查询条件的结果值。
find 函数:在一个字符串中查找指定的单个字符或字符组。如果找到,就返回首次匹配的开始位置;如果没有找到匹配的内容,则返回 string::npos。一般有两个人参数,一个是待查询的字符串,一个是查询的起始位置,默认起始位置为 0。
find_first_of 函数:在一个字符串中进行查找,返回值是第一个与指定字符串中何字符匹配的字符位置;如果没有找到匹配的内容,则返回 string::npos。一般有两个输人参数,一个是待查询的字符串,一个是查询的起始位置,默认起始位置为 0。
find_last_of 函数:在一个字符串中进行查找,返回最后一个与指定字符串中任何字符匹配的字符位置;如果没有找到匹配的内容,则返回string::npos。一般有两个输人参数,一个是待查询的字符串,一个是查询的起始位置,默认起始位置为 0。
find_first_not_of 函数:在一个字符中进行查找,返回第一个与指定字符串中任何字符都不匹配的元素位置;如果没有找到匹配的内容,则返回 string::npos。般有两个输人参数,一个是待查询的字符串,一个是查询的起始位置,默认起始位置为0。
find last_not_of函数;在一个字符中进行查找,返回下标值最大的与指定字符
中任何字符都不匹配的元素位置;如果没有找到匹配的内容,则返回 string::npos一般有两个输人参数,一个是待查询的字符串,一个是查询的起始位置,默认起始位置为0。
rfind 函数:对一个串从尾至头查找指定的单个字符或字符组,如果找到,就返回首次匹配的开始位置;如果没有查找到匹配的内容,则返回 string :npos。一般有两个输人参数,一个是待查询的字符串,一个是询的起始位置,默认起始位量为串尾部。
下列示例代码:
#include <string>
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
string s = "what't your name? my name is TOM. How do you do? Fine, thanks. ";
int n = s.find("your");
cout << "the first your pos:" << n << endl;
n = s.find("you", 15);
cout << "the first your pos begin from 15:" << n << endl;
n = s.find_first_of("abcde");
cout << "find pos when character within abcde:" << n << endl;
n = s.find_first_of("abcde", 3);
cout << "find pos begin from 2 when character within abcde:" << n << endl ;
return 0;
}
结果如下:
(1)find(“your”), 是从源串起始位置为零(默认值)处查找有”your”字符串位置,所以结果为7。
the first your pos: 7
(2)find(“you”, 15), 是从源串起始位置为15处查找有”you”字符串位置,所以结果为41。
the first your pos begin from 15:41
(3)find_first_of(“abcde”),是从源串起始位置为零(默认值)处依次查找每个字符,如果它在输入的字符串参数“abcde”中,则返回该字符的位置。由于源串头两个字符”w”,”h”不在查找的串中,而第3个字符“a”在查找的目的串中,又由于字符串是以0位基点的,所以结果值为2。
(4)find_first_of(“abcde”,3),是从源串起始位置为3处依次查找每个字符,如果它在输入的字符串参数“abcde”中,则返回该字符的位置。分析同find_first_of(“abcde”),结果为13,对应的源串字符为“a”。
5.4 在字符串中删除字符
在字符串中删除字符主要用 erase 函数,有两个选代器输人参数,之间表示的字符将会被删除掉
#include<string>
#include<iostream>
using namespace std;
int main(int argc, char * argv[]){
string s1="How are you?";
s1.erase(s1.begin(),s1.begin()+3);
cout<<"after erase to sl is:"<<s1<<endl;
string s2="Fine,thanks";
s2.erase (s2.begin(),s2.end());
cout<<"after erase to s2 is;"<<s2<<endl;
return 0;}
s1.erase(s1.begin(),s1.begin()+3)表明删除 s1串的前三个字符,所以结果是s1变为"are you?";
s2.erase(s2.begin(),s2.end())表明删除整个 s2 字符串,故没有显示结果。
5.5 字符串比较
依据ASCII值来比较大小。若字符s1“大于”s2,表明两者相比较时遇到了第一对不同的字符,字符串 s1中第一个不同的字符比字符 s2 中同样位置的字符在ASCII表中的位置更靠后
C++STL提供了多种字符串比较方法,它们各具特色。其中最简单的就是使用非成员的重载运算符函数 operator==、operator! =、operator>、operator<、operator>=、operator<=
#include<string>
#include<iostream>
using namespace std;
int main(int argc, char * argv[]){
string s1="this" ;
string s2="that";
if(s1>s2) cout<<"s1>s2"<<endl;
if(s1==s2)cout<<"s1=s2"<<endl;
if(s1<s2) cout<<"s1<s2"<<endl;
return 0;}
直接运用重载运算符s1、s2 前两个字符是相同的,第三个字符比较时由于i的ASCII大于 a 的 ASCII,因此 s>s2。
5.6 综合示例
整型数据与字符串互相转化
#include<string>
#include<iostream>
#include<sstream>
using namespace std;
int main(int argc, char * argv[]){
//将整型 10转化成字符串”10”
int n1=10;
string s1;
stringstream os1;
os1<<n1;
os1>>s1;
cout<<"整型10转化成字符串10:"<<s1<<endl;
//将字符串”123”转化成整型 123
int n2=0;
string s2="123";
stringstream os2;
os2<<s2;
os2 >>n2;
cout<<"字符串 123 转化成整型 123:"<<n2<<endl;
return 0;}
在 STL中,实现基本数据类型与字符串的相互转化,用流类如 stringstream间媒介是一个很好的思路。stringstream 是输人输出流,利用输出流功能先填充输出区,再利用输人流功能依次读缓冲区,赋值给相应的变量。
下方整个程序的功能是将字符串按照空格进行分割,并逐个输出分割后的子串
1)在搜索每一个符合条件的子串前,先使子串起始位置等于结束位置,然后按照find firsr_of 函数查找相应子串,若有则子串结束位置不等于起始位置,再利用 substr 函数取之间的字符,即得子串值
2)程序中的 if 语句是判断最后一个子串的边界条件的,若起始位置等于该字符串的长度,表明已无字符串可取,否则还有一个子串。例如本例中的"How are you?”,当前两个串("How","are")取完后,size_prev_pos =size_pos=8,但最后一个串"you?"已搜索不到空格字符,因此跳出 while 循环,if 语句条件成立,可取到子串"you?"。当然,若源串是"How are you?",结尾多一空格,则所有的子串均可在 while 条件语句中搜索到,if 语句条件不成立,条件体也就不执行了。
例:利用STL拆分字符串(次重点)
#include <iostream>
#include <cstdio>
#include <string>
#include <stack>
using namespace std ;
int main(int argc ,char *argv[])
{
string strText = "How are you !?" ;
string strseparator = " " ;//strseparator是分隔符,初始化为一个空格字符
string strResult;//存储分割后的子串
int Separator_pos = 0 ;//当前分割位置的索引,初始化为0
int size_prev_pos = 0 ;//上一个分割位置的索引,初始化为0
//strText.find_first_of(space,Separator_pos)用于在strText中从Separator_pos位置开始查找第一个与space(即空格字符)匹配的字符的索引。
//如果找到了匹配的字符,则返回该字符的索引;如果找不到匹配的字符,则返回string::npos,表示查找失败
while((Separator_pos = strText.find_first_of(space,Separator_pos))!=string::npos)
{
//将分割位置之间的子串提取出来,并赋值给strResult。substr函数的参数表示要提取子串的起始位置和长度
strResult= strText.substr(size_prev_pos,Separator_pos-size_prev_pos) ;
cout<<"string = "<<strResult<<endl;
size_prev_pos = ++Separator_pos ;//将分割位置更新为下一个位置
//++Separator_pos
//size_prev_pos=++Separator_pos
}
if(size_prev_pos!=strText.size())//检查是否还有剩余的子串未输出
{
//提取剩余的子串,并输出。
strResult= strText.substr(size_prev_pos,Separator_pos-size_prev_pos) ;
cout<<"string = "<<strResult<<endl;
}
return 0 ;
}
程序的功能是将字符串按照空格进行分割,并逐个输出分割后的子串的另一种方法
#include<iostream>
#include<sstream>
#include<string>
using namespace std;
int main(int argc, char * argv[]){
string strResult="";//用于存储分割后的子串
string strText="How are you";
//istr是一个字符串输入流对象,通过istringstream将strText封装成字符串输入流
istringstream istr(strText);
while(! istr.eof()){//只要字符串输入流还未到达末尾
//>>从字符串输入流中读取字符串,并将其赋值给strResult。
//输入运算符会按照空格进行分隔,每次读取一个子串
istr>>strResult;
cout<<"string="<<strResult<<endl;}
return 0;}
第一个程序使用了find_first_of函数来查找分隔符的位置,并使用substr函数来提取子串。它通过不断更新分隔位置的方式来进行字符串的分割。这个方法比较适合在较为复杂的字符串分割场景中使用,可以自定义多个分隔符,并对分割后的子串进行进一步处理。需要更灵活的分割方式或对分割后的子串进行进一步处理,可以选择第一个程序的方法
第二个程序使用了字符串输入流istringstream和输入运算符>>来实现字符串的分割。它将整个字符串封装成输入流,并使用输入运算符从流中逐个读取子串。这个方法比较简洁,适合用于按照单一分隔符进行简单字符串分割并输出的场景。只需按照单一分隔符进行简单分割并输出,可以选择第二个程序的方法。
若按其他字符比如“,”拆分该怎么办,要用 getline 函数代替提取符>> 改变while循环为以下内容
while(! istr.eof()){
getline(istr,strResult,',');
cout<<"string="<<strResult<<endl;}
以下例子C++ STL实现的 string trim()功能。去掉字符串两端的空格,STL中没有单独的 trim 函数可用,主要是应用erase find_first_not_of,find_last_not_of 函数
#include<iostream>
#include<sstream>
#include<string>
using namespace std;
int main(int argc, char * argv[]){
string s="hello";
//找到字符串开头第一个不是空格的字符的位置,
//然后从字符串开头到该位置之前的所有字符都被删除,即去除开头的空格
s.erase(0,s.find_first_not of(" "));
//找到字符串结尾最后一个不是空格的字符的位置,然后从该位置之后的所有字符都被删除,即去除结尾的空格
s.erase(s.find_last_not_of(" ")+1);
cout<<s;
return 0;
}
对string类的进一步封装。好的办法是进一步封装 string类,编制所需要的字符串函数,这样使调用方就可调用所编各函数,完成相应功能。
例 如要实现以下功能:
(1)字符串去空格;
(2)字符串转化成整型数;
(3)整型数转化成字符串
#include<string>
#include<sstream>
#include<iostream>
using namespace std;
class ext_string: public string{
public:
int GetInt(string strText)//把字符申转化为整型
{
int n=0;
stringstream os;
os<<strText;//使用stringstream对象os将strText写入
os>>n;//其读取为整型数n
return n;}
string GetString(int n)//把整型转化为字符串
{
string s;
stringstream os;//使用stringstream对象os将n写入
os<<n;
os>>s;
return s;}
string GetTrim(string strText)//去字符串两侧空格
{
strText.erase(0,strText.find_first_not_of("")); //删除左空格
strText.erase(strText.find_last_not_of(" ")+1);//剥除右空格
return strText;
}
};
int main(int argc, char * argv[]){
ext_string extstring;
int n=extstring.GetInt("123");//将串”123"转化为整形数
string s1=extstring.GetString(456);//将整型数转化为字符串
string s2=extstring.GetTrim("hello");//去两侧空格
cout<<"The string'123' convert to integer is: "<<n<<endl;
cout<<"The integer 456 convert to string is;"<<s1<<endl;
cout<<"The string'hello' erase spaceis;"<<s2<<endl;
return 0;
}
5.7 习题
1、下述语句后,变量b内容是什么?D
string a = “1”;
a.insert(0, “2”); //将字符串 "2" 插入到字符串 a 的开头位置,此时 a 的值为 "21"
a.append(3); //将字符 '3' 追加到字符串 a 的末尾 3 次,此时 a 的值为 "21333
string b = a + “3”;//b = a + "3" 将字符串 a 和字符串 "3" 进行拼接,得到字符串 "21333",将其赋值给变量 b
a)1233 b) 2313 c) 3213 d) 2133// string的存储结构是从a【0】开始
2、想要将string S=”C++ STL: isn’t hard to learn?”中“+:’;”等符号的迭代器位置找出来,那个函数最合理?D
a) search b) for_each c) count d) find_first_of
find_first_of 函数可以在一个字符串(或其他序列容器)中查找第一个匹配指定字符集中任何一个字符的位置,并返回一个迭代器
3、 将上述11题中s1的内存空间内容用()函数转化成字符串。
A. to string() B. to_string() C. getline() D. GetString()
4、字符串的插入:
(1)insert函数,(任意位置插入)第一个参数表明插人源串的位置,第二个参数表明要插人的字符串,因此利用该函数可实现串首、串尾及任意位置处的字符串插人功能。
(2)append函数,(首字符前,尾字符后)仅有一个输人参数在源字符串尾部追加该字符串。
(3)利用+实现字符串的连接,从而创建新的字符串。
(4)size 函数,无输入参数,通过例子可知:它表明字符串长度值,即有多少个字符,初始的时候值是2,当完成增加后,值就变成 14 了。说明字符串 string 类本身可根据需要自动调节串所在的内存空间大小,编程者无须参与,这一点是 C语言中 char 型数组无法比拟的。
5、字符串的替换:
Replace函数有三个参数:①用于指示从字符串的什么位置开始改写②用于指示从原字符串中删除多少个字符③是替换字符串的
6、字符串的初始化?
C++ STL中字符串的初始化有多种方式,以下是两种常见的初始化方法:
(1). 使用字符串字面值进行初始化:
#include <string>
std::string str1 = "Hello World";
(2). 使用构造函数进行初始化:
#include <string>
std::string str2("Hello World");
两种方法都可以用来初始化C++ STL中的字符串。第一种方法使用字符串字面值直接赋值给字符串变量,第二种方法使用构造函数将字符串字面值作为参数传递给字符串对象。