C++中的字符串

"本文介绍了C++中字符串的处理方法,包括C风格字符串和string类库。C风格字符串以空字符''结尾,字符串常量在内存中隐含包含空字符。C++允许字符串常量的拼接,并提供了getline()和get()函数来处理整行的字符串输入。对于混合输入字符串和数字,需要注意换行符的影响,可能需要使用get()来丢弃换行符。"
摘要由CSDN通过智能技术生成

        

目录

3.2.1 拼接字符串常量

3.2.2 在数组中使用字符串

3.2.3 字符串输入

3.2.4 每次读取一行字符串输入

3.2.5 混合输入字符串和数字


        字符串是存储在内存的连续字节中的一系列字符。C++处理字符串的方式有两种。第一种来自C语言,常被称为C-风格字符串;另一种是基于string类库的方法。
        存储在连续字节中的一系列字符意味着可以将字符串存储在char数组中,其中每个字符都位于自己的数组元素中。字符串提供了一种便捷存储文本信息的便捷方式。
        C风格字符串具有一种特殊的性质:以空字符结尾,空字符被写作\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数组,但是只有第二个数组是字符串。空字符对于C风格字符串而言至关重要。例如,C++中有很多处理字符串的函数,其中包含cout使用的那些函数。他们都逐个地处理字符串中的字符,直到到达空字符为止。如果使用cout显示上面cat这样的字符串,则将显示前7个字符,发现空字符后停止。
        但是如果用cout显示上面的dog数组(他不是字符串),cout将打印出数组中的8个字母,并接着将内存中随后的各个字节解释为要打印的字符,直到遇到空字符为止。由于空字符(实际上使被设置为0的字节)在内存中很常见,因此这一过程将很快停止。
        但尽管如此,还是不应将不是字符串的字符数组当作字符串来处理。
        在cat数组实例中,将数组初始化为字符串的工作看上去冗长无味————使用大量单引号,但必须加上空字符。其实有一种更好的,将字符数组初始化为字符串的方法————只需使用一个用引号括起来的字符串即可,这种字符串被称为字符串常量或字符串字面值,如下所示:

char bird[11] = "Mr.Cheeps";
char fish[] = "Bubbles";

        用引号括起来的字符串隐式地包含结尾的空字符,因此不用显式地包括他。另外各种C++输入工具通过键盘输入,将字符串输入到char数组中时,将自动加上结尾的空字符。
        当然,应确保数组足够大,能够存储字符串中的所有字符————包括空字符。使用字符串常量初始化字符数组是这样的一种情况,即让编译器计算元素数目更加安全。让数组比字符串长没什么坏处,只是会浪费一些空间而已。这是因为处理字符串的函数会根据空字符的位置,而不是数组长度来处理。C++对字符串长度没有限制。
        警告:在确定存储字符串所需的最短数组时,别忘了将结尾的空字符计算在内。
        注意:字符串常量(使用双引号)不能与字符常量(使用单引号)互换。字符常量(如'S')是字符串编码的简写表示。在ASCII上,'S'只是83的另一种写法,因此,下面的语句将83赋给shirt_size:

char shirt_size = 'S';

        但"S"不是字符常量,他表示的是两个字符(字符S和字符\0)组成的字符串。更糟糕的是,"S"表示的其实是字符串所在的内存地址。因此下面的语句尝试将一个内存地址赋给shirt_size:

char short_size = "S";

        由于地址在C++中是一种独立的类型,因此C++编译器不允许这种不合理的做法。

3.2.1 拼接字符串常量

        C++允许拼接字符串字面值,即将两个用引号括起来的字符串合并为1个。事实上,任何两个由空白(空格、制表符和换行符)换行的字符串常量都将拼接成一个。
因此,下面所有的输出语句都是等效的:

cout<<"I'd give my right arm to be" " a great violinist.\n";    //由空格
cout<<"I'd give my right arm to be a great violinist.\n";
cout<<"I'd give my right ar"
    "m to be a great violinist.\n"; //由换行

        注意:拼接时不会在被拼接的字符串之间添加空格,第二个字符串的第一个字符将紧紧跟在第一个字符串的最后一个字符(不考虑\0)的后面。第一个字符串中的\0字符将被第二个字符串的第一个字符所取代。

3.2.2 在数组中使用字符串

        要将字符串=存储到数组中,最常见的方法有两种————将数组初始化为字符串常量,将键盘或文件输入读取到数组中。
下面是使用上述两种方法的代码:

#include<iostream>
#include<cstring>

int main() {
	using namespace std;
	const int Size = 15;
	char name1[Size];
	char name2[Size] = "C++owboy";

	cout << "Howdy! I'm " << name2;
	cout << "! What's your name?\n";
	cin >> name1;
	cout << "Well, " << name1 << ", your name has ";
	cout << strlen(name1) << " letters has stored\n";
	cout << "in an array of " << sizeof(name1) << " bytes.\n";
	cout << "Your initial is " << name1[0] << ".\n";
	name2[3] = '\0';
	cout << "Here are the first 3 characters of my name: ";
	cout << name2 << endl;
	return 0;

}

        首先,sizeof运算符指出整个数组的长度:15字节,但strlen()函数返回的是存储在数组中的字符串的长度,而不是数组本身的长度。另外strlen()只计算可见的字符,而不把空字符计算在内。假如cin接收到的字符串是Basicman,那么其返回值是8,而不是9.如果cosmic是字符串,则要存储该字符串,数组的长度不能短于strlen(cosmic)+1。
        由于name1和name2是数组,因此可以用索引来访问数组中的各个字符。例如,该程序使用name1[0]找到数组中的第一个字符。另外,该程序将name2[3]设置为空字符。这使得字符串在第3个字符后即结束,虽然数组中还有其他的字符。

