【C++ Primer Plus学习记录】字符串——C-style string

字符串是存储在内存的连续字节中的一系列字符。

C++处理字符串的方式有两种:

1.来自C语言,常被称为C-风格字符串(C-style string)

特殊的性质:以空字符结尾,空字符被写作\0,其ASCII码为0,用来标记字符串的结尾。

例如:

char dog[8] = {'b','e','a','u','x','','I','I'};     //not a string
char cat[8] = {'f','a','t','e','s','s','a','\0'};   //a string

上述两个数组都是char数组,但是只有第二个数组是字符串。

在cat数组示例中,将数组初始化为字符串的工作看上去冗长乏味——使用大量单引号,且必须记住加上空字符。有一种更好的、将字符数组初始化为字符串的方法——只需使用一个用引号括起来的字符串即可,这种字符串被称为字符串常量(string constant)或字符串面值(string literal),如下所示:

char bird[11] = "Mr.Cheeps";  //the \0 is understood
char fish[] = "Bubbles";      //let the compiler count 

 用引号括起的字符串隐式地包括结尾的空字符,因此不用显式地包括它。

注意:在确定存储字符串所需的最短数组时,别忘了将结尾的空字符计算在内。

2.基于string类库的方法

存储在连续字节中的一系列字符意味着可以将字符串存储在char数组中,其中每个字符都位于自己的数组元素中。

在数组中使用字符串

要将字符串存储到数组中,最常用的方法有两种:

1.将数组初始化为字符串常量

2.将键盘或文件输入读入到数组中

程序清单4.2演示了这两种方法

//将一个数组初始化为用引号括起的字符串
//并使用cin将一个输入字符串放到另一个数组中
//使用标准C语言库函数strlen()来确定字符串的长度,标准头文件cstring提供了该函数以及很多与字符串相关的其他函数的声明
#include<iostream>
using namespace std;

int main()
{
	const int Size = 15;
	char name1[Size];    //empty array
	char name2[Size] = "C++owboy"; //initialized array

	cout << "Howdy! I'm " << name2;
	cout << "! What's your name?\n";
	cin >> name1;
	//使用标准C语言库函数strlen()来确定字符串的长度,标准头文件cstring提供了该函数以及很多与字符串相关的其他函数的声明
	//strlen()函数返回的是存储在数组中的字符串的长度,而不是数组本身的长度。
	//它只计算可见的字符,不把空字符计算在内
	cout << "Well, " << name1 << " ,your name has " << strlen(name1) << " letters and is stored\n";
	//sizeof运算符返回类型或数据对象的长度(单位为字节)
	//如果将sizeof运算符用于数组名,得到的将是整个数组中的字节数。
	//但如果将sizeof用于数组元素,则得到的将是元素的长度(单位为字节)
	cout << "in an array of " << sizeof(name1) << " bytes.\n";
	cout << "your initial is " << name1[0] << endl;
	name2[3] = '\0';//重新赋值
	cout << "Here are the first 3 characters of my name:";
	cout << name2 << endl;
	system("pause");
	return 0;
}

上述代码可能存在的问题是,输入字符串可能比目标数组长,像上述例子一样使用cin,确实不能防止将包含30个字符的字符串放到20个字符的数组中的情况发生。

每次读取一行字符串输入

要将整条短语而不是一个单词作为字符串输入,需要采用另一种字符串读取方法。即,需要采用面向行而不是面向单词的方法。istream中的类提供了一些面向行的类成员函数:getline()和get()。这两个函数都读取一行输入,直到到达换行符。但是,getline()将丢弃换行符,而get()将换行符保留在输入序列中。

1.getline()

getline()函数读取整行,它使用通过回车键输入的换行符来确定输入结尾。使用cin.getline()调用这种方法。该函数有两个参数,第一个参数是用来存储输入行的数组的名称,第二个参数是要读取的字符数。如果这个参数为20,则函数最多读取19个字符,余下的空间用于存储自动在结尾处添加的空字符。

