字符串简介:
字符串是存储在内存中连续字节的一系列字符。C++中处理字符串有两种风格,一种来自C语言,叫做C语言风格;另一种风格基于string库,我们姑且叫string库风格。其中C语言风格,利用了字符串储存在内存中连续字节的特点,将字符串储存在字符数组中,以‘\0’结尾;而string库风格提供的方法,允许程序员将字符串作为变量来使用,接下来将分别介绍这两种风格。
1.C语言风格
(1)字符串和字符数组
C语言风格的处理方式是将字符串置于字符数组中,每个字符位于一个数组元素之中,并且以空字符(’\0’)结尾。以空字符结尾对C语言风格的字符串来说非常重要,这是区别字符串和字符数组的标志,比如:
char a[5] = {'1','2','3','4','5'}; //这是一个字符数组 char b[5] = {'1','2','3','4','\0'
}; //这是一个字符串
同时,字符串结尾的空字符也是很多函数判断是否到达字符串结尾的标志,如果将字符串中的某个元素设置为’\0’,那么很多函数会错误的判断字符串的结尾位置并带来错误,比如:
char dog[6] = {'T','e','d','d','y','\0'}; //定义一个字符串Teddy cout<<"改变前: "<<dog<<
"\n"; //cout会输出直到遇到末尾的空字符为止 dog[2] = '\0'; //将字符'd'改为空字符 cout<<"改变后: "<<dog;
这样一个代码段执行结束以后的结果将是:
如果用户不将字符数组结尾设置为空字符,cout函数将继续将内存后面的内容解释为要打印的元素直到遇到空字符为止,但是由于空字符在内存中很常见,所以输出也将很快停止,这大多不会影响输出,但是用户仍然不应该将非空字符结尾的字符串当作字符串使用。
(2)字符串的初始化
字符串的初始化可以刚刚的范例中的那样做,使用单引号分隔单个字符并在末尾加上空字符,但这样不免繁琐。更简洁的将字符数组初始化为字符串的方式是用双引号("")将内容括起。这种字符串叫做字符串常量,或者字符串字面值。用引号括起的字符串隐式包含了结尾的空字符,不需要人为添加,但应记得为其预留位置。在这样输入字符串的时候,我们可以手动设置字符数组长度,也可以让编译器自动计算空间,比如:
char dog[6] = "Teddy" //人为设置,预留一个空字符的位置 char cat[] = "Persion" //编译器计算
需要注意的是,应当为字符串分配足够大的空间,如果分配的空间存在没用完的部分,将自动被设置为空字符’\0’
(3)字符串的拼接以及对字符串中字符操作
在C语言风格中,算术运算符’+‘不会被解释为字符串的拼接,所以不能直接使用’+'拼接两个字符串。可以使用函数strcat()达到目的,但是需要使用到string库,所以放在下面进行讲解。
在明白字符串和字符数组的关系后,就可以利用数组的性质对字符串中的字符进行操作,比如实现一个单词的大小写转化:
char dog[6] = "Teddy"; for (int i=0;i<6;i++){ //如果未知长度,可以使用strlen(dog)获取 if(dog
[i]<'Z' && dog[i]>'A'){ dog[i] += 32; } else{ dog[i] -= 32;
(4)字符串输入
和字符数组一样,字符串可以使用cin进行输入。但是cin使用空白(空格,换行符,制表符)确定输入边界,这意味着输入的字符串中不能含有空格(一次性只能输入一个单词),这样的问题是很明显的。
但实际上,如果空格和换行符在确认边界上有同样的效果,那么是不是意味着在运行程序时也会出现同样的结果呢?比如下面的一段代码:
char name[10]; char food[10]; cout<<"What is your name:"; cin>>name; cout<<
"What is your favorite food:"; cin>>food; cout<<name<<" likes "<<food;
如果使用换行符,即使用回车来分隔两个字符串,运行范例是:
看起来似乎没什么问题,但如果使用空格来分割呢?
在这种情况下,程序员尚未对输入最喜欢的食物做出反应,程序已经将其显示了出来。这是因为使用空格以后,rice被留在了输入队列中,故当程序在输入队列搜索输入结果时发现了留在其中的rice,直接读取存入字符串中,这样也就不需要用户再输入了。
首先,一次性只能输入一个单词肯定是不好的,但是istream中的类提供了一些类成员函数,可以面向行进行输入,即是说仅把换行符(按下enter)作为输入结尾标志。这样的函数有getline()和get()两种。
区别在于getline()将会读取换行符并舍弃,而get()将保留换行符在输入队列中。这两种函数的一般形式为:cin.getline(arrayName,arrayLenth)和cin.get(arrayName,arrayLenth);代表将输入放到arrayLenth长度的arrayName的数组中去。这样就能解决使用空格作为输入边界带来的一些问题了,比如:
char name[15]; cout<<"input name:"; cin.getline(name,15); cout<<name;
测试范例为:
可以看到此时就可以输入含有空格的字符串了!
但是我们需要注意,由于get()函数将回车留在了队列中,可能需要另一个get()来读取回车,否则会给输入带来问题,比如:
char pets[10]; char food[10]; cin.get(pets,10); cin.get();
//抵消留在输入队列中的字符串,也可以使用getchar() cin.get(food,10); cout<<pets<<"\n"; cout<<food;
2.string库风格(加入cstring或者string.h)
通过添加string类扩展了C++库,现在就可以使用string类型的变量而不是字符数组储存字符串。现在可以这样定义字符串了:
string pets = "dog and cat";
(1)赋值,拼接和附加
string first_name; string last_name; first_name = "Mercer"; //给字符串变量赋值
last_name= "Alex"; cout<<last_name+" ,"+first_name; //此处的"+"解释为字符串拼接
(2)和字符数组的联系
实际上string类定义隐藏了字符串的数组性质,实际上string和字符数组存在很多相同,也存在一些不同,下面以一个测试程序为例:
string first_name = "Alex"; //可以用C风格字符串初始化字符串对象 string last_name; cin>>
last_name; //可以使用cin从键盘读取到string对象中 cout<<"full_name: "<<first_name+last_name<<
"\n";//可以用cout输出 cout<<first_name[0]; //可以使用数组表示法输出字符串中的字符
(3)string的输入
string类可以使用cin和>>进行输入,但是和C语言风格一样,面对空格可能带来的问题。但是之前的C语言风格中,我们使用getline(),get()时,需要确定字符数组的名称和长度,但是string类隐去了数组性质,又如何输入呢?可以使用函数getline(cin,str)来进行输入,例如:
string full_name; getline(cin,full_name);
(4)一些常用函数
①strcpy --将字符串复制到字符数组中
strcpy(a,b),将数组b复制到a中,会覆盖原来a中的内容;
char a[4] = "123"; char b[4] = "456"; cout<<strcpy(a,b);
结果为
②strcat --将字符串附加到字符串末尾
strcat(a,b),将数组b附加到a末尾,同样是刚刚的例中
char a[4] = "123"; char b[4] = "456"; cout<<strcat(a,b);
结果为:
③strlen --输出字符串长度
char a[4] = "123"; cout<<strlen(a);
结果为:
这里需要注意,字符数组长度为4,而输出的长度为3,这是因为预留的一个元素位置是为引号括起的内容隐式添加的’\0’留的,而strlen()函数遇到空字符停止,不会将其计入字符串长度,这里也侧面反应了上面我们对C语言风格字符串初始化的观点。