(接上....)
#include < conio.h >
#include < string > using namespace std;
int main()
... {
string s="a1234567bd2efg3";
char *p="2345";
string::size_type n1,n2;
n1=s.find(p,3,2);
n2=s.find_first_of(p,5,2);
if(n1!=string::npos) cout<<"n1="<<n1<<endl;
else cout<<"n1 not find"<<endl;
if(n2!=string::npos) cout<<"n2="<<n2<<endl;
else cout<<"n2 not find"<<endl;
getch();
return 0;
}
Output
n1 not find
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.3 string insert, replace, erase
了解了string 的操作符,查找函数和substr,其实就已经了解了string的80%的操作了。insert函数, replace函数和erase函数在使用起来相对简单。下面以一个例子来说明其应用。
string只是提供了按照位置和区间的replace函数,而不能用一个string字串来替换指定string中的另一个字串。这里写一个函数来实现这个功能:
... {
string::size_type pos=0;
string::size_type srclen=strsrc.size();
string::size_type dstlen=strdst.size();
while( (pos=strBig.find(strsrc, pos)) != string::npos)
...{
strBig.replace(pos, srclen, strdst);
pos += dstlen;
}
}
看看如何调用:
#include < iostream >
using namespace std;
int main()
... {
string strinfo="This is Winter, Winter is a programmer. Do you know Winter?";
cout<<"Orign string is : "<<strinfo<<endl;
string_replace(strinfo, "Winter", "wende");
cout<<"After replace Winter with wende, the string is : "<<strinfo<<endl;
return 0;
}
其输出结果:
After replace Winter with wende, the string is :
This is wende, wende is a programmer. Do you know wende ?
如果不用replace函数,则可以使用erase和insert来替换,也能实现string_replace函数的功能:
... {
string::size_type pos=0;
string::size_type srclen=strsrc.size();
string::size_type dstlen=strdst.size();
while( (pos=strBig.find(strsrc, pos)) != string::npos)
...{
strBig.erase(pos, srclen);
strBig.insert(pos, strdst);
pos += dstlen;
}
}
2 string 和 C风格字符串
现在看了这么多例子,发现const char* 可以和string 直接转换,例如我们在上面的例子中,使用
string_replace(strinfo, "Winter", "wende");
来代用
void string_replace(string & strBig, const string & strsrc, const string &strdst)
在C语言中只有char* 和 const char*,为了使用起来方便,string提供了三个函数满足其要求:其中:const charT* c_str() const const charT* data() const size_type copy(charT* buf, size_type n, size_type pos = 0) const你或许会问,c_str()的功能包含data(),那还需要data()函数干什么?看看源码:
- c_str 直接返回一个以/0结尾的字符串。
- data 直接以数组方式返回string的内容,其大小为size()的返回值,结尾并没有/0字符。
- copy 把string的内容拷贝到buf空间中。
原来c_str()的流程是:先调用terminate(),然后在返回data()。因此如果你对效率要求比较高,而且你的处理又不一定需要以/0的方式结束,你最好选择data()。但是对于一般的C函数中,需要以const char*为输入参数,你就要使用c_str()函数。const charT* c_str () const{ if (length () == 0) return ""; terminate (); return data (); }对于c_str() data()函数,返回的数组都是由string本身拥有,千万不可修改其内容。其原因是许多string实现的时候采用了引用机制,也就是说,有可能几个string使用同一个字符存储空间。而且你不能使用sizeof(string)来查看其大小。详细的解释和实现查看Effective STL的条款15:小心string实现的多样性。
另外在你的程序中,只在需要时才使用c_str()或者data()得到字符串,每调用一次,下次再使用就会失效,如:
string strinfo( " this is Winter " );...
// 最好的方式是:foo(strinfo.c_str());
// 也可以这么用:
const char * pstr = strinfo.c_str();foo(pstr);
// 不要再使用了pstr了, 下面的操作已经使pstr无效了。
strinfo += " Hello! " ;foo(pstr); // 错误!
3 string 和 Charactor Traits
了解了string的用法,该详细看看string的真相了。前面提到string 只是basic_string的一个typedef。看看basic_string 的参数:template <class charT, class traits = char_traits<charT>,class Allocator = allocator<charT> >class basic_string{ //...}
就像Steve Donovan在过度使用C++模板中提到的,这些确实有些过头了,要不是系统自己定义了相关的一些属性,而且用了个typedef,否则还真不知道如何使用。
但复杂总有复杂道理。有了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数字。
4 string 建议
使用string 的方便性就不用再说了,这里要重点强调的是string的安全性。
- string并不是万能的,如果你在一个大工程中需要频繁处理字符串,而且有可能是多线程,那么你一定要慎重(当然,在多线程下你使用任何STL容器都要慎重)。
- string的实现和效率并不一定是你想象的那样,如果你对大量的字符串操作,而且特别关心其效率,那么你有两个选择,首先,你可以看看你使用的STL版本中string实现的源码;另一选择是你自己写一个只提供你需要的功能的类。
- string的c_str()函数是用来得到C语言风格的字符串,其返回的指针不能修改其空间。而且在下一次使用时重新调用获得新的指针。
- string的data()函数返回的字符串指针不会以'/0'结束,千万不可忽视。
- 尽量去使用操作符,这样可以让程序更加易懂(特别是那些脚本程序员也可以看懂)
5 小结
难怪有人说:
string 使用方便功能强,我们一直用它!
6 附录
函数名 | 描述 |
begin | 得到指向字符串开头的Iterator |
end | 得到指向字符串结尾的Iterator |
rbegin | 得到指向反向字符串开头的Iterator |
rend | 得到指向反向字符串结尾的Iterator |
size | 得到字符串的大小 |
length | 和size函数功能相同 |
max_size | 字符串可能的最大大小 |
capacity | 在不重新分配内存的情况下,字符串可能的大小 |
empty | 判断是否为空 |
operator[] | 取第几个元素,相当于数组 |
c_str | 取得C风格的const char* 字符串 |
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 | 从输入流中读入一行 |
Example:查找文件中的单词的出现的次数
using namespace std;
#include < fstream >
#include < string >
int WordCount_Of_File( const string filename , const string word);
void main()
... {
cout<<WordCount_Of_File("word.txt","er")<<endl;
system("pause");
}
// 在指定文件中查找单词,并返回找到的单词总数
int WordCount_Of_File( const string filename , const string word)
... {
//打开文本文件以便读入
ifstream infile(filename.c_str (),ios::in);
if(!infile)
...{
cerr<<"unable to open file"<<filename<<"--bailing out! ";
::system ("pause");
return 0;
}
//单词总数
int iCount=0;
//单行文本
string text_line ;
//依次读取每一行,这也以为着,将来无法
//使用此函数来查找诸如 "a b"字样的单词
//因为我把它截断了
while(getline(infile,text_line,' '))
...{
//为了方面检查结果,我把每一行的内容输出了,可去掉
cout<<text_line<<endl;
string::size_type pos=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;
}
}
return iCount;
}