之所以抛弃char*
的字符串而选用C++
标准程序库中的string
类,是因为他和前者比较起来,不必 担心内存是否足够、字符串长度等等,而且作为一个类出现,他集成的操作函数足以完成我们大多数情况下(
甚至是100%)
的需要。我们可以用 =
进行赋值操作,==
进行比较,+
做串联(是不是很简单?
)。我们尽可以把它看成是C++
的基本数据类型。
好了,进入正题
………
首先,为了在我们的程序中使用string
类型,我们必须包含头文件 <string>
。如下:
#include <string> //
注意这里不是string.h string.h
是C
字符串头文件
1 .声明一个 C++ 字符串
声明一个字符串变量很简单:
string Str;
这样我们就声明了一个字符串变量,但既然是一个类,就有构造函数和析构函数。上面的声明没有传入参数,所以就直接使用了string
的默认的构造函数,这个函数所作的就是把Str
初始化为一个空字符串。String
类的构造函数和析构函数如下:
a) string s; //
生成一个空字符串
s
b) string s(str) //
拷贝构造函数 生成str
的复制品
c) string s(str,stridx) //
将字符串str
内“
始于位置stridx”
的部分当作字符串的初值
d) string s(str,stridx,strlen) //
将字符串str
内“
始于stridx
且长度顶多strlen”
的部分作为字符串的初值
e) string s(cstr) //
将C
字符串作为s
的初值
f) string s(chars,chars_len) //
将C
字符串前chars_len
个字符作为字符串s
的初值。
g) string s(num,c) //
生成一个字符串,包含num
个c
字符
h) string s(beg,end) //
以区间beg;end(
不包含end)
内的字符作为字符串s
的初值
i) s.~string() //
销毁所有字符,释放内存
都很简单,我就不解释了。
2 .字符串操作函数
这里是C++
字符串的重点,我先把各种操作函数罗列出来,不喜欢把所有函数都看完的人可以在这里找自己喜欢的函数,再到后面看他的详细解释。
a) =,assign() //
赋以新值
b) swap() //
交换两个字符串的内容
c) +=,append(),push_back() //
在尾部添加字符
d) insert() //
插入字符
e) erase() //
删除字符
f) clear() //
删除全部字符
g) replace() //
替换字符
h) + //
串联字符串
i) ==,!=,<,<=,>,>=,compare() //
比较字符串
j) size(),length() //
返回字符数量
k) max_size() //
返回字符的可能最大个数
l) empty() //
判断字符串是否为空
m) capacity() //
返回重新分配之前的字符容量
n) reserve() //
保留一定量内存以容纳一定数量的字符
o) [ ], at() //
存取单一字符
p) >>,getline() //
从stream
读取某值
q) << //
将谋值写入
stream
r) copy() //
将某值赋值为一个
C_string
s) c_str() //
将内容以C_string
返回
t) data() //
将内容以字符数组形式返回
u) substr() //
返回某个子字符串
v)
查找函数
w)begin() end() //
提供类似STL
的迭代器支持
x) rbegin() rend() //
逆向迭代器
y) get_allocator() //
返回配置器
下面详细介绍:
2 . 1 C ++ 字符串和 C 字符串的转换
C ++ 提供的由C++ 字符串得到对应的C_string 的方法是使用data() 、c_str() 和copy() ,其中,data() 以字符数组的形式返回字符串内容,但并不添加’/0’ 。c_str() 返回一个以‘/0’ 结尾的字符数组,而copy() 则把字符串的内容复制或写入既有的c_string 或 字符数组内。C++ 字符串并不以’/0’ 结尾。我的建议是在程序中能使用C++ 字符串就使用,除非万不得已不选用c_string 。由于只是简单介绍,详细介绍掠过,谁想进一步了解使用中的注意事项可以给我留言( 到我的收件箱) 。我详细解释。
2 . 2 大小和容量函数
一个C++ 字符 串存在三种大小:a) 现有的字符数,函数是size() 和length() ,他们等效。Empty() 用来检查字符串是否为空。b)max_size() 这个大小是指当前C++ 字符串最多能包含的字符数,很可能和机器本身的限制或者字符串所在位置连续内存的大小有关系。我们一般情况下不用关心他,应该大小足够我们用的。但是不够用的话,会抛出length_error 异常c)capacity() 重新分配内存之前 string 所能包含的最大字符数。这里另一个需要指出的是reserve() 函数,这个函数为string 重新分配内存。重新分配的大小由其参数决定, 默认参数为0 ,这时候会对string 进行非强制性缩减。
还有必要再重复一下C++
字符串和C
字符串转换的问 题,许多人会遇到这样的问题,自己做的程序要调用别人的函数、类什么的(比如数据库连接函数Connect(char*,char*)
),但别人的函数参 数用的是char*
形式的,而我们知道,c_str()
、data()
返回的字符数组由该字符串拥有,所以是一种const char*,
要想作为上面提及的函数的参数,还必须拷贝到一个char*,
而我们的原则是能不使用C
字符串就不使用。那么,这时候我们的处理方式是:如果 此函数对参数(
也就是char*)
的内容不修改的话,我们可以这样Connect((char*)UserID.c_str(), (char*)PassWD.c_str()),
但是这时候是存在危险的,因为这样转换后的字符串其实是可以修改的(有兴趣地可以自己试一试),所以我强调除非函数调用的时候不对参数进行修改,否则必须拷贝到一个char*
上去。当然,更稳妥的办法是无论什么情况都拷贝到一个char*
上去。同时我们也祈 祷现在仍然使用C
字符串进行编程的高手们(说他们是高手一点儿也不为过,也许在我们还穿开裆裤的时候他们就开始编程了,哈哈…
)写的函数都比较规范,那样 我们就不必进行强制转换了。
2
.
3
元素存取
我们可以使用下标操作符[]
和函数at()
对元素包含的字符进行访问。但是应该注意的是操作符[]
并不检查索引是否有效(有效索引0~str.length()
),如果索引失效,会引起未定义的行为。而at()
会检查,如果使用 at()
的时候索引无效,会抛出out_of_range
异常。
有一个例外不得不说,const string a;
的操作符[]
对索引值是a.length()
仍然有效,其返回值是’/0’
。其他的各种情况,a.length()
索引都是无效的。举例如下:
const string Cstr(“const string”);
string Str(“string”);
Str[3]; //ok
Str.at(3); //ok
Str[100]; //
未定义的行为
Str.at(100); //throw out_of_range
Str[Str.length()] //
未定义行为
Cstr[Cstr.length()] //
返回 ‘/0’
Str.at(Str.length());//throw out_of_range
Cstr.at(Cstr.length()) ////throw out_of_range
我不赞成类似于下面的引用或指针赋值:
char& r=s[2];
char* p= &s[3];
因为一旦发生重新分配,r,p
立即失效。避免的方法就是不使用。
2 . 4 比较函数
C ++
字符串支持常见的比较操作符(>,>=,<,<=,==,!=
),甚至支持string
与C-string
的比较(
如 str<”hello”)
。在使用>,>=,<,<=
这些操作符的时候是根据“
当前字符特性”
将字符按字典顺序进行逐一得 比较。字典排序靠前的字符小,比较的顺序是从前向后比较,遇到不相等的字符就按这个位置上的两个字符的比较结果确定两个字符串的大小。同时,string (“aaaa”) <string(aaaaa)
。
另一个功能强大的比较函数是成员函数compare()
。他支持多参数处理,支持用索引值和长度定位子串来进行比较。他返回一个整数来表示比较结果,返回值意义如下:0-
相等 〉0-
大于 <0-
小于。举例如下:
string s(“abcd”);
s.compare(“abcd”); //
返回0
s.compare(“dcba”); //
返回一个小于0
的值
s.compare(“ab”); //
返回大于0
的值
s.compare(s); //
相等
s.compare(0,2,s,2,2); //
用”ab”
和”cd”
进行比较 小于零
s.compare(1,2,”bcx”,2); //
用”bc”
和”bc”
比较。
怎么样?功能够全的吧!什么?还不能满足你的胃口?好吧,那等着,后面有更个性化的比较算法。先给个提示,使用的是STL
的比较算法。什么?对STL
一窍不通?靠,你重修吧!
2 . 5 更改内容
这在字符串的操作中占了很大一部分。
首先讲赋值,第一个赋值方法当然是使用操作符=
,新值可以是string(
如:s=ns)
、c_string(
如:s=”gaint”)
甚至单一字符(如:s=’j’
)。还可以使用成员函数assign()
,这个成员函数可以使你更灵活的对字符串赋值。还是举例说明吧:
s.assign(str); //
不说
s.assign(str,1,3);//
如果str
是”iamangel”
就是把”ama”
赋给字符串
s.assign(str,2,string::npos);//
把字符串str
从索引值2
开始到结尾赋给s
s.assign(“gaint”); //
不说
s.assign(“nico”,5);//
把’n’ ‘I’ ‘c’ ‘o’ ‘/0’
赋给字符串
s.assign(5,’x’);//
把五个x
赋给字符串
把字符串清空的方法有三个:s=””;s.clear();s.erase();(
我越来越觉得举例比说话让别人容易懂!)
。
string
提供了很多函数用于插入(insert
)、删除(erase
)、替换(replace
)、增加字符。
先说增加字符(这里说的增加是在尾巴上),函数有 +=
、append()
、push_back()
。举例如下:
s+=str;//
加个字符串
s+=”my name is jiayp”;//
加个C
字符串
s+=’a’;//
加个字符
s.append(str);
s.append(str,1,3);//
不解释了 同前面的函数参数assign
的解释
s.append(str,2,string::npos)//
不解释了
s.append(“my name is jiayp”);
s.append(“nico”,5);
s.append(5,’x’);
s.push_back(‘a’);//
这个函数只能增加单个字符 对STL
熟悉的理解起来很简单
也许你需要在string
中间的某个位置插入字符串,这时候你可以用insert()
函数,这个函数需要你指定一个安插位置的索引,被插入的字符串将放在这个索引的后面。
s.insert(0,”my name”);
s.insert(1,str);
这 种形式的insert()
函数不支持传入单个字符,这时的单个字符必须写成字符串形式(
让人恶心)
。既然你觉得恶心,那就不得不继续读下面一段话:为了插 入单个字符,insert()
函数提供了两个对插入单个字符操作的重载函数:insert(size_type index,size_type num,chart c)
和insert(iterator pos,size_type num,chart c)
。其中size_type
是无符号整数,iterator
是char*,
所以,你这么调用insert
函数是不行的:insert(0,1, ’j’);
这时候第一个参数将转换成哪一个呢?所以你必须这么写:insert((string::size_type)0,1,’j’)
!第二种形式指 出了使用迭代器安插字符的形式,在后面会提及。顺便提一下,string
有很多操作是使用STL
的迭代器的,他也尽量做得和STL
靠近。
删除函数erase()
的形式也有好几种(真烦!),替换函数replace()
也有好几个。举例吧:
string s=”il8n”;
s.replace(1,2,”nternationalizatio”);//
从索引1
开始的2
个替换成后面的C_string
s.erase(13);//
从索引13
开始往后全删除
s.erase(7,5);//
从索引7
开始往后删5
个
2 . 6 提取子串和字符串连接
题取子串的函数是:substr(),
形式如下:
s.substr();//
返回s
的全部内容
s.substr(11);//
从索引11
往后的子串
s.substr(5,6);//
从索引5
开始6
个字符
把两个字符串结合起来的函数是+
。(谁不明白请致电120
)
2 . 7 输入输出操作
1
.>>
从输入流读取一个string
。
2
.<<
把一个string
写入输出流。
另一个函数就是getline(),
他从输入流读取一行内容,直到遇到分行符或到了文件尾。
2 . 8 搜索与查找
查找函数很多,功能也很强大,包括了:
find()
rfind()
find_first_of()
find_last_of()
find_first_not_of()
find_last_not_of()
这些函数返回符合搜索条件的字符区间内的第一个字符的索引,没找到目标就返回npos
。所有的函数的参数说明如下:
第一个参数是被搜寻的对象。第二个参数(可有可无)指出string
内的搜寻起点索引,第三个参数(可有可无)指出搜寻的字符个数。比较简单,不多说不理解的可以向我提出,我再仔细的解答。当然,更加强大的STL
搜寻在后面会有提及。
最 后再说说npos
的含义,string::npos
的类型是string::size_type,
所以,一旦需要把一个索引与npos
相比,这个索引值必须是string::size)type
类型的,更多的情况下,我们可以直接把函数和npos
进行比较(如:if(s.find(“jia”)== string::npos)
)。
第二部分是关于C++
字符串对迭代器的支持的,视大家的需要我将写出来(意思就是不需要就算了,我乐得轻省,哈哈…
)。
好 了,大概的对string
类型进行了阐述,希望起到抛砖引玉的作用,让初学者对string
有个了解而不必已开始就面对复杂的内部结构和无数个注意事项。 对字符串更详细地讲解有很多参考书,其实我的内容也是从C++
标准程序库得来的,加上几句自己的看法,所以要感谢这本书的作者和译者。任何人对本文进行引 用都要标明作者是Nicolai M.Josuttis
译者是侯捷/
孟岩。不过不要提及我,任何观点的错误都与我无关(除了这里边体现我主观想法的几句话,也就那几句话)。