getline()函数每次读取一行。它通过换行符来确定行尾,但不保存换行符。在存储字符串时,它用空字符替换换行符。

#include<iostream>
using namespace std;

int main()
{
	const int ArSize = 20;
	char name[ArSize];
	char dessert[ArSize];

	cout << "Enter your name:\n";
	cin.getline(name, ArSize);
	cout << "Enter your favorite dessert:\n";
	cin.getline(dessert, ArSize);
	cout << "I have some delicious " << dessert << " for you, " << name << endl;
	system("pause");

	return 0;
}

2.get()

istream类有另一个名为get()的成员函数,该函数有几种变体,其中一种变体的工作方式与getline()类似,它们接受的参数相同,解释参数的方式也相同,并且都读取到行尾。但get()并不再读取并丢弃换行符,而是将其留在输入队列中,假设我们连续两次调用get():

cin.get(name,ArSize);
cin.get(dessert,ArSize);

由于第一次调用后,换行符将留在输入队列中,因此第二次调用时看到的第一个字符便是换行符,因此get()认为已到达行尾,而没有发现任何可读取的内容。如果不借助于帮助,get()将不能跨过该换行符。

使用不带任何参数的cin.get()调用可读取下一个字符(即使是换行符),因此可以用来处理换行符,为读取下一行输入做好准备。即,可以采用下面的调用序列:

cin.get(name,ArSize);    //read first line
cin.get();               //read newline
cin.get(dessert,ArSize); //read second line

另一种使用get()的方式是将两个类成员函数拼接起来(合并),如下所示:

cin.get(name,ArSize).get();

可以这样做的原因,是由于cin.get(name,ArSize)返回一个cin对象,该对象随后将被用来调用get()函数。

//程序清单4.5采用了拼接方式
#include<iostream>
using namespace std;

int main()
{
	const int ArSize = 20;
	char name[ArSize];//空数组
	char dessert[ArSize];//空数组

	cout << "Enter your name:\n";
	cin.get(name, ArSize).get();//read string,newline
	//cin.get(name, ArSize);
	cout << "Enter your favorite dessert:\n";
	cin.get(dessert, ArSize).get();
	cout << "I have some delicious " << dessert << "for you, " << name << endl;
	//cin.get(dessert, ArSize);
	system("pause");

	return 0;
}

getline()使用起来简单一些,但是get()使得检查错误更简单些。

3.空行或其他问题

当getline()或get()读取空行时,当get()(不是getline())读取空行后将设置失效位。这意味着接下来的输入将被阻断,但可以用下面的命令来恢复输入:

cin.clear();

另一个潜在的问题是,输入字符串可能比分配的空间长。如果输入行包含的字符数比指定的多,则getline()和get()将把余下的字符留在输入队列中,而getline()还会设置失效位,并关闭后面的输入。

混合输入字符串和数字

混合输入数字和面向行的字符串会导致问题。如下:

//following number input with line input
#include<iostream>
using namespace std;

int main()
{
	cout << "What year was your house built?\n";
	int year;
	cin >> year;
	//cin.get();//解决办法

	cout << "What is its street address?\n";
	char address[80];
	cin.getline(address, 80);//行输入

	cout << "Year built: " << year << endl;
	cout << "Address: " << address << endl;
	cout << "Done!\n";

	system("pause");

	return 0;
}

用户根本没有输入地址的机会。当cin读取年份,将回车键生成的换行符留在了输入队列中。后面的cin.getline()看到换行符后,将认为是一个空行,并将一个空字符串赋给address数组。解决的办法是,在读取地址之前先读取并丢弃换行符。可以通过以下几种方法完成:

1.使用没有参数的get()和使用接受一个char参数的get()

cin >> year;
cin.get();//使用不带任何参数的cin.get()调用可读取下一个字符(即使是换行符),因此可以用来处理换行符,为读取下一行输入做好准备。

2.可以利用表达式cin >> year返回cin对象,将调用拼接起来:

(cin >> year).get();

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值