存储在连续字节中的一系列字符意味着可以将字符串存储在char数组中。
要将字符串存储到数组中,常用的方法有两种:
- 将数组初始化为字符串常量
- 将键盘或文件输入读入到数组中
1 数组字符串的初始化、赋值和使用
初始化数组字符串有两种方法:
- 像初始化普通数组一样,使用大量单引号给出每个字符,且必须包含空字符作为结尾。
- 只需是使用一个引号括起所有的字符串即可,即使用字符串常量或字符串字面值(隐式地包含了结尾的空字符)。
数组初始化需要注意:
- 如果某些系统初始化数组时必须加static关键字,则初始化char数组时也必须使用该关键字。
- 应确保数组足够大,能够存储字符串中所有字符,包括空字符。让数组比字符串长出了浪费空间没有什么害处。
- 处理字符串只是根据空字符的位置,而不是数组的长度。
- C++对字符串长度没有限制。
- 字符串常量和字符常量不能互换。字符常量'S'是字符编码的简写,而"S"不是字符常量,它是'S'和'\0'两个字符组成的字符串串。"S"实际上表示的是字符串所在的内存地址。
- 地址在C++中是一种独立的类型,C++编译器不允许把一个内存地址赋给char变量。
char shirt = 'S';
char skirt = "S"; //非法
要想知道字符串的有效长度,可用的方法有:sizeof、strlen()和size()以及length()。
- sizeof运算符指出整个数组的长度。
- strlen()函数返回的是存储在数组中的字符串长度,而不是数组本身的长度。 该函数的头文件为cstring(老式为string.h)。
- strlen()函数只计算可见的字符,不把空字符计算在内。数组shirt要存储字符串seri,数组的长度不能短于strlen(seri)+1。
- str.length()和str.size()用于求string类对象str的成员函数,前者沿用C,后者兼容STL,均返回字节数。不用于数组字符串。
C-风格字符串的特殊性质:以空字符'\0'结尾(ASCII码为0),用来标记字符串的结尾。
- cout打印输出时会连续打印字符,知道遇到空字符为止。
- 由于空字符(值设为0)在内存中很常见,因此打印过程很容易很快终止。不应将不是字符串的字符数组当做字符串处理。
字符串很长而无法放到一行时,C++允许拼接字符串字面值,将两个引号括起来的字符串合并为一个。
- 任何两个由空白(空格、制表符、换行符)分隔的字符串常量都将自动拼接成一个。
- 拼接时不会再被连接的两个字符串之间添加空格,第二个字符串的第一个字符将紧跟咋第一个字符串的最后一个字符(不考虑\0)后面,第一个字符串中的\0字符将被第二个字符串的第一个字符取代。
cout << "I am a student coming from XXX university.\n";
cout << "I am a student coming from" " XXX university.\n";
cout << "I am a student coming from"" XXX university.\n";
2 数组字符串的输入
用cin处理字符串输入存在的问题:
- 不能通过键盘输入空字符:cin使用空白(空格、制表符、换行符)确定字符串的结尾位置,即只能读取一个单词,并在结尾自动添加空字符。
- 输入字符串可能比目标数组长。
读取一行字符串输入时,需要采用面向行而不是面向单词的输入方法:getline()和get()。
2.1 getline()函数
getline()函数是istream类对象cin的成员函数,它读取整行,在读取指定数目的字符或分界符(默认为换行符)时停止读取。
调用格式一:cin.getline(name, length);
调用格式二:cin.getline(name, length,charEnd);
- 第一个参数是用来存储输入行的数组的名称。
- 第二个参数是要读取的字符数(包括分界符),即为要输入字符数加一。
- 第三个参数可选,指定用作分界符的字符,默认为换行符'\n'。
使用cin.getline(name, length)函数每读一行,通过换行符来确定结尾,但不保存换行符,在存储字符串时用空字符替换换行符。
int ArSize = 20;
char name1[ArSize], name2[ArSize];cin >> name1; //只能输入一个单词
cin.getline(name2, ArSize);cout << "name1: " << name1 << endl;
cout << "name2: " << name2 << endl; //可以输出完整字符串行
2.2 get()函数
get()函数也是istream类对象cin的成员函数,它读取整行,有几种变体,其中一种和getline()函数类似。
调用格式一:cin.get(name, length);
调用格式二:cin.get(name, length, charEnd);
调用格式三:cin.get(ch); //读取一个字符ch
调用格式四:cin.get(); //不带任何参数,读取一个字符
- get()的前两种格式和getline()的工作方式类似,接受的参数相同,解释参数的方式也相同,并且都读取到行尾(分界符)。
- get()并不读取并丢弃换行符(分界符),仍将其保留在输入队列中。
- 为跨过换行符,调用cin.get()可读取下一个字符,即使是换行符(分界符)。因此,它用来处理换行,为读下一行做准备。
使用get()(无参数)有两种方式。
- 直接单独调用cin.get()。
- 将两个类成员函数拼接起来(合并)。因为get()函数返回一个cin对象,故可继续调用get()函数。getline()函数也可合并。
int ArSize = 20;
char name1[ArSize], name2[ArSize];cin.get(name1, ArSize);
cin.get(name2, ArSize); //读取出问题//有效方案1
cin.get(name1, ArSize);
cin.get();
cin.get(name2, ArSize);//有效方案2
cin.get(name1, ArSize).get();
cin.get(name2, ArSize);//有效方案3
cin.getline(name1, ArSize);
cin.getline(name2, ArSize);//有效方案4
cin.getline(name1, ArSize).getline(name2, ArSize);
类成员函数get()和getline()的区别:
- get()将换行符(分界符)留在输入流中,getline()则将其丢弃并替换为空字符。
- 有些老式实现没有getline()成员函数。
- get()使得输入更仔细,可以用于查看下一个输入字符。如是换行符(分界符),则说明读完一行;如不是,数组已填满,该行还有其他输入。
- getline()使用起来更简单一些,但get()使得错误检查更简单些。
2.3 空行及相关问题
如何处理空行问题?
- 最初的做法是,下一条输出语句将在前一条getline()或get()结束读取的位置开始读取。
- 目前,get()读取空行后将设置失效位(failbit)。这意味着接下来的输入将被阻断,但可以用cin.clear()命令恢复输入。
输入字符串比分配的空间长怎么办?
- getline()和get()会把余下的字符留在输入队列中。
- getline()还会设置,并关闭后面的输入。
混合输入数字和面向行的字符串会导致问题:当cin读取数字后,会将回车键生成的换行符留在输入队列中。此后使用getline()会读入换行符,将认为是一个空行。解决办法在于先读取并丢弃换行符。
- 使用没有参数的get()。
- 使用接受一个char参数的get()。
- 利用cin返回的cin对象再次调用get()。
#include <iostream>
using namespace std;int main() {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;
}//解决办法
//方案一:
cin >> year;
cin.get();//方案二:
char ch;
cin >> year;
cin.get(ch);//方案三:
(cin >> year).get();//方案四:
char ch;
(cin >> year).get(ch);
3 使用指针处理字符串
C++常使用指针而不是数组来处理字符串。
具体请参考: