字符串
C风格字符串
C-风格字符串具有 一种特殊的性质:以空字符(null character)结尾,空字符被写作\0, 其ASCII码为0,用来标记字符串的结尾。例如,
char dog[8] = {'b', 'e', 'a', 'u', 'x', ' ', 'i', 'i'}; // 不是字符串
char cat[8] = {'f', 'a', 't', 'e', 's', 's', 'a', '\0'}; // 是字符串
更好的初始画字符串的方法:使用字符串常量
char bird[11] = "Mr. Cheeps"; // \0 会自动隐式添加,不必显式添加
char fish[] = "bubbles"; // 让编译器计算字符串长度
警告
在确定存储字符串所需的最短数组时,别忘了将结尾的空字符计算在内。
使用
#include<iostream>
#include<cstring> // 为了使用strlen()函数
using namespace std;
int main() {
char cat[20] = {'a', 'b', '\0'};
cout << strlen(cat) << endl; // 输出2,strlen()只计算可见字符,而不把空字符计算在内
cout << sizeof(cat) << endl; // 输出20
char dog[20] = "abc";
cout << strlen(dog) << endl; // 输出3
cout << sizeof(dog) << endl; // 输出20
return 0;
}
从上述程序可以学到什么呢?
首先,sizeof运算符指出整个数组的长度:20字节,但 strlen( )函数返回的是存储在数组中的字符串的长度,而不是数组本身的长度
其次, strlen( ) 只计算可见的字符,而不把空字符计算在内。
第三,如果cosmic是字符串,则要存储该字符串,数组的长度不能短于strlen(cosmic)+1。
字符串输入
#include<iostream>
using namespace std;
int main() {
const int len = 20;
char name[len];
char food[len];
cout << "Enter your name:" << endl;
cin >> name;
cout << "Enter your favourite food:" << endl;
cin >> food;
cout << "I have some food " << food;
cout << " for you, " << name << endl;
return 0;
}
该程序的意图很简单:读取来自键盘的用户名和用户喜欢的甜点,
然后显示这些信息。下面是该程序的运行情况,
Enter your name:
Jack apple
Enter your favourite food:
I have some food apple for you, Jack
Process exited with status 0
我们甚至还没有对“输入甜点的提示”作出反应,程序便把它显示出 来了,然后立即显示最后一行。
cin是如何确定已完成字符串输入呢?由于不能通过键盘输入空字 符,因此cin需要用别的方法来确定字符串的结尾位置。cin使用空白 (空格、制表符和换行符)来确定字符串的结束位置,这意味着cin在 获取字符数组输入时只读取一个单词。读取该单词后,cin将该字符串 放到数组中,并自动在结尾添加空字符。
另一个问题是,输入字符串可能比目标数组长(运行中没有揭示出 来)。像这个例子一样使用cin,确实不能防止将包含30个字符的字符 串放到20个字符的数组中的情况发生。
每次读取一行字符串输入
istream中的类(如cin)提 供了一些面向行的类成员函数:getline( )和get( )。这两个函数都读取一 行输入,直到到达换行符。然而,随后getline( )将丢弃换行符,然后用空字符来替换换行符。而get( )将换行符保留在输入序列中。
- 面向行的输入:getline( )
getline( )函数读取整行,它使用通过回车键输入的换行符来确定输 入结尾。要调用这种方法,可以使用cin.getline( )。该函数有两个参数。 第一个参数是用来存储输入行的数组的名称,第二个参数是要读取的字 符数。如果这个参数为20,则函数最多读取19个字符,余下的空间用于 存储自动在结尾处添加的空字符。getline( )成员函数在读取指定数目的 字符或遇到换行符时停止读取。
例如,假设要使用getline( )将姓名读入到一个包含20个元素的name 数组中。可以使用这样的函数调用:
cin.getline(name, 20); // 这将把一行读入到name数组中—如果这行包含的字符不超过19个。
- .面向行的输入:get( )
我们来试试另一种方法。istream类有另一个名为get( )的成员函数, 该函数有几种变体。其中一种变体的工作方式与getline( )类似,它们接 受的参数相同,解释参数的方式也相同,并且都读取到行尾。但get并 不再读取并丢弃换行符,而是将其留在输入队列中。假设我们连续两次 调用get( ):
cin.get(name, 10);
cin.get(name2, 10);
由于第一次调用后,换行符将留在输入队列中,因此第二次调用时 看到的第一个字符便是换行符。因此get( )认为已到达行尾,而没有发现 任何可读取的内容。如果不借助于帮助,get( )将不能跨过该换行符。
幸运的是,get( )有另一种变体。使用不带任何参数的cin.get( )调用 可读取下一个字符(即使是换行符),因此可以用它来处理换行符,为 读取下一行输入做好准备。也就是说,可以采用下面的调用序列:
cin.get(name, 10);
cin.get();
cin.get(name2, 10);
另一种使用get( )的方式是将两个类成员函数拼接起来(合并),如 下所示:
cin.get(name, 10).get();
为什么要使用get( ),而不是getline( )呢?首先,老式实现没有 getline( )。其次,get( )使输入更仔细。例如,假设用get( )将一行读入数 组中。如何知道停止读取的原因是由于已经读取了整行,而不是由于数 组已填满呢?
混合输入字符串和数字
#include<iostream>
using namespace std;
int main() {
int n;
cin >> n;
char address[10];
cin.getline(address, 10);
return 0;
}
用户根本没有输入地址的机会。问题在于,当cin读取年份,将回 车键生成的换行符留在了输入队列中。后面的cin.getline( )看到换行符 后,将认为是一个空行,并将一个空字符串赋给address数组。解决之道 是,在读取地址之前先读取并丢弃换行符。这可以通过几种方法来完 成,其中包括使用没有参数的get( )和使用接受一个char参数的get( ),如 前面的例子所示。可以单独进行调用:
#include<iostream>
using namespace std;
int main() {
int n;
cin >> n;
cin.get(); // or cin.get(ch)
char address[10];
cin.getline(address, 10);
return 0;
}
基于string类库的字符串
要使用string类,必须在程序中包含头文件string。string类位于名称 空间std中,因此您必须提供一条using编译指令,或者使用std::string来 引用它。
在很多方面,使用string对象的方式与使用字符 数组相同。
- 可以使用C-风格字符串来初始化string对象。
- 可以使用cin来将键盘输入存储到string对象中。
- 可以使用cout来显示string对象。
- 可以使用数组表示法来访问存储在string对象中的字符
赋值、拼接和附加
使用string类时,某些操作比使用数组时更简单。例如,不能将一 个数组赋给另一个数组,但可以将一个string对象赋给另一个string对 象
可以将C-风格字符串或string对象与 string对象相加,或将它们附加到string对象的末尾。
string类的其他操作
在C++新增string类之前,程序员也需要完成诸如给字符串赋值等工 作。对于C-风格字符串,程序员使用C语言库中的函数来完成这些任 务。头文件cstring(以前为string.h)提供了这些函数。例如,可以使用 函数strcpy( )将字符串复制到字符数组中,使用函数strcat( )将字符串附 加到字符数组末尾:
strcpy(arr1, arr2); // copy arr2 to arr1
strcat(arr1, arr2); // append arr2 to arr1
比较
#include<iostream>
#include<string> // 使string类库可用
#include<cstring> // C-style string函数库可用
int main() {
char arr1[10];
char arr2[10] ="abc";
string s1;
string s2 = "abc";
s1 = s2;
strcpy(arr1, arr2);
s1 += s2;
strcat(arr1, arr2);
int len1 = s1.size();
int len2 = strlen(arr1);
return 0;
}
string类I/O
可以使用cin和运算符<<来将输入存储到string对象 中,使用cout和运算符<<来显示string对象,其句法与处理C-风格字符串 相同。但每次读取一行而不是一个单词时,使用的句法不同
#include<iostream>
#include<string> // 使string类库可用
#include<cstring> // C-style string函数库可用
int main() {
char arr[20];
string s;
cin.getline(arr, 20);
getline(cin, s);
return 0;
}