详细解说STL string(二)
2007-09-15 23:21
761 查看
(接上....)
#include<iostream>
#include<conio.h>
#include<string>usingnamespacestd;
intmain()
...{
strings="a1234567bd2efg3";
char*p="2345";
string::size_typen1,n2;
n1=s.find(p,3,2);
n2=s.find_first_of(p,5,2);
if(n1!=string::npos)cout<<"n1="<<n1<<endl;
elsecout<<"n1notfind"<<endl;
if(n2!=string::npos)cout<<"n2="<<n2<<endl;
elsecout<<"n2notfind"<<endl;
getch();
return0;
}
Output
n1notfind
n2=10
find和rfind都还比较容易理解,一个是正向匹配,一个是逆向匹配,后面的参数pos都是用来指定起始查找位置。对于find_first_of和find_last_of就不是那么好理解。
find_first_of是给定一个要查找的字符集,找到这个字符集中任何一个字符所在字符串中第一个位置。
find()和find_first_of()的区别是find_first_of是查找所给字符串的任意字符的第一次出现。
参数p,4,2的意思是从s的第4号位置开始,查找p的前两个字符。
所以s.find(p,4,2);就是在s="a123bd1efg"的"b"开始查找"12"
s.find_first_of(p,4,2);就是在s="a123bd1efg"的"b"开始查找"1"或者"2"(2表示p最前面的2个字符中,找到其中任意一个即可)
string只是提供了按照位置和区间的replace函数,而不能用一个string字串来替换指定string中的另一个字串。这里写一个函数来实现这个功能:
voidstring_replace(string&strBig,conststring&strsrc,conststring&strdst)
...{
string::size_typepos=0;
string::size_typesrclen=strsrc.size();
string::size_typedstlen=strdst.size();
while((pos=strBig.find(strsrc,pos))!=string::npos)
...{
strBig.replace(pos,srclen,strdst);
pos+=dstlen;
}
}
看看如何调用:
#include<string>
#include<iostream>
usingnamespacestd;
intmain()
...{
stringstrinfo="ThisisWinter,Winterisaprogrammer.DoyouknowWinter?";
cout<<"Orignstringis:"<<strinfo<<endl;
string_replace(strinfo,"Winter","wende");
cout<<"AfterreplaceWinterwithwende,thestringis:"<<strinfo<<endl;
return0;
}
其输出结果:
Orignstringis:ThisisWinter,Winterisaprogrammer.DoyouknowWinter?
AfterreplaceWinterwithwende,thestringis:
Thisiswende,wendeisaprogrammer.Doyouknowwende?
如果不用replace函数,则可以使用erase和insert来替换,也能实现string_replace函数的功能:
voidstring_replace(string&strBig,conststring&strsrc,conststring&strdst)
...{
string::size_typepos=0;
string::size_typesrclen=strsrc.size();
string::size_typedstlen=strdst.size();
while((pos=strBig.find(strsrc,pos))!=string::npos)
...{
strBig.erase(pos,srclen);
strBig.insert(pos,strdst);
pos+=dstlen;
}
}
当然,这种方法没有使用replace来得直接。
来代用
其中:
c_str直接返回一个以/0结尾的字符串。
data直接以数组方式返回string的内容,其大小为size()的返回值,结尾并没有/0字符。
copy把string的内容拷贝到buf空间中。
你或许会问,c_str()的功能包含data(),那还需要data()函数干什么?看看源码:
原来c_str()的流程是:先调用terminate(),然后在返回data()。因此如果你对效率要求比较高,而且你的处理又不一定需要以/0的方式结束,你最好选择data()。但是对于一般的C函数中,需要以constchar*为输入参数,你就要使用c_str()函数。
对于c_str()data()函数,返回的数组都是由string本身拥有,千万不可修改其内容。其原因是许多string实现的时候采用了引用机制,也就是说,有可能几个string使用同一个字符存储空间。而且你不能使用sizeof(string)来查看其大小。详细的解释和实现查看EffectiveSTL的条款15:小心string实现的多样性。
另外在你的程序中,只在需要时才使用c_str()或者data()得到字符串,每调用一次,下次再使用就会失效,如:
stringstrinfo("thisisWinter");...
//最好的方式是:foo(strinfo.c_str());
//也可以这么用:
constchar*pstr=strinfo.c_str();foo(pstr);
//不要再使用了pstr了,下面的操作已经使pstr无效了。
strinfo+="Hello!";foo(pstr);//错误!
[/code]
char_traits不仅是在basic_string中有用,在basic_istream和basic_ostream中也需要用到。
就像SteveDonovan在过度使用C++模板中提到的,这些确实有些过头了,要不是系统自己定义了相关的一些属性,而且用了个typedef,否则还真不知道如何使用。
但复杂总有复杂道理。有了char_traits,你可以定义自己的字符串类型。当然,有了char_traits<char>和char_traits<wchar_t>你的需求使用已经足够了,为了更好的理解string,咱们来看看char_traits都有哪些要求。
如果你希望使用你自己定义的字符,你必须定义包含下列成员的结构:
想看看实际的例子,你可以看看sgiSTL的char_traits结构源码.
现在默认的string版本中,并不支持忽略大小写的比较函数和查找函数,如果你想练练手,你可以试试改写一个char_traits,然后生成一个case_string类,也可以在string上做继承,然后派生一个新的类,例如:ext_string,提供一些常用的功能,例如:
定义分隔符。给定分隔符,把string分为几个字段。
提供替换功能。例如,用winter,替换字符串中的wende
大小写处理。例如,忽略大小写比较,转换等
整形转换。例如把"123"字符串转换为123数字。
这些都是常用的功能,如果你有兴趣可以试试。其实有人已经实现了,看看ExtendedSTLstring。如果你想偷懒,下载一个头文件就可以用,有了它确实方便了很多。要是有人能提供一个支持正则表达式的string,我会非常乐意用。
string并不是万能的,如果你在一个大工程中需要频繁处理字符串,而且有可能是多线程,那么你一定要慎重(当然,在多线程下你使用任何STL容器都要慎重)。
string的实现和效率并不一定是你想象的那样,如果你对大量的字符串操作,而且特别关心其效率,那么你有两个选择,首先,你可以看看你使用的STL版本中string实现的源码;另一选择是你自己写一个只提供你需要的功能的类。
string的c_str()函数是用来得到C语言风格的字符串,其返回的指针不能修改其空间。而且在下一次使用时重新调用获得新的指针。
string的data()函数返回的字符串指针不会以'/0'结束,千万不可忽视。
尽量去使用操作符,这样可以让程序更加易懂(特别是那些脚本程序员也可以看懂)
string使用方便功能强,我们一直用它!
Example:查找文件中的单词的出现的次数
#include<iostream>
usingnamespacestd;
#include<fstream>
#include<string>
intWordCount_Of_File(conststringfilename,conststringword);
voidmain()
...{
cout<<WordCount_Of_File("word.txt","er")<<endl;
system("pause");
}
//在指定文件中查找单词,并返回找到的单词总数
intWordCount_Of_File(conststringfilename,conststringword)
...{
//打开文本文件以便读入
ifstreaminfile(filename.c_str(),ios::in);
if(!infile)
...{
cerr<<"unabletoopenfile"<<filename<<"--bailingout!";
::system("pause");
return0;
}
//单词总数
intiCount=0;
//单行文本
stringtext_line;
//依次读取每一行,这也以为着,将来无法
//使用此函数来查找诸如"ab"字样的单词
//因为我把它截断了
while(getline(infile,text_line,''))
...{
//为了方面检查结果,我把每一行的内容输出了,可去掉
cout<<text_line<<endl;
string::size_typepos=0;//记录找到单词的位置
while((pos=text_line.find(word,pos))!=string::npos)//npos的含义,string::npos的类型是string::size_type,
...{
//所以,一旦需要把一个索引与npos相比,这个索引值必须是string::size_type类型的,更多的情况下,
//我们可以直接把函数和npos进行比较(如:if(s.find(“jia”)==string::npos))。
//找到单词,单词总数+1
iCount++;
//这里用的是++pos,而不是pos+=word.size()
//比如在字符"aaaaa"查找"aaa",答案是1还是3的问题
//我认为应该是3,所以++pos;
++pos;
}
}
returniCount;
}
#include<iostream>
#include<conio.h>
#include<string>usingnamespacestd;
intmain()
...{
strings="a1234567bd2efg3";
char*p="2345";
string::size_typen1,n2;
n1=s.find(p,3,2);
n2=s.find_first_of(p,5,2);
if(n1!=string::npos)cout<<"n1="<<n1<<endl;
elsecout<<"n1notfind"<<endl;
if(n2!=string::npos)cout<<"n2="<<n2<<endl;
elsecout<<"n2notfind"<<endl;
getch();
return0;
}
Output
n1notfind
n2=10
find和rfind都还比较容易理解,一个是正向匹配,一个是逆向匹配,后面的参数pos都是用来指定起始查找位置。对于find_first_of和find_last_of就不是那么好理解。
find_first_of是给定一个要查找的字符集,找到这个字符集中任何一个字符所在字符串中第一个位置。
find()和find_first_of()的区别是find_first_of是查找所给字符串的任意字符的第一次出现。
参数p,4,2的意思是从s的第4号位置开始,查找p的前两个字符。
所以s.find(p,4,2);就是在s="a123bd1efg"的"b"开始查找"12"
s.find_first_of(p,4,2);就是在s="a123bd1efg"的"b"开始查找"1"或者"2"(2表示p最前面的2个字符中,找到其中任意一个即可)
1.3stringinsert,replace,erase
了解了string的操作符,查找函数和substr,其实就已经了解了string的80%的操作了。insert函数,replace函数和erase函数在使用起来相对简单。下面以一个例子来说明其应用。string只是提供了按照位置和区间的replace函数,而不能用一个string字串来替换指定string中的另一个字串。这里写一个函数来实现这个功能:
voidstring_replace(string&strBig,conststring&strsrc,conststring&strdst)
...{
string::size_typepos=0;
string::size_typesrclen=strsrc.size();
string::size_typedstlen=strdst.size();
while((pos=strBig.find(strsrc,pos))!=string::npos)
...{
strBig.replace(pos,srclen,strdst);
pos+=dstlen;
}
}
看看如何调用:
#include<string>
#include<iostream>
usingnamespacestd;
intmain()
...{
stringstrinfo="ThisisWinter,Winterisaprogrammer.DoyouknowWinter?";
cout<<"Orignstringis:"<<strinfo<<endl;
string_replace(strinfo,"Winter","wende");
cout<<"AfterreplaceWinterwithwende,thestringis:"<<strinfo<<endl;
return0;
}
其输出结果:
Orignstringis:ThisisWinter,Winterisaprogrammer.DoyouknowWinter?
AfterreplaceWinterwithwende,thestringis:
Thisiswende,wendeisaprogrammer.Doyouknowwende?
如果不用replace函数,则可以使用erase和insert来替换,也能实现string_replace函数的功能:
voidstring_replace(string&strBig,conststring&strsrc,conststring&strdst)
...{
string::size_typepos=0;
string::size_typesrclen=strsrc.size();
string::size_typedstlen=strdst.size();
while((pos=strBig.find(strsrc,pos))!=string::npos)
...{
strBig.erase(pos,srclen);
strBig.insert(pos,strdst);
pos+=dstlen;
}
}
当然,这种方法没有使用replace来得直接。
2string和C风格字符串
现在看了这么多例子,发现constchar*可以和string直接转换,例如我们在上面的例子中,使用string_replace(strinfo,"Winter","wende");
来代用
voidstring_replace(string&strBig,conststring&strsrc,conststring&strdst)
在C语言中只有char*和constchar*,为了使用起来方便,string提供了三个函数满足其要求:[code]constcharT*c_str()constconstcharT*data()constsize_typecopy(charT*buf,size_typen,size_typepos=0)const
其中:
c_str直接返回一个以/0结尾的字符串。
data直接以数组方式返回string的内容,其大小为size()的返回值,结尾并没有/0字符。
copy把string的内容拷贝到buf空间中。
你或许会问,c_str()的功能包含data(),那还需要data()函数干什么?看看源码:
constcharT*c_str()const{if(length()==0)return"";terminate();returndata();}
原来c_str()的流程是:先调用terminate(),然后在返回data()。因此如果你对效率要求比较高,而且你的处理又不一定需要以/0的方式结束,你最好选择data()。但是对于一般的C函数中,需要以constchar*为输入参数,你就要使用c_str()函数。
对于c_str()data()函数,返回的数组都是由string本身拥有,千万不可修改其内容。其原因是许多string实现的时候采用了引用机制,也就是说,有可能几个string使用同一个字符存储空间。而且你不能使用sizeof(string)来查看其大小。详细的解释和实现查看
另外在你的程序中,只在需要时才使用c_str()或者data()得到字符串,每调用一次,下次再使用就会失效,如:
stringstrinfo("thisisWinter");...
//最好的方式是:foo(strinfo.c_str());
//也可以这么用:
constchar*pstr=strinfo.c_str();foo(pstr);
//不要再使用了pstr了,下面的操作已经使pstr无效了。
strinfo+="Hello!";foo(pstr);//错误!
[/code]
3string和CharactorTraits
了解了string的用法,该详细看看string的真相了。前面提到string只是basic_string的一个typedef。看看basic_string的参数:template<classcharT,classtraits=char_traits<charT>,classAllocator=allocator<charT>>classbasic_string{//...}
char_traits不仅是在basic_string中有用,在basic_istream和basic_ostream中也需要用到。
就像SteveDonovan在
但复杂总有复杂道理。有了char_traits,你可以定义自己的字符串类型。当然,有了char_traits<char>和char_traits<wchar_t>你的需求使用已经足够了,为了更好的理解string,咱们来看看char_traits都有哪些要求。
如果你希望使用你自己定义的字符,你必须定义包含下列成员的结构:
char_type | 字符类型 |
int_type | int类型 |
pos_type | 位置类型 |
off_type | 表示位置之间距离的类型 |
state_type | 表示状态的类型 |
assign(c1,c2) | 把字符c2赋值给c1 |
eq(c1,c2) | 判断c1,c2是否相等 |
lt(c1,c2) | 判断c1是否小于c2 |
length(str) | 判断str的长度 |
compare(s1,s2,n) | 比较s1和s2的前n个字符 |
copy(s1,s2,n) | 把s2的前n个字符拷贝到s1中 |
move(s1,s2,n) | 把s2中的前n个字符移动到s1中 |
assign(s,n,c) | 把s中的前n个字符赋值为c |
find(s,n,c) | 在s的前n个字符内查找c |
eof() | 返回end-of-file |
to_int_type(c) | 将c转换成int_type |
to_char_type(i) | 将i转换成char_type |
not_eof(i) | 判断i是否为EOF |
eq_int_type(i1,i2) | 判断i1和i2是否相等 |
现在默认的string版本中,并不支持忽略大小写的比较函数和查找函数,如果你想练练手,你可以试试改写一个char_traits,然后生成一个case_string类,也可以在string上做继承,然后派生一个新的类,例如:ext_string,提供一些常用的功能,例如:
定义分隔符。给定分隔符,把string分为几个字段。
提供替换功能。例如,用winter,替换字符串中的wende
大小写处理。例如,忽略大小写比较,转换等
整形转换。例如把"123"字符串转换为123数字。
这些都是常用的功能,如果你有兴趣可以试试。其实有人已经实现了,看看
4string建议
使用string的方便性就不用再说了,这里要重点强调的是string的安全性。string并不是万能的,如果你在一个大工程中需要频繁处理字符串,而且有可能是多线程,那么你一定要慎重(当然,在多线程下你使用任何STL容器都要慎重)。
string的实现和效率并不一定是你想象的那样,如果你对大量的字符串操作,而且特别关心其效率,那么你有两个选择,首先,你可以看看你使用的STL版本中string实现的源码;另一选择是你自己写一个只提供你需要的功能的类。
string的c_str()函数是用来得到C语言风格的字符串,其返回的指针不能修改其空间。而且在下一次使用时重新调用获得新的指针。
string的data()函数返回的字符串指针不会以'/0'结束,千万不可忽视。
尽量去使用操作符,这样可以让程序更加易懂(特别是那些脚本程序员也可以看懂)
5小结
难怪有人说:string使用方便功能强,我们一直用它!
6附录
string函数列表函数名 | 描述 |
begin | 得到指向字符串开头的Iterator |
end | 得到指向字符串结尾的Iterator |
rbegin | 得到指向反向字符串开头的Iterator |
rend | 得到指向反向字符串结尾的Iterator |
size | 得到字符串的大小 |
length | 和size函数功能相同 |
max_size | 字符串可能的最大大小 |
capacity | 在不重新分配内存的情况下,字符串可能的大小 |
empty | 判断是否为空 |
operator[] | 取第几个元素,相当于数组 |
c_str | 取得C风格的constchar*字符串 |
data | 取得字符串内容地址 |
operator= | 赋值操作符 |
reserve | 预留空间 |
swap | 交换函数 |
insert | 插入字符 |
append | 追加字符 |
push_back | 追加字符 |
operator+= | +=操作符 |
erase | 删除字符串 |
clear | 清空字符容器中所有内容 |
resize | 重新分配空间 |
assign | 和赋值操作符一样 |
replace | 替代 |
copy | 字符串到空间 |
find | 查找 |
rfind | 反向查找 |
find_first_of | 查找包含子串中的任何字符,返回第一个位置 |
find_first_not_of | 查找不包含子串中的任何字符,返回第一个位置 |
find_last_of | 查找包含子串中的任何字符,返回最后一个位置 |
find_last_not_of | 查找不包含子串中的任何字符,返回最后一个位置 |
substr | 得到字串 |
compare | 比较字符串 |
operator+ | 字符串链接 |
operator== | 判断是否相等 |
operator!= | 判断是否不等于 |
operator< | 判断是否小于 |
operator>> | 从输入流中读入字符串 |
operator<< | 字符串写入输出流 |
getline | 从输入流中读入一行 |
#include<iostream>
usingnamespacestd;
#include<fstream>
#include<string>
intWordCount_Of_File(conststringfilename,conststringword);
voidmain()
...{
cout<<WordCount_Of_File("word.txt","er")<<endl;
system("pause");
}
//在指定文件中查找单词,并返回找到的单词总数
intWordCount_Of_File(conststringfilename,conststringword)
...{
//打开文本文件以便读入
ifstreaminfile(filename.c_str(),ios::in);
if(!infile)
...{
cerr<<"unabletoopenfile"<<filename<<"--bailingout!";
::system("pause");
return0;
}
//单词总数
intiCount=0;
//单行文本
stringtext_line;
//依次读取每一行,这也以为着,将来无法
//使用此函数来查找诸如"ab"字样的单词
//因为我把它截断了
while(getline(infile,text_line,''))
...{
//为了方面检查结果,我把每一行的内容输出了,可去掉
cout<<text_line<<endl;
string::size_typepos=0;//记录找到单词的位置
while((pos=text_line.find(word,pos))!=string::npos)//npos的含义,string::npos的类型是string::size_type,
...{
//所以,一旦需要把一个索引与npos相比,这个索引值必须是string::size_type类型的,更多的情况下,
//我们可以直接把函数和npos进行比较(如:if(s.find(“jia”)==string::npos))。
//找到单词,单词总数+1
iCount++;
//这里用的是++pos,而不是pos+=word.size()
//比如在字符"aaaaa"查找"aaa",答案是1还是3的问题
//我认为应该是3,所以++pos;
++pos;
}
}
returniCount;
}
相关文章推荐
- (转)详细解说STL string
- 详细解说STL string
- 详细解说STL string
- 转:详细解说STL string
- 详细解说STL string
- 详细解说STL string(一)
- 详细解说STL string
- 详细解说STL string 【收藏】
- 详细解说STL string
- 详细解说STL string
- 详细解说STL string
- 详细解说STL string
- 详细解说STL string
- 详细解说STL string
- 详细解说STL string
- 详细解说STL string
- 详细解说STL string
- 详细解说STL string
- 详细解说STL string
- 详细解说STL string