【C++】学习笔记五——C-风格字符串

C-风格字符串

字符串是存储在内存的连续字节中的一系列字符。C++处理字符串的方式有两种。第一种来自C语言,常被称为C-风格字符串(C-style string);另一种是基于string类库的方法。

本文介绍C-风格字符串。

存储在连续字节中的一系列字符意味着可以将字符串存储在char数组中,其中每个字符都位于自己的数组元素中。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数组,但只有第二个是字符串。空字符对C-风格字符串至关重要。C++有很多处理字符串的函数,包括cout使用的那些函数,它们都逐个地处理字符串中的字符,直到发现空字符为止。

如果使用cout显示上面的dog数组(它不是字符串),cout将打印出数组中的8个字母,并接着将内存中随后的各个字节解释为要打印的字符,直到遇到空字符为止。

在cat数组示例中,将数组初始化为字符串的方法要使用大量的单引号,且必须记住加上空字符,这种方法十分麻烦。有一种更好的将字符数组初始化为字符串的方法——只需使用一个用双引号阔气的字符串即可,这种字符串被称为字符串常量(string constant)或字符串字面值(string literal),如下所示:

char bird[11] = "Mr. Cheeps";                 //结尾自动包含\0
char fish[] = "Bubbles";                      //让编译器自动计数

用双引号括起的字符串隐式地包含结尾的空字符,各种C++输入工具输入字符串到char数组中时,将自动加上结尾的空字符。

应确保数组足够大,能够存储字符串中的所有字符——包括空字符。使用字符串常量初始化字符数组时,让编译器自动计算元素数目更为安全。

字符串常量(使用双引号)与字符常量(使用单引号)不能互换。
字符常量(如’S’)是字符串编码的简写表示,而”S”不是字符常量,它表示字符S和\0组成的字符串。而且,”S”实际上表示的是字符串所在的内存地址。

char shirt_size = 'S';               //合理,将83赋给shirt_size
char shirt_size = "S";               //类型不匹配,试图将一个内存地址赋给shirt_size                   

1.拼接字符串常量

有时候,字符串很长,无法放到一行中。C++允许拼接字符串常量,讲讲两个用双引号括起的字符串合并为一个。事实上,任何两个由空白(空格、tab和换行符)分隔的字符创常量都将自动拼接成一个。因此,西面所有的输出语句都是等效的:

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将被第二个字符串的第一个字符取代。

2.在数组中使用字符串

程序4.2

#include<iostream>
#include<cstring>           //使用strlen()函数
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 and is 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 charaters of my name: ";
    cout << name2 << endl;
    cin.get();
    cin.get();
    return 0;
}

运行结果:
这里写图片描述

sizeof()返回整个数组的长度:15字节;
strlen()返回存储在数组中的字符串的长度,而不是数组本身的长度。strlen()只计算可见的字符,而不把空字符计算在内。

程序4.2将name2[3]设置为空字符,使得该字符串在第三个字符后就结束。

3.字符串输入

程序4.2有一个缺陷,但该缺陷由于精心选择输入而被掩盖掉了。
程序4.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";
    cin.get();
    cin.get();
    return 0;
}

运行结果:
这里写图片描述

在输入完name(CSDN blog)后,还没有输入dessert,程序便将它显示出来了,并立即显示了最后一行。

cin如何确定字符串输入的完成呢?
由于不能用键盘输入空字符,因此cin使用空白(空格、制表符和换行符)来确定字符串的结束位置。

这意味着cin在获取字符数组输入时只读取一个单词,读取该单词后,cin将该字符串放到数组中,并自动在结尾添加空字符。

因此,程序4.3中,cin把CSDN作为第一个字符串,并将它放到name数组中,并自动在结尾添加空字符。这把blog留在了输入队列中,当cin在输入队列中搜索dessert时,发现了blog,因此cin读取blog并存在dessert中。

另一个问题是,输入字符串的长度可能比目标数组长。

4. 每次读取一行字符串输入

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

1. 面向行的输入:getline()

使用cin.getline()函数有两个参数,第一个参数是用来存储输入行的数组名称;第二个参数是要读取的字符数,如果这个参数为20,则最多读取19个字符,余下的空间用于存储在动在结尾处添加的空字符。getline()成员函数在读取指定数目的字符或遇到换行符时停止读取。

cin.getline(name,20);  //将输入读入到一个包含20个元素的name数组中

程序4.4

#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";
    cin.get();
    cin.get();
    return 0;
}

这里写图片描述

2. 面向行的输入:get()

cin.get()与getline()类似,它们的参数相同。但到行尾时,get()不丢弃换行符,而是将换行符保留在输入队列中。假设我们连续两次调用get():

cin.get(name, Arsize);
cin.get(dessert, Arsize);   //第二次调用看到的第一个字符便是换行符,因此get()认为已到达行尾,
                            //而没有发现任何可读取的内容.

get()有另一种变体,使用不带任何参数的cin.get()调用可读取下一个字符(即使是换行符),因此可以用它来处理换行符,为读取下一行做准备,如:

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

或者将两个类成员函数合并起来:

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

之所以可以这样做,是因为cin.get(name,Arsize)返回一个cin对象,该对象随后调用get()函数。同样,下面的语句将连续输入的两行分别读入到数组name1和name2中,其效果与两次调用cin.getline()相同:

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

程序4.5

#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";
    cin.get();
    cin.get();
    return 0;
}

3. 空行和其他问题

当get()读取空行后,将设置失效位(failbit),接下来的输入将被阻断,但可以用下面的命令来恢复输入:

cin.clear();

如果输入行包含的字符数比指定的多,则getline()和get()将把余下的字符留在输入队列中。getline()还会设置失效位,并关闭后面的输入。

5. 混合输入字符串和数字

混合输入数字和面向行的字符串会导致问题。
程序4.6

#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";
    cin.get();
    cin.get();
    cin.get();
    return 0;
}

运行结果:
这里写图片描述
用户根本没有输入address的机会。问题在于,当cin读取年份是,将回车键生成的换行符留在了输入队列中。后面的cin.getline()看到换行符后,将认为是一个空行,并将一个空字符串赋给address数组。
解决办法是,在读取地址之前先丢弃换行符。如:

cin>>year;
cin.get();            //or cin.get(ch);
(cin>>year).get();         //or (cin>>year).get(ch);

C++程序常用指针(而不是数组)处理字符串。

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值