字符串
字符串是存储在内存的连续字节中的一系列字符,C++处理字符串有两种方式:
一、来自C语言的C-风格字符串(C-style string);
二、string 类库;

一、C-风格字符串(C-style string)
存储在连续字节中的字符意味着可以将字符串保存在char数组中,其中每个字符都位于自己的数组元素中。C-风格字符串(c-string)具有一种特殊的性质:以空字符(null)结尾,空字符被写作’\\0’,其ASCII码为0,用来标记字符串的结尾。例如,下面的声明:
char ex1[3] ={‘c’,’b’,b’};
char ex2[3] ={‘c’,’b’,’\\0’};
char ex3[3]=”cb”
char ex4[]=”you and i”
这两个都是char 数组 (字符数组),但只有第二个是字符串(C-string)。

1.1 初始化与输出:空字符特性对字符串使用极其重要,首先,C-string 存储在字符数组中并且其一律默认以空字符结尾,所以,当使用字符串常量(由双引号包含,其隐式的包含了末尾的空字符)对一个字符数组进行初始化时,要注意字符串的真正长度是显式长度+1,因此,当这个真正长度大于字符数组长度时,(idel)将认定出错(上面ex3)。 当然可以以另一种方式初始化字符数组(上面的ex4),这种数组的初始化方法在类型变为int等的时候将出现错误(未指明数组大小),但对char类型有效。因为字符串的存储方式为字符数组,对其进行输出时将与字符数组的输出方式相同:
以字符串名称 (也是相应字符数组第一个单元的地址)为地址索引,将之后的每个内存单元中的内容作为char类型数据(字符数据)进行输出,直到遇到空字符。
使用这种方式可以输出一个字符串变量或字符数组,(因为二者在内存空间上是同一种存储方式,因此输出也具有相同的方式),要注意如果该部分内存空间上是字符串时,因为其存储空间末尾位置固定出现空字符,因此可以实现字符串变量的完整输出。但当存储的只是普通的一组字符时,(其中未必有空字符出现),输出索引将一直在内存空间上向后延续,将每个字节解释为要打印的字符,直到遇到空字符为止。
另外,由双引号包括的字符串常量的值(例如:”schoolhahh”)实际上是该字符串所在的地址,(故而可以使用这种方式对字符数组进行赋值)。 但是要与其他数组的输出区分:

int list1[5]={1,2,3,4,5}
char list2[5]=”abs”
cout <<list1: 43FE8A02
cout <<list2: abs

1.2 拼接字符串: C++允许拼接字符串,即将两个用引号括起的字符串合并为一个,事实上,任何两个由空白(空格、制表符和换行符)分隔的字符串常量都将自动拼接为一个。注意:拼接时前字符串最后的’\\0’会被后字符串的第一个字符取代,意味着二者之间不会添加任何间隙。

1.3 在数组中使用字符串:将字符串存储到数组中常用两种方法:一,将数组初始化为字符串常量;二,将键盘或文件输入读入到数组中。下面的程序演示了这两个方法:
char name[5];
char nn[5]=”stay”;
cin >> name; :you and i
cout << endl << nn; :stay
cout << endl << name; :you // 在这里忽视了输入的空格 (1)
cout << sizeof(name) : 5 //返回整个数组的长度
cout <<strlen(name) :3 //将存储在数组中的内容视为一个字符串,返回其长度(忽视末尾的空字符)
前面的(1)式说明在使用键盘或文件对字符数组进行初始化时,cin对象会以空格、制表符和换行符作为字符串的边界,当cin读到这些“边界”时,会默认字符串结束,将已经读取的部分存入数组,且自动在末尾添加空字符。因此,采用这种方式对字符数组进行初始化时,不会有字符数组越界的检查:
char name[5];
cin >> name; :absolutelyyou qwer
cout <<name: :absolutelyyou //持续输出直到空字符
1.4 字符串输入:如上所述, 当利用数组实现字符串的存储时存储着最主要的两个问题:一、字符串边界的鉴定;二、字符数组大小与字符串大小可能存在着冲突。
针对这两个问题分别讨论两种初始化方法:

