C++ 输入流

面试中笔试常会遇ACM模式(需要写主函数、输入输出)。这里总结一下,方便后续查看。

1. cin 1.1 operator>> 1.2 get() 1.3 getline() 1.4 cin 的条件状态


1. cin

   <iostream>Input Output Stream 的缩写,是标准的输入、输出流库。定义了标准的输入、输出对象。
   Including <iostream> 还会自动包含<ios><streambuf><istream><ostream><iosfwd>

  Note: iostream 类主要声明在头文件 <istream>中。

   输入流std::cin 是 istream 类的对象,它主要面向窄字符(narrow characters (of type char))的标准输入流。

   输出流std::cout 是 ostream 类的对象,它主要面向窄字符(narrow characters (of type char))的标准输出流。

   在理解 cin 功能时,不得不提标准输入缓冲区。当我们从键盘输入数据的时候,需要敲一下回车键才能够将这个数据 送入到缓冲区 中,那么敲入的这个回车键(\r)会被转换为一个换行符’\n’,这个换行符也会被存储在 cin 的缓冲区中并且被当成一个字符来计算!
   比如我们在键盘上敲下了 123456 这个字符串,然后敲一下回车键(\r)将这个字符串送入了缓冲区中,那么此时缓冲区中的字节个数是 7 ,而不是 6。
   cin 读取数据也是从缓冲区中获取数据,缓冲区为空时,cin 的成员函数会阻塞等待数据的到来,一旦缓冲区中有数据,就触发 cin 的成员函数去读取数据。

I. std::cin 可以连续从键盘读取想要的数据,并忽略空格、tab 或 换行符。 II. 可通过函数 cin.fail() 判断 cin 是否成功从缓冲区读取数据,若刚进行的操作失败,则返回真,否则返回假。 III. 可通过函数 cin.eof() 判断 是否检测到文件尾,linux可通过Ctrl+D(window可过Ctrl+Z)模拟文件尾,当检测到 EOF后cin.eof()返回true

1.1 operator>>

operator>> 应用于输入流 std::cin 的运算符(operator >>)称作提取运算符。

  I. 通常使用 while(cin >> a) 来读取输入数据,因为operator>>函数返回对象istream&。当读取成功时bool(cin>>tmp)值为true,反之为false
  II: 因为operator>>函数返回输入流对象istream&,所有可以使用 cin>>a>>b 来进行读取。

operator >> 函数原型如下:
istream& operator>> (bool& val);					// arithmetic types (1)
istream& operator>> (short& val);					// >> 右侧为缓冲区数据:
istream& operator>> (unsigned short& val);
istream& operator>> (int& val)
... // 此处省略其他同类型函数

istream& operator>> (streambuf* sb );				// stream buffers (2)	

istream& operator>> (istream& (*pf)(istream&));		// manipulators (3)	
istream& operator>> (ios& (*pf)(ios&));
istream& operator>> (ios_base& (*pf)(ios_base&));

   测试代码-1:

int main () {

	int tmp = 999;
    cin >> tmp;
    cout << "int value: " << tmp 
         << " \nbool(cin): " << bool(cin)
         << " \ncin.eof  : " << cin.eof() 
         << " \ncin.fail : " << cin.fail() << endl;
    
    return 0;
}

输入: W        // W不是int类型匹配的数据,因此读取失败,temp设置为 0
打印:int value: 0
   bool(cin): 0
   cin.eof : 0
   cin.fail :1

输入: 100        // 读取成功,temp设置为100,
打印:int value: 100
   bool(cin): 1
   cin.eof : 0
   cin.fail :0

   测试代码-2:

int main () {

    vector<int> data;
	int tmp;

	while (cin >> tmp){				// 读取数据,将数据插入 vector中
		data.push_back(tmp);
		if (cin.get() == '\n')		// cin.get()读取 ','
            break;
	}

    for (auto& i:data)				// 打印 vector
        cout << i << " ";

    return 0;
}

输入:1,2,3,4,5
打印:1 2 3 4 5

输入:1,2,3,4, ,5
输出:1 2 3 4

cin >> temp 会读取一个匹配类型,并将后续数据留在输入缓冲区中。 上述列子中,可以看出 cin.get()可帮忙读取一个间隔','字符,当连续输入两个','字符时,cin 读取到第二个','字符,因此bool(cin>>temp)false,循环结束,导致 5 并未插入数组中。

