C++字符串
(基于C++ Primer第五版整理)
C++常用的有两种形式的字符串:
- string是标准库定义的类类型,可以储存可变长的字符序列。
- char[]为字符数组形式的字符串,其中的每个元素都是char类型数据,以’
\0
'结尾,这种字符串是沿袭C语言的风格定义的,所以称为C风格字符串。
关于命名空间及其using声明的简单介绍
std::cout<<"HelloWorld";
在这个语句中,std::
表明cout是定义在名为std的命名空间(namespace)中的。其中::
是作用域运算符。
命名空间的作用是帮助我们避免不经意间的命名冲突,以及使用不同库中相同的名字导致的冲突。
标准库定义的所有名字都在命名空间std中。
如果在程序中加入了如下的using声明语句:
using namespace::name;
则可以直接访问命名空间中的名字,而不必在其前面加上namespace::
一、标准库类型string
标准库类型string表示可变长字符序列。使用string需要包含string头文件,string定义在命名空间std中。
#include <string>
using std::string;
(当然,你也可以直接声明using namespace std;
)
1. string对象的定义和初始化
string s1;
//默认初始化,s1是一个空字符串
string s2=s1; //赋值初始化
string s2(s1); //直接初始化
//等价操作,s2是s1的副本
string s3="hello"; //复制初始化
string s3("hello"); //直接初始化
//等价操作,s3是"hello"的副本,但是不包含最后的空字符
string s4(5,'h');
//s4是连续的5个字符组成的字符串,等价于string s4("hhhhh");
2. string上的操作
读写操作
os<<s //将s写到输出流os中,返回os
is>>s //从输入流is读取字符串赋值给s,返回is
读取时,string对象会自动忽略开始的空白,从第一个真正的字符开始读,知道下一个空白(空白指空格符,换行符,制表符等)。
例如以下程序:
string s1, s2;
cin >> s1 >> s2;
cout << s1 << s2 <<endl;
输入" Hello World “,输出"HelloWorld”。
连续读写:
while(cin>>str)
cout<<str<<endl;
如果想保留输入内容中的空白,可以用getline可以读取一整行。
getline的两个参数时输入流和string对象,getline读取知道遇到换行符为止:
getline(cin,str);
其他操作
s.empty()
//判断s是否为空,若为空,返回真,若不空,返回假
s.size()
//返回s中字符的个数
s1+s2
//返回s1和s2连接之后的结果
s1=s2
//用s2的副本代替s1中原有的字符
s1==s2
s1!=s2
//判断s1和s2包含的字符时候完全相同(大小写敏感)
s1>s2
s1<s2
s1>=s2
s1<=s2
//按字典顺序对s1和s2进行比较
特别说明:
字符串的连接(+)的两个operator可以有字符串字面值常量,但是必须至少有一个是string类型对象。
string s3=s1+s2; //正确,将两个string类型对象相加
string s3+=s1; //正确,等价于s3=s3+s1;
string s1=s2+"hello"; //正确,将一个string类型对象和一个字符串字面值常量相加
string s1=s2+"hello"+"world"; //正确,s2+"hello"返回修改过的s2对象,第二个+将新的s2和"world"连接
string s1="hello"+"world"+s2; //错误,第一个+两个操作对象均为字符串字面值常量
3. 处理string中的单个字符
第一种方法,可以使用下标运算符对string对象中的字符进行操作:
string对象字符的下表从0开始,计数到s.size()-1,s[n]返回对该位置上字符的引用(左值)。
string s="duck";
s[0]='f';
cout<<s;
//修改s的第一个字符,输出fuck
这里简要介绍一下左值和右值的区别:本质上讲,右值是简简单单一个数值,而左值是一个对象。例如int型变量a=5。如果是右值,那么就是一个数字5,处理数值和数据类型没有别的含义;如果作为左值,那么代表了a所在的位置,即在内存中的空间,是一个在内存中真实存在的对象。
第二种方法,范围for循环。
string s="hello";
for(char &i:s) //对于s中的每一个字符...
{
i=toupper(i); //将i转化为大写,这个函数包含在头文件cctype中
}
cout<<s<<endl;
二、C风格字符串
字符串字面值即是一种常见的C风格字符串。这种结构是从C语言继承而来的。
C风格字符串是char类型的数组,在操作上和一般的数组类型基本一致,将char类型数据存放于字符数组中并以空字符结尾(’\0’)。
数组名是指向数组第一个元素的地址的const型指针。
C风格字符串不仅使用起来不方便,而且带来了很多安全问题,极易引发程序漏洞,在C++程序中最好不要使用它们。
1. 初始化
char s1[]="hello";
//s1含有6个字符:hello中的5个字母和结尾自动加上的'\0'
一些不推荐或错误的用法:
char s[3]="abc"; //没有空间储存'\0'
char s[]={'a','b','c'}; //同上
char s1[]=s2; //错误,不能使用一个数组初始化另一个数组
s1=s2; //错误,不能使用一个数组直接给另一个数组赋值
2. 库函数
库函数 | 作用 |
---|---|
strlen§ | 返回p的长度,不包含结尾的空字符 |
strcmp(p1, p2) | 比较p1和p2的相等性。如果p1和p2相等,则返回0;如果p1>p2,则返回一个正值;如果p1<p2,则返回一个负值。 |
strcat(p1, p2) | 将p2附加到p1之后,返回p1 |
strcpy(p1, p2) | 将p2拷贝给p1,返回p1 |
注:"==", “!=” , “<”, “>”,"<=", ">="这些比较运算符对字符数组型字符串是没有意义的。
字符数组是不可变长的,因此使用strcat, strcpy这些函数时,应该注意字符数组的长度,否则容易导致溢出。Lippman评价:“这类代码充满了风险而且经常导致严重的安全泄露”。
附加:新旧代码的接口
为了是现代的C++程序和充满了数组和C风格字符串的代码衔接,C++提供了专门的一组功能。
我们介绍过用字符串字面值来初始化string对象:
string s("Hello");
更一般的情况是:任何出现字符串字面值的地方都可以用空字符结尾的字符数组来代替:
- 允许使用以空字符结束的字符数组来初始化string对象或为string对象赋值
- 在string对象的加法运算中允许使用以空字符结尾的字符数组作为其中的一个运算对象;在string对象的复合赋值运算中允许使用以空字符结束的字符数组作为右侧的运算对象。
但是上述结论反过来就不成立了,我们不能用string对象代替字符数组。为了完成该功能,string专门提供了一个名为c_str的成员函数:
char *str = s; //错误
const char *str =s.c_str(); //正确
c_str函数返回的结果是一个C风格字符串,也就是一个指针,指向一个以空字符结尾的字符数组,这个数组保存的内容恰好与string对象一样,指针的类型是const str*,保证我们不会改变字符数组的内容。
使用数组初始化vector:略…
int int_arr[]={1,2,3,4,5};
vector<int> ivec(begin(int_arr), end(int_arr));