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

By | 2020年4月2日

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
*/

点击量:257

发表评论

邮箱地址不会被公开。 必填项已用*标注