3.2.3 字符串输入

#include<iostream>

int main() {
	using namespace std;
	const int ArSize = 20;
	char name[ArSize];
	char dessert[ArSize];
	cout << "Enter your name.\n";
	cin >> name;
	cout << "Enter your favorite dessert:\n";
	cin >> dessert;
	cout << "I have some delicious " << dessert;
	cout << " for you, " << name << ".\n";
	return 0;
}

        我们没有对“输入甜点的提示”做出反应,程序便把他显示出来了,然后立即显示最后一行。cin通过使用空白(空格、制表符和换行符)来确定字符串的结束位置,这意味着cin在获取字符数组输入时只读取一个单词。读取该单词以后,cin将该字符串放到数组中,并自动在结尾添加空字符。
        这个例子的实际结果是,cin把Alistair作为第一个字符串,并把他放到了name数组中,这把Dreeb留在输入队列中。当cin在输入队列中搜索用户喜欢的甜点时,他发现了Dreeb,并将他放到dessert数组中。

3.2.4 每次读取一行字符串输入

        每次读取一个单词通常不是最好的选择,加入程序要求用户输入城市名,用户加入想输入New York,我们希望程序读取并存储完整的城市名,而不仅仅是New。
        要将整个短语而不是一个单词作为字符串输入,需要采用另一种字符串读取方法。具体地说,需要采用面向行而不是面向单词的方法。幸运的是,istream中的类(如cin)提供了一些面向行的类成员函数,:getline()与get()。这两个函数都读取一行输入,直到到达换行符。
        然而,随后getline()将丢弃换行符,而get()将换行符保留在输入序列中,下面介绍他们:
1、面向行的输入:getline()
        getline()函数读取整行,它使用通过回车键输入的换行符来确定输入结尾。要调用这种方法,可以使用cin.getline()。
        该函数有两个参数。第一个参数是用来存储输入行的数组的名称,第二个参数是要读取的字符数。如果这个参数为20,则函数最多读取19个字符,余下的空间用于存储自动在结尾处添加的空字符。
        getline()成员函数在读取指定数目的字符或遇到换行符时停止读取。例如,假设要使用getline()将姓名读入到一个包含20个元素的name数组中。可以使用这样的函数调用:

cin.getline(name,20);

        这将把一行读入到name数组中————如果这行包含的字符不超过19个。
        下面对上面的程序进行了一些修改,将输入改为了cin.get(),而不是简单的cin:

#include<iostream>

int main() {
	using namespace std;
	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;
	cout << " for you, " << name << ".\n";
	return 0;
}

        该程序现在可以读取完整的姓名以及用户喜欢的甜点!getline()函数每次读取一行。它通过换行符来确定行尾,但不保存换行符。相反,在存储字符串时,它用空字符来替换换行符。
2、面向行的输入:get()
        istream类有另一个名为get()的成员函数,该函数有几种变体。其中一种变体的工作方式与getline()类似,它们接受的参数相同,解释参数的方式也相同,并且都读取到行尾。但get并不再读取并丢弃换行符,而是将其留在输入队列中。假设我们连续两次调用get():

cin.get(name,ArSize);
cin.get(dessert,ArSize);    //a problem

        由于第一次调用后,换行符将留在输入队列中,因此第二次调用时看到的第一个字符便是换行符。因此get()认为已到达行尾,而没有发现任何可读取的内容。如果不借助于帮助,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()函数。同样,下面的语句将把输入中连续的两行分别读入到数组name1和数组name2中,其效果与两次调用cin.getline()相同:

cin.getline(name1,ArSize).getline(name2,ArSize);

下面采取了拼接的方式:

#include<iostream>

int main() {
    using namespace std;
    const int ArSize = 20;
    char name[ArSize];
    char dessert[ArSize];
    cout << "Enter your name.\n";
    cin.get(name, ArSize).get();
    cout << "Enter your favorite dessert:\n";
    cin.get(dessert, ArSize).get();
    cout << "I have some delicious " << dessert;
    cout << " for you, " << name << ".\n";
    return 0;
}

        需要指出一点是,C++允许函数有多个版本,条件是这些版本的参数列表不同。如果使用的是cin.get(name,ArSize),则编译器知道是将一个字符串放入数组中,因而将使用适当的成员函数。如果使用的是cin.get(),则编译器知道是要读取一个字符。

3.2.5 混合输入字符串和数字

#include<iostream>

int main() {
    using namespace std;
    cout << "What year was your house built?\n";
    int year;
    cin >> year;
    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";
    return 0;
}

运行上面的代码,运行结果如下:

What year was your house built?
1966
What is its street address?
Year built: 1966
Address:
Done!

D:\Programme\VisualStudio\data\Project2\x64\Debug\Project2.exe (进程 10764)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

        用户根本没有输入地址的机会。问题在于,当cin读取年份,将回车键生成的换行符留在了输入队列之中。后面的cin.getline(address,80)看到换行符后,将认为是一个空行,并将一个空字符串赋给adress数组。
        解决问题的关键是,在读取地址之前先读取并丢弃换行符。这可以通过几种方法来完成,这其中包括使用没有参数的get()和接受使用一个char参数的get()。
        如前面的例子所示,可以单独进行调用:

cin>>year;
cin.get();  //或者cin.get(ch);

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

(cin>>year).get();  //或者(cin>>year).get(ch);

        按上述任何一种方法修改程序以后,他便可以正常工作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值