C++入门学习笔记7:输入输出流

406 阅读3分钟

本文全部内容基于西安电子科技大学潘蓉老师的《面向对象程序设计》课程记录而成


printf、scanf的缺点

  • 非类型安全。可以输入/输出各种类型的数据。如果scanf的地址写错,可能会让程序硬写内存导致一些危险的后果
  • 不可扩充性。没办法输出复杂的东西

C++使用类型安全的 I/O 流操作,不同类型的 I/O 流操作都是重载的,没定义过 I/O 功能的类型不具备 I/O 操作的能力。通过重载,可以对 I/O 进行自定义的修改和扩充。

C++ 使用输入输出流进行 I/O

流 Stream

流,是字节的序列。从一个位置流向另一个位置。输入输出,就是字符序列在内存和外设之间的流动。
从流中获取数据:提取操作(输入操作)
向流中添加数据:插入操作(输出操作)
输入流:从外设到内存;
输出流:从内存到外设。

C++ I/O流类库的层次结构

I/O 流类库有两个平行基类:streambuf、ios

常用 ios,ios 有 istream(输入流类)、ostream(输出流类)、fstreambase(文件流类基类)、strstreambase(字符串流类基类)

I/O流类库头文件:

  • iostream:其包含 cin、cout、cerr(非缓冲方式,内容会迅速显示出来)、clog(缓冲方式,会先存入缓冲区,缓冲区刷新才显示) 四个流对象
  • fstream 用于文件
  • strstream 用于字符串流IO
  • stdiostream
  • iomanip 使用格式化 I/O,可以包含控制符 / 操作符

无格式输入输出

输出:cout<<

"<<":插入运算符,作用在 cout 对象上。向流中添加数据(插入操作)

cout 是 ostream 的对象,其在 iostream 头文件中作为全局对象定义如下:

ostream cout(stdout);		// 定义如此,stdout 就是屏幕

单个字符输出

cout.put('a');
cout.put(71).put(79);	// ascii,这样是连续调用

输入:cin>>

“>>”:提取运算符,作用在 cin 对象上

其为 istream 流类的对象,同样在 iostream 中作为全局对象定义如下:

istream cin(stdin);		// stdin: 键盘

输入输出格式控制

使用流对象的有关成员函数进行格式化 I/O

  • 格式由各种状态标志来确定,在ios中都是16进制的4位数表示。

  • 使用时要用 ios:: 前缀,使用符号名指定。

    long ios::setf(long flags);		// 追加状态标志,在现有的之上加
    long ios::unsetf(long flags);	// 清除标志
    long ios::flags();	// 获取状态标志
    long ios::flags(long flag);	// 将流的状态标志位设置为flag
    							// 并返回【设置前的】状态标志值
    							// 将会将过去的所有flag清0
    // 这些函数需要使用流类对象进行调用
    <流对象名>.<函数名>(ios::<状态标志>)
    cout.setf(ios::showpos);	// 正数前面加正号
    cout.setf(ios::scientific);	// 科学计数法表示
    cout.setf(ios::showpos | ios::scientific);	// 同时生效
    
  • 设置输出宽度,不够的补空格。默认是0,就按照实际的往出打

    int ios::width(int len)	// 返回设置之前的宽度
    int ios::width()		// 返回当前宽度
    // width 函数只会对后面紧跟着输出的第一个输出项有效!
    cout<<i<<j;	// 只有 i 会起作用
    
  • 设置填充字符

    char ios::fill(char ch);// 返回设置之前的。和width配合使用填空
    char ios::fill();		// 返回当前
    
  • 类似的,还有 precision(int p)(浮点输出精度,是整个数字长度,有效位数!)

例:

int main() {
    int i;
    i = cout.width();
    cout<<"width:"<<i<<endl;
    cout.width(8);
    cout<<cout.width()<<i<<endl;
    char c;
    c = cout.fill();
    cout<<"filling word is:"<<c<<"(ASCII code"<<(int)c<<")"<<endl;
    cout.fill('*');
    cout<<cout.fill()<<"("<<(int)cout.fill()<<")(new filling word)"<<endl;
    int j;
    j = cout.precision();
    cout<<"precision:"<<j<<endl;
    cout.precision(8);
    cout<<123.456789<<"(example)"<<endl;
    cout<<cout.precision()<<"(new precision)"<<endl;
    return 0;
}

