详细解说STL string(二)

(接上....) 

 

#include < iostream >
#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中的另一个字串。这里写一个函数来实现这个功能:

 

void  string_replace( string   &  strBig,  const   string   &  strsrc,  const   string   & strdst) 
{        
    
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  < string >
#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;
}

其输出结果:

 

Orign  string   is  :This  is  Winter, Winter  is  a programmer. Do you know Winter ?
After replace Winter with wende, the 
string   is  :
This 
is  wende, wende  is  a programmer. Do you know wende ?

如果不用replace函数,则可以使用erase和insert来替换,也能实现string_replace函数的功能:

 

void  string_replace( string   &  strBig,  const   string   &  strsrc,  const   string   & strdst) 
{        
    
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;        
    }

}
当然,这种方法没有使用replace来得直接。

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 
 
其中:
  1. c_str 直接返回一个以/0结尾的字符串。
  2. data 直接以数组方式返回string的内容,其大小为size()的返回值,结尾并没有/0字符。
  3. copy 把string的内容拷贝到buf空间中。
你或许会问,c_str()的功能包含data(),那还需要data()函数干什么?看看源码:
const charT* c_str () const{ if (length () == 0) return ""; terminate (); return data (); }
 
原来c_str()的流程是:先调用terminate(),然后在返回data()。因此如果你对效率要求比较高,而且你的处理又不一定需要以/0的方式结束,你最好选择data()。但是对于一般的C函数中,需要以const char*为输入参数,你就要使用c_str()函数。

对于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{        //...}
 
char_traits不仅是在basic_string 中有用,在basic_istream 和 basic_ostream中也需要用到。

就像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是否相等
想看看实际的例子,你可以看看sgi STL的 char_traits结构源码.

现在默认的string版本中,并不支持忽略大小写的比较函数和查找函数,如果你想练练手,你可以试试改写一个char_traits , 然后生成一个case_string类, 也可以在string 上做继承,然后派生一个新的类,例如:ext_string,提供一些常用的功能,例如:

  1. 定义分隔符。给定分隔符,把string分为几个字段。
  2. 提供替换功能。例如,用winter, 替换字符串中的wende
  3. 大小写处理。例如,忽略大小写比较,转换等
  4. 整形转换。例如把"123"字符串转换为123数字。
这些都是常用的功能,如果你有兴趣可以试试。其实有人已经实现了,看看 Extended STL string。如果你想偷懒,下载一个头文件就可以用,有了它确实方便了很多。要是有人能提供一个支持正则表达式的string,我会非常乐意用。

 

4 string 建议

使用string 的方便性就不用再说了,这里要重点强调的是string的安全性。

  1. string并不是万能的,如果你在一个大工程中需要频繁处理字符串,而且有可能是多线程,那么你一定要慎重(当然,在多线程下你使用任何STL容器都要慎重)。
  2. string的实现和效率并不一定是你想象的那样,如果你对大量的字符串操作,而且特别关心其效率,那么你有两个选择,首先,你可以看看你使用的STL版本中string实现的源码;另一选择是你自己写一个只提供你需要的功能的类。
  3. string的c_str()函数是用来得到C语言风格的字符串,其返回的指针不能修改其空间。而且在下一次使用时重新调用获得新的指针。
  4. string的data()函数返回的字符串指针不会以'/0'结束,千万不可忽视。
  5. 尽量去使用操作符,这样可以让程序更加易懂(特别是那些脚本程序员也可以看懂)

5 小结

难怪有人说:
string 使用方便功能强,我们一直用它!

 

6 附录

string 函数列表

函数名 描述
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:查找文件中的单词的出现的次数

#include  < iostream >
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;
}



 

 

 

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值