前言:
!!!阅读前最好先看前言和目录
通过阅读这篇文章,你可以学会什么:
- cin.get()和get()它们的区别是什么?
- 各种输入方法,它们输入终止的条件是什么?是遇到空格,还是换行符?
- 如何一次性从键盘读取多行字符?
- cin.eof(),cin.fail()等等,这些是什么,它们的意义是什么?
一.cin
流对象和与输入有关的全局函数
输入流的概念
首先,我们介绍一个概念:输入流
输入流(Input Stream)是计算机编程中用于从某个源获取数据的概念。输入流可以是文件、键盘、网络连接等。在C++中,输入流通常用于从标准输入设备(例如键盘)或文件中读取数据。
C++标准库提供了 istream
类,istream
支持从不同源读取数据的方法,并提供了一些用于处理输入的函数。cin
是 istream
类的一个实例,cin流下的对象用于从标准输入设备(键盘)读取数据。而包括get,getline在内的全局函数,也具备从输入流中读取字符的功能。
因此,在c++中,从键盘中得到输入,最常见的两种方法就是使用cin
流对象和与输入有关的全局函数。下面是它们一些主要的区别:
二者的区别
1.类型:
- 输入流(如 cin): 属于输入流类 (istream),是面向对象的。cin 是 istream 类的一个实例,提供了一些丰富的成员函数,可以用于不同类型的输入操作。
- 全局函数(如 get): 这些函数是全局函数,通常较为简单,专注于读取字符。
2.方便性和易用性:
- 输入流: cin 提供了方便且易用的输入方式,可以直接使用 >> 运算符进行输入。它会根据变量的类型进行格式化的输入。
- 全局函数: 使用全局函数可能需要更多的手动处理,特别是在需要对输入进行更精细的控制时。例如,处理换行符、空格等。
3.输入控制:
- 输入流: cin 有一些内建的机制,可以处理空格、制表符,并会自动跳过它们。它也能够自动转换输入为适当的类型。
- 全局函数: 全局函数如 get 可以更灵活地处理输入,但也需要程序员显式地控制读取的内容。
4.错误处理:
- 输入流: cin 可以触发流状态位,从而进行错误处理。例如,如果期望读取一个整数,但输入了非数字字符,cin 的状态位将被设置,允许你进行相应的错误处理。
- 全局函数: 一些全局函数可能不提供像 cin 那样的丰富的错误处理机制。
综合来说,使用 cin
是更常见和更简单的方式,适用于大多数一般的输入需求。全局函数提供了更底层的控制,适用于一些特殊的输入场景,但通常会显得更冗长和手动。选择使用哪种方式取决于你的具体需求和编程风格。
二.cin
流对象
`cin` 是 C++ 标准库中的标准输入流对象,属于 `istream` 类的实例。除了 `cin`,`istream` 类还有一些其他常用的函数和对象,用于从输入流中读取数据。以下是其中一些:
cin的基本用法
在使用cin
时,它默认使用空格作为分隔符,因此如果你需要输入包含空格的字符串,可能需要采用不同的方法。以下是一些关于字符输入的说明:
读取单个字符:
char ch;
cin >> ch;
读取字符串:
string str;
cin >> str; // 读取到空格为止
cin
流对象的基本用法
1. cin.get() 函数:
- get 函数用于从输入流中读取一个字符。
- 函数声明:int get();(
get
函数返回一个整数,它可以是读取的字符的 ASCII 值(0 到 255)或者特殊值EOF
) - 在一些编译器中,cin.get()与cin.getline() 的功能基本相同,只是不将截止标志储存到字符串中
char ch;
ch = cin.get(); // 读取单个字符
2. cin.getline() 函数:
- getline 函数用于从输入流中读取一行字符串,包括换行符,并将其存储到字符串中。
- 函数声明:istream& getline(char* s, streamsize n, char delim = '\n');(streamsize表示字符的数量,delim是截止符的意思)
- getline默认的截止符是 '\n',即换行符,可以在第三个形式参数中为它设置截止符
- !!!值得一提的是,如果使用
string line;
cin.get(line,100)
这种读取方法是错误的,因为参数形式不一致,应该使用getline函数直接进行读取,后面会提到。
char buffer[256];
cin.getline(buffer, 256); // 读取一整行,可以包含空格
3. cin.ignore() 函数:
- ignore函数用于忽略输入流中的字符,可以指定要忽略的字符数或者遇到的分隔符。
- 函数声明:`istream& ignore(streamsize n = 1, int delim = EOF);
- 如果在
ignore
达到指定的总字符数之前遇到了指定的分隔符(或者达到文件末尾),ignore
会提前停止忽略字符,并将流状态标志设置为eofbit
(如果是到达文件末尾)或failbit
(如果是遇到指定的分隔符)。(流状态的设置会在后面说明)
cin.ignore(100, '\n');
// 忽略输入流中的前100个字符或者直到遇到换行符
4. cin.peek() 函数:
- peek 函数用于查看输入流中的下一个字符,但不从流中提取它。
- 函数声明:int peek();
char nextChar = cin.peek(); // 查看下一个字符,但不提取
5. cin.putback() 函数:
- putback函数用于将一个字符放回输入流中,使其成为下一个被读取的字符。
- 函数声明:istream& putback(char c);
cin.putback('A'); // 将字符 'A' 放回输入流中
三.与输入有关的全局函数
在C++中,有一些全局函数可以用于从输入流中读取字符。以下是其中一些常见的:
1. get:
- get 函数用于从输入流中读取一个字符,并返回其 ASCII 值。
- 可以使用 `get()` 读取一个字符。
- 可以指定一个分隔符,get(delimiter) 会读取并丢弃一个字符。
- !!!get是一个古老的函数,在如codeblocks的编译器中并不支持以下的代码
char ch;
ch = get(); // 读取单个字符
char delimiter;
get(delimiter); // 读取并丢弃一个字符,可以用于处理分隔符
2. getline:
- getline 函数用于从输入流中读取一行string类字符串,直到遇到换行符,并不将换行符存储到变量中。
- 可以用于读取整行,包括空格。
string line;
getline(cin,line);
注意:
char line[256]; getline(cin, line);
这种用法是存在问题的。getline
函数的第二个参数应该是 std::string
类型的对象,而不是字符数组。getline
函数用于从输入流中读取一行字符串,并将其存储到字符串对象中,而不是字符数组。
如果你想使用字符数组来存储输入的一行字符串,你可以使用 cin.getline
函数
3. getchar:
- getchar 函数用于从输入流中读取一个字符,并返回其 ASCII 值。
char ch = getchar(); // 读取单个字符
4. gets:
- gets 函数用于从输入流中读取一行字符串,直到遇到换行符。
- 注意:gets 已经被标记为不安全,不推荐在现代C++中使用,因为它无法防止缓冲区溢出。
这些全局函数提供了一些不同的方式来处理输入,你可以根据具体的需求选择合适的函数。在现代C++中,更推荐使用 `getline` 和 `cin` 对象来进行输入操作,因为它们提供了更安全和更灵活的机制。
关于合法性的问题,这些函数在现代 C++ 中仍然是有效的,但在一些特定的编译器或环境中,可能会有一些差异。如果在 Code::Blocks 或其他 IDE 中出现问题,可以检查编译器的设置和标准库的配置。通常情况下,这些函数应该是可用的。
四.如何读取多行的字符串
在输入的读取过程中,有时候我们需要读取包括空格,换行符在内的符号,通过前面的讲解,我们知道了使用cin.getline(),getlint(cin, )可以读取包括空格在内的多行变量,但遇到换行符'\0'这样的空白符,还是会停止读取。那如何一次性读取多行字符呢?
首先,一个比较自然的想法是用cin.getline( , ,'\0'),如果将空字符作为输入的截止字符,那就可以接受所有输入的字符了,但这样也存在一些问题,在codeblocks这样的编译器中,没有特殊设置,可能是不支持用Ctrl+z等方法强制停止输入的,这样就会导致代码运行时只能一直输入。实际上,通过函数本身来做到多行输入的读取是很难做到的。所以,我们应该寻找其他方法来做到多行输入的读取,这个方法,就是循环的使用。
1.利用循环和数组读取多行字符串
#include <iostream>
using namespace std;
int main() {
const int maxLines = 100; // 假设最多读取100行
const int maxLineLength = 256; // 假设每行最多256个字符
char ch[maxLines][maxLineLength];
int lineCount = 0;
// 逐行读取文本
while (lineCount < maxLines && cin.getline(ch[lineCount], maxLineLength) && ch[lineCount][0] != '\0') {
lineCount++;
}
return 0;
}
这是一个用二维数组读取最多100行,每行最多256个字符的代码 。当读取到空白行时,停止读取。实际上,遇到第一个空白行后停止,就是通常情况下我们用于读取多行字符串的方法。
但有时候,二维数组的使用,可能会使我们对字符串的处理变得麻烦,那有没有办法使用一维数组解决呢。如果是自己编译的过程中,我们可以通过条件设置来控制输入的截止,如果在给定的测试中,方法就比较多样了。例如,我们可以用cin.eof()作为条件判断的标志,如以下代码:
#include <iostream>
#include <string>
using namespace std;
int main() {
const int maxSize = 10000;
char ch[maxSize];
int i = 0;
while (i < maxSize - 1 && cin.get(ch[i])) {
// 检查是否已到达文件末尾
if (ch[i] == EOF) {
break;
}
i++;
}
// 添加 null 结尾标记
ch[i] = '\0';
// 输出字符数组
cout << ch;
return 0;
}
当然,这样使用是无法再我们自己编译时手动终止输入的。在后面调用时,我们可以用
for(int i=0;ch[i]!='\0';i++)来调用
2.利用循环和string类
#include <iostream>
#include <string>
using namespace std;
int main() {
string totalline,line;
while (true) {
// 读取一行字符,包括换行符
getline(cin, line);
// 如果输入为空行,退出循环
if (line.empty()) {
break;
}
totalline+=line+'\n';//实现分行读取后合并
}
cout<<totalline;
return 0;
}
除此之外,我们还可以用vector,stringstream处理多行文本,这些属于后面的知识,暂时不补充了。
五.流状态的设置
在C++中,流(stream)是一种用于输入和输出的抽象概念。流状态是描述流的当前状态的一组标志,用于指示流的可用性和发生的事件。C++标准库提供了一些函数和标志,以便在程序中检查和处理流的状态。
以下是一些常见的流状态和相关的标志:
1. goodbit:
- goodbit 是流状态的默认初始状态。
- 当流状态正常,没有错误发生时,`goodbit` 被设置。
- 通过 good()成员函数来检查流是否处于正常状态。
2. eofbit:
- eofbit 表示流已经到达文件末尾(End of File)。
- 当读取操作尝试读取文件末尾之后的数据时,`eofbit` 被设置。
- 通过 eof() 成员函数来检查流是否已到达文件末尾。cin.eof()与cin.get()==EOF这两种写法在检查是否达到结尾上是相似的,但不完全等价。使用
cin.eof()
更多地关注流的状态,而cin.get() == EOF
更侧重于具体的字符读取和 EOF 常量的比较。
3. failbit:
- failbit 表示一个非致命的错误,可能会导致流不再可用。
- 例如,当试图读取一个整数,但输入的数据不是整数时,failbit 被设置。还比如,提前达到了上面cin.ignore()的截止符。
- 通过 fail() 成员函数来检查是否发生了非致命的错误。
4. badbit:
- badbit 表示致命的错误,导致流不再可用。
- 例如,当底层设备发生故障时,`badbit` 被设置。
- 通过 bad() 成员函数来检查是否发生了致命的错误。
这些标志可以通过成员函数 `clear()` 来清除,并可以通过 `setstate()` 来设置。
具体的使用方法:
#include <iostream>
using namespace std;
int main() {
int value;
cout << "Enter an integer: ";
// 尝试读取输入
cin >> value;
if (cin.good()) {
// 输入正常,没有错误
cout << "You entered: " << value << std::endl;
} else if (cin.eof()) {
// 到达文件末尾
cout << "Reached end of file." << std::endl;
} else if (cin.fail()) {
// 非致命错误,例如输入不是整数
cerr << "Invalid input. Please enter an integer." << endl;
cin.clear(); // 清除错误标志
cin.ignore(1, '\n'); // 忽略当前字符
cin>>value;
} else if (cin.bad()) {
// 致命错误,例如底层设备故障
cerr << "Fatal error occurred." << endl;
return 1; // 退出程序或采取适当的错误处理措施
}
return 0;
}
编译器检测输入,并设置相应的的流状态。而通过cin.good()等方式,我们可以对当前的流状态进行检测,如果输入正常,cin.good()返回true,以此类推。
流状态在C++中的作用主要是提供一种机制来检测和处理输入/输出操作中的异常情况,例如文件末尾、无效输入等。通过检查流的状态,你可以更好地控制程序的行为,避免程序因为输入错误而崩溃或产生不可预测的结果。根据流的状态,你可以动态地调整程序的行为。例如,如果输入包含非法字符,你可能会选择提示用户重新输入,而不是继续使用无效的输入。
总体而言,流状态提供了一种机制,让你能够更灵活地处理输入和输出,以及更好地应对各种潜在的错误情况。这有助于提高程序的可靠性。