/** 以下是打印输出
width:0
       8(new width)
filling word is: (ASCII code32)
*(42)(new filling word)
precision:6
123.45679(example)
*/

用于字符输入的流成员函数

get函数读入字符
int a;
char b;

a = cin.get();	// 输入的值是返回值
cin.get(b);		// 读取成功就把读到的赋给 b。成功返回非0值,失败返回0
// cin.get(字符数组/字符指针, 字符个数n, 终止字符);
// 连续读取 n-1 个字符赋给数组指针,末尾自动加\0
// 如果在 n-1 个字符前遇到了终止字符,则读取提前结束
// 例:
int main() {
    char ch[20];
    cin.get(ch, 10, '/');
    cout<<ch<<endl;
    cin.get(ch, 20, '\n');	// 如果读到换行为止,则 \n 是可省略的
    cout<<ch<<endl;
    return 0;
}
/* 输出例:
you!/her.	// 第一个 cin 对应的输入
you!		// 只读到 / 位置
/her.		// 第二个 cin 对应的输入,从刚才停下的位置继续开始。输出。 
*/
getline读取一行
char ch[20];
cin.getline(ch, 20, '/');

getline 读取停止后,字符指针会移到所读的最后一个字符的下一个的位置,而带三个参数的 get 不会——最终会停止在终止字符的位置

istream的其他成员函数

eof函数

无参函数,表示文件结束。从输入流读数据。到文件末尾(遇到文件结束符)返回非0值(真),否则为0(假)

常用在循环语句中,判断循环是否结束

while(!cin.eof())
    ...
// 没有到文件末尾就继续,到了就跳出循环
peek函数

观察一下指针所指的当前字符并返回,不会将指针后移。

putback()
ignore()
cin.ignore(n, 终止字符); 	// 忽略输入流中的 n 个字符,或遇到终止提前结束
cin.ignore(5, 'A'); // 跳过 5 个字符,遇到 'A' 就不再跳

ignore(); // 无参数情况下,等价于 ignore(1, EOF),也就是只跳一个

使用专门的控制符进行格式化 I/O

可以不直接以标志位方式处理流的状态。这些控制符不属于任何类成员,定义在 iomanip 头文件中

#include <iostream>
#include <iomanip>

using namespace std;

int main() {
    char *p = "12345", *q="678";
    char f[4], g[4];
    int i = 10;
    cout<<p<<setw(6)<<q<<setw(4)<<p<<q<<endl;	// 设置输出宽度
    cin>>setw(4)>>f>>g;		// 设置输入宽度
    cout<<f<<endl<<g<<endl<<"i:"<<i<<endl;
    return 0
}

/*
12345   67812345678		// q只有三个数,要右对齐,所以最前空了三个格
						// 后面 setw(4) 小于实际宽度,所以按实际显示
12345					// 键盘敲12345
123						// 设置的输入宽度为4,所以只存下了3个字符
						// 超长的部分被截断,放到下一个输入里面
45
i:10					// 所有控制符都失效,直接显示

*/

  • setfill(char):用 char 填充空白的地方。常与 setw(int) 联合使用。设置后到下一次设置前一直有效!参数可以是字符常量或变量
  • setprecision(int):指明输出实数的有效位数,默认是6 。以 fixed、scientific 形式输出时,参数时小数的位数
  • setiosflags(ios::fixed):定点方式表示实数
  • setiosflags(ios::scientific):科学计数法表示实数
  • setiosflags(ios::left):输出数据左对齐
  • setiosflags(ios::left):输出数据右对齐
  • 其他参数:uppercase大写;showpos正数带"+";skipws忽略前导空格
  • resetiosflags():终止所有格式状态。括号内可以填参数指定内容

例:

#include <iostream>
#include <iomanip>

using namespace std;

int main() {
	double values[] = {1.23, 35.36, 653.7, 1234.56};
    char *names[] = {"Zoot", "Jimmy", "AI", "Stan"};
    for(int i=0; i<4; i++) {
        cout<<setiosflags(ios::left)	// 设置左对齐
            <<setw(6)<<names[i]			// 设置输出长度
            <<resetiosflags(ios::left)	// 取消左对齐
            <<setw(10)<<values[i]		// 设置输出长度
            <<endl;
    }
    return 0
}

/*
Zoot       1.23
Jimmy     35.36
AI        653.7
Stan    1234.56
*/