1.2 get()

get() 有多种重载形式: I. get()get(ch) 可读取缓冲区的下一个字符,哪怕是空格或回车,因此可以使用它来读取分隔符。
int get();
istream& get(char& c);		

   测试代码-1:

int main () {

    vector<int> data;
	int tmp;
    char ch;

    cin.get(ch);
    cin >> tmp;
    cout << ch << tmp;		//  即使 ch 为空格或回车,都会打印ch
    
    return 0;
}

II. 下述原型函数中,函数 get() 从缓冲区读取字符串并将其存储在 C 风格的字符串中。
istream& get(char* s, streamsize n);			
istream& get(char* s, streamsize n, char delim);
get(char* s, streamsize n):参数1为数组地址,参数n为读取的字符数,停止读取的条件如下:
(1) 读取过程中遇到换行符时,停止读取,并在该位置处将'\0'插入数组,换行符留在输入队列中
(2) 若输入字符串长度大于 n,则读取 n-1 个字符,最后一个字符自动补'\0'
   get(char* s, streamsize n, char delim): 参数 delim 为分隔符。
  如果找到了分隔符,则将分隔符保留在输入队列中。
  对于n>0,即便读取的是空字符串,也会在字符串中补一个'\0'

   测试代码-2:

// istream& get(char* s, streamsize n);	
int main() {
	char ch;
	char array[20]; 
	cin.get(array,20);
	cin.get(ch);

	cout << array << " " << int(ch) << endl;
	return 0;
}

输入:fufu!
打印:fufu! 10
分析:cin.get(array,20); 读取一行时,遇到换行符时结束读取,并在该位置处将'\0'插入数组,换行符留在输入队列中(缓冲)cin.get() 将换行符读入变量 ch,打印输入换行符的 ASCII 码值为 10

   测试代码-3:

// istream& get(char* s, streamsize n, char delim);
int main() {
	char ch;
	char array[20]; 
	cin.get(array,20,'A');			// 使用分隔符,遇到‘A’停止,‘A’留在输入缓冲中
	cin.get(ch);

	cout << array << " " << int(ch) << endl;   
	return 0;
}

输入:fufu …~ A
输出:fufu …~ 65
可以看到,cin.get(ch) 读取到缓冲中的字符A,打印int类型为65.

III. 还有一种参数类型为 streambufget() 函数。
istream& get(streambuf& sb);						// 3. stream buffer
istream& get(streambuf& sb, char delim);

1.3 getline()

从函数名可以看出,该函数读取一行数据。 getline() 函数与 get() 功能相似,不同在于当遇到换行符时,getline() 将丢弃换行符,而get() 将换行符保留在输入缓冲中。
istream& getline(istream& is, string& str);
istream& getline(istream& is, string& str, char delim);		
int main() {
	char name[20];
    cin.getline(name,20);			// 1. 舍弃换行符

    char age[10];
	cin.getline(age,20,'A');		// 2. 分隔符‘A’

    char ch;
	cin.get(ch);

	cout << "name: " << name;
    cout << " age:  " << age;
    cout << " ch:   " << int(ch);  
	return 0;
}

输入:
   fufu
  13!A
打印:
  name: fufu age: 13! ch: 10

可以看出,读取名称fufu后,舍弃了换行符。 继续读取 13!A 时,遇到分隔符 'A',丢弃 'A',此时输入缓冲区还剩下换行符。 cin.get(ch) 读取换行符,打印出来对应的int为10.

1.4 cin 的条件状态

最开始时简单的讲解了 cin.fail()cin.eof()。此处对cin对象的条件状态进行介绍:

条件状态标识符号为有:

  goodbit		无错误 
  eofbit		已到达文件尾
  failbit		非致命的输入/输出错误,可挽回
  badbit		致命的输入/输出错误,无法挽回 
  
  ### 函数功能 ###
  s.eof()				若流s 的eofbit置位,则返回true;
  s.fail()				若流s 的failbit置位,则返回true;
  s.bad()				若流s 的badbit置位,则返回true;
  s.good()				若流s 的goodbit置位,则返回true;
  s.clear(flags)		清空状态标志位,并将给定的标志位flags置为1,返回void。
  s.setstate(flags)		根据给定的flags条件状态标志位,将流s 中对应的条件状态位置为1,返回void。
  s.rdstate()			返回流s 的当前条件状态,返回值类型为strm::iostate
来源url
栏目