(1)在程序中直接初始化:
char nn[10] = “SBD sdc”;
char nn[10]=“SBD” \" sdc\";
char nn[10] = “SBD”
\" sdc\";
cout << nn;
根据字符串拼接原理可知,这三种初始化都正确且将得到相同的结果,这种初始化方法将直接对字符串长度进行检查,当字符串的真正长度大于数组长度时,编译器会报错:
char nn[5]=”abcd”; ✔
char nn[5]=”abc” “d”; ✔
char nn[5]=”abcde”; ✖
char nn[5]=”abc” “de” ✖
(2) 通过键盘或文件输入进行初始化:
char nn[5];
cin >>nn; :abcdefgh
如前所述,使用这种方法初始化时,cin对象将一直读取直到出现空格、换行或制表符,并且自动添加空字符为结尾,不存在对字符串大小和数组大小的比较。这种情况下,输出数组中的第i个元素时,只要有效的字符串(strlen())大于等于i,就能输出,而不是根据数组大小(sizeof())来判断:
char nn[5];
cin >> nn; : abcdefgh ijkl
cout << nn << endl; :abcdefgh
cout << nn[7]<<endl; :h
cout << nn[8]<<endl; :
cout << nn[9]<<endl; :
cout << strlen(nn)<<endl; :8
cout << sizeof(nn); :5
由于这种初始化方式对空格、换行和制表符的敏感性,使用时可能产生如下问题:
char name[10];
char like[10];
cout << \"Your name: \" << endl;;
cin >> name; :John Snow
cout << “what do you like:”<<endl;
cin >> like; :将跳过该阶段
cout << \"You are \" << name << \" and you like \" << like;
最后的输出为:You are John and you like Snow.
这种特性由输入流的特性决定。为了解决这个问题,可以使用istream类提供的面向行的类成员函数:getline()和get().这两个函数都将读取一行输入。
(1) 面向行的输入getline(name,num):它的两个参数分别为存储输入行的数组名和要读取的最大存储长度。通过换行符来确定行尾,但不保存换行符,相反,在存储字符时,它用空字符来替换换行符。当输入长度超过最大读取长度num时,将自动停止,但不会认为发生了行的改变。
(2) 面向行的输入 get(name,num): 其参数调用与gerline()相同,也是通过换行符判断行尾,但是,get()并不读取换行符,而是将其留在输入队列中。因此在连续使用get()时要注意到换行符的存在,可以使用不带任何参数的get()固定读取下一个字符,来处理这个换行符。

注:getline() 和get()都能实现一行的输入,但是要注意到如果连续两次调用这两个函数,并且前一个因输入达到最大长度限制而非换行符存在而结束,二者在后续的读取上完全不同。
注:考虑到getline()和get()都是istream类的cin对象的函数,其返回值是调用它的对象(即cin),因此,可以对其进行联用:
cin.getline(name,num).getline(name,num);
cin.get(name,num).get();
注:使用get(name,num)要是得输入更为灵活仔细,因为可以通过使用get()来检查当前读取结束的下一个字符是不是换行符,因此可以判断当前get(name,num)的结束是由于行的结束还是最大长度的限制。

二,string类简介
ISO/ANSI C++标准通过添加string类扩展了C++库,因此现在可以使用string 类型的变量而不是字符数组来存储字符串。string类使用起来更简单,而且提供了一种将字符串作为一种数据类型的表示方法。
要使用string类,必须在程序中包含头文件string,string类位于名称空间std中,因此必须提供一条using编译语句或者通过std::string来引用它。string类的定义隐藏了字符串的数组性质,使得我们可以像处理变量一样处理它。因为在这里,string作为一种像int或char那样的简单变量类型存在,而不是数组这样的复合类型,这样可以省去数组大小的限制。事实上,string类的设计让程序能够自动处理string的大小,当只对string对象进行声明时,会创建一个长度为0的string对象,但程序将输入读到该对象时,将自动调整该对象的长度。
在很多方面,使用string 类对象的方式与使用字符数组的方式相同:
(1) 可以使用C-string来初始化string类对象;
string a=”abc def g”
(2) 可以使用键盘或文件输入存储到string类对象中;
string a;
cin>>a;
(3)可以使用cout来输出string对象;
(4) 可以使用数组表示法来访问存储在string对象中的字符;
string a=”abcd”;
cout<<string[3];
2.1 string 类的操作
赋值:使用string类时,某些操作比使用数组更加简单。例如直接将一个string对象的值赋给另外一个string对象,这在数组中是不被允许的。
拼接和附加:可以使用操作符+直接将两个对象合并起来,还可以使用操作符+=将一个string对象附加到另一个string对象的末尾:
str3=str1+str2;
str1+=str2;

头文件cstring中: strcpy(s1,s2) 复制字符串 s2 到字符串 s1
strcat(s1,s2) 连接字符串 s2 到字符串 s1 的末尾
strlen(s1) 返回字符串 s1 的长度
strcmp(s1,s2) 返回s1与s2的比较结果
strchr(s1,ch) 返回一个指针,指向字符串s1中字符ch的第一次出现的位置
strstr(s1,s2) 返回一个指针,指向字符串s1中s2的第一次出现的位置

函数size()或length()功能与strlen()相同,但这二者是string类的方法,应该以对象调用的形式使用。
string类的输入与输出:可以使用cin和运算符>>以及cout和运算符<<实现对string类进行存储与输出(需要包含头文件string),其句法与C-风格字符串相同(同样需要注意到空格、换行和制表符的存在),但是当读取一行输入时,使用的句法有所不同:

string 类型的出现在istream类出现之后,因此,istream的方法与string类对象的结合是以友元函数的形式实现的,这意味着在用istream的getline()方法应用于string对象时,cin对象应作为参数传递给getline():
char a[];
string b;
cin.getline(a,10);
get(cin,b)

收藏 打印