C++Prime Plus(7)

96.输入输出概述

C++的输入输出是由库iostream中提供的一组类实现的;


C++把输入输出看成字节流,每个字节可以由程序解释成一个字符,或者只是一个二进制位构成的串;

输入流:外围设备流向输入缓冲区,Input对象从输入缓冲区读数据再流向内存;
输出流:内存流向Output对象,Output对象处理后传送给输出缓冲区,再流向外围设备;

通常,输入对象来自类istream,输出对象来自类ostream。

C++在命名空间std下预定义了一些输入对象和输出对象:

  • cin对象:类istream的对象,与标准输入设备(通常指键盘)关联在一起;
  • cout对象:类ostream的对象,与标准输出设备(通常指显示器)关联在一起;
  • cerr对象:类ostream的对象,与标准错误输出设备(通常指显示器)关联在一起,不经过缓冲区;
  • clog对象:类ostream的对象,与标准错误输出设备(通常指显示器)关联在一起,经过缓冲区;

语句的解释:cout<<对象1<<对象2;

首先确保对象1和对象2的类中,有定义运算符<<的重载(通常是类中的友元,回顾60.友元),cout<<对象1 返回ostream类的对象(其实还是cout,因为cout作为参数传入,在调用结束后返回cout对象),所以变成cout<<对象2 继续输出。


重定向标准输入输出
如果我们在命令行界面执行可执行文件,只要在命令行提示符后面输入可执行文件名,程序中的输入部分都是从键盘读,输出都是显示在显示器。

我们可以在命令行执行可执行文件时,重定向输入:

可执行文件名 < 文本文件名 #从文本文件读数据

重定向输出:

可执行文件名 > 文本文件名 #将数据输出到文本文件

同时重定向输入和输出,从文本文件1读数据,将处理后的数据输出到文本文件2:

可执行文件名 < 文本文件名1 > 文本文件名2

97.使用cout输出(1)ostream基本功能

假设有:

int x=-1;
cout<<x;

编译器执行整型类的operator<<函数,从x的地址开始取4个字节,将其转换成字符串”-1”,发送到显示器

operator<<函数的返回值是ostream&,所以允许拼接输出,比如:

cout<<x<<"abc";

ostream类的成员函数:put和write
put:输出一个字符
函数原型ostream& put(char),返回ostream类的对象本身

cout.put('a'); //输出字符a
cout.put('a').put('b'); //先输出字符a,再输出字符b,cout.put('a')返回对象cout

write函数:输出指定字符数的字符串

const char* tmp="abcdefg";
cout.write(tmp,5); //输出abcde

98.使用cout输出(2)格式化输出

C++提供了用于执行格式化 输入输出 的流操作算子(本质上是<<运算符的参数,是函数对象)和成员函数(ostream类的成员函数)

设置整型数的基数
输入输出流中的整型数默认为十进制表示,若要设置其他数制,可以用流操作算子(使用任何带参数的流操作算子,都必须包含头文件iomanip):
fig1
基数值只有被显式更改时才会变化,否则一直沿用原有的基数

示例
fig2
设置浮点数的精度
浮点数的精度指实数的有效位数,设置方法:

  • 流操作算子:setprecision(位数)
  • cout成员函数:precision(位数)

一旦设置了精度,将影响所有输出的浮点数精度,直到下一个设置精度的操作出现为止

示例
fig3
设置域宽
域宽是数据所占的字符数,设置方法为:

  • 流操作算子:setw(宽度)
  • 成员函数:width(宽度)

比如a=123, b=456,输出cout<<a<<b123456,但是如果cout<<setw(5)<<x<<set(5)<<y会输出:\n\n123\n\n456

99.使用cin输入

cin是istream类的对象,>>运算符的返回值还是cin,>>以空白字符(空格,回车,TAB)作为输入流(输入缓冲区)中的分隔符,或者在读缓冲区时,遇到与接收变量类型不符合的数据时,本次>>输入结束(即使用类型不符合的数据作为分隔符)。
fig4
利用cin>>时,会自动丢弃空白字符;使用类型不符合的数据作为分隔符时,不丢弃该数据;

输入错误的处理
如果输入数据不能转换成接收变量的类型,或者输入了EOF,cin>>变量 表达式返回0,变量的值也变成0;
fig5
输入错误的分类

  • cin.eof():读到EOF;
  • cin.bad():IO失败或者无法诊断的错误;
  • cin.fail():没能读到预期类型的数据;

fig6
遇到错误时,程序不会继续读输入缓冲区,我们可以调用cin.clear()清除错误,让程序可以从输入缓冲区读数据。示例如下:
fig7
其他输入方法

  • ch=cin.get():读入一个字符,可以是任意字符,包括空白字符和EOF
  • cin.getline(字符数组名,数组规模)
  • cin.get(字符数组名,数组规模)

cin.getline(字符数组名,数组规模)cin.get(字符数组名,数组规模)以回车字符或达到数组规模结束输入,区别:getline将回车的换行符丢弃,get会将换行符留在缓冲区放在下一次输入的最开始位置。

在早期C语言没有getline时候,只能使用get,但是get对于读入回车的处理会让人们对字符文本的逻辑容易出错,为了让get每次都只输入一行,并让回车不放在下一次输入的行中,我们使用无参数的cin.get()cin.get()读入任意一个字符,包含回车。

100.文件(1)简单的文件IO

文件读写需要我们定义流对象,并与文件相关联;

文件流类(从标准输入输出流类派生,被定义在头文件fstream中):
ifstream:输入文件流
ofstream:输出文件流
fstream:输入输出文件流

文件流对象关联文件(两种方式):
1.使用open成员函数:

ofstream outfile;
outfile.open(“file_name”);

2.在定义对象时,向构造函数传递参数(参数为文件名):

ifstream infile(“file_name”);

标准输入输出流对象在控制台和内存之间交流,文件流对象在文件和内存之间交流:

infile>>x; //将infile关联的文件内容读到变量x
outfile<<123<<3+5; //将字符串”123”和int数值8保存到outfile关联的文件

切断文件流对象与文件的关联:
使用close成员函数:infile.close();

简单文件访问示例:
fig8
上述示例会在当前工程目录下创建名为pythag的文件,并将内容写入,我们也可以用记事本查看文件中的内容。

101.文件打开的进一步讨论

首先回顾关联文件与文件流对象的两种方式:
1.open成员函数
2.流的构造函数(向构造函数传递参数)

文件打开时可以指定打开方式:

文件流对象.open(文件名, 打开模式);
文件流类 文件流对象(文件名, 打开模式);

常用打开模式如下:

  • ios_base::in,含义:打开文件,做读操作,这是ifstream的默认参数(如果指定路径没有文件,不会创建);
  • ios_base::out,含义:打开文件,做写操作,这是ofstream的默认参数(1.如果指定路径中并没有包含该文件,会创建一个新的;2.每次写入的内容会覆盖以前的内容,暗含了std::ios_base::truc);
  • ios_base::app,含义:在每次写操作前,找到文件尾,即append(如果指定路径不包含该文件,会创建一个新的);
  • ios_base::ate,含义:打开文件后,立刻定位到文件尾(如果指定的路径不存在该文件不会创建)
  • ios_base::truc,含义:打开文件时,清空文件(如果指定的路径不存在该文件不会创建)
  • ios_base::binary,含义:以二进制模式进行输入输出操作(如果指定的路径不存在该文件不会创建)

打开方式组合:以添加方式打开二进制文件

ofstream outfile(“file1”, ios_base::app | ios_base::binary);

关于文本文件
文件中的信息是合法字符,用<<向文件写数据时,数据被转换成字符串,用>>从文件读数据时,字符串被转换成接收变量的类型。

关于二进制文件
存储的数值是机器表示,比如将一个double类型数值写入二进制文件,文件存储的是64位表示。

对比:将整型-1写入文件
文本文件:占用2个字符,分别是’-’和’1’
二进制文件:在文件中占4字节,是-1的补码表示

检查文件打开是否成功
1.检查文件流对象:打开文件失败时,流对象返回false;
2.fail成员函数:打开文件失败时,fail函数返回true;
3.is_open成员函数:文件打开成功后,is_open函数返回true。

102.二进制文件访问

对于文本文件,只要文件与文件流对象建立关联(也就是打开了文件),文件的访问方式和控制台一样,只是换了一种流对象而已。

但对于二进制文件的访问,我们人是看不懂二进制文件的,但是二进制文件相对于文本文件有一个优点:数据在文件中占用的空间与数据类型有关,与具体的值无关,这便于随机访问。

假设我们在文本文件中写入一组整数,我们要获取第1000个整数的值,我们只能从文本文件中一个一个读,直到读到第1000个整数才能获得值。但对于二进制文件,由于整型都是只占有4字节,我们可以不用读前999个数,我们可以直接锁定到第1000个数的地址,然后取值。(这就是随机访问)

二进制文件读写
对于二进制文件的读写,我们不能使用>>和<<(因为>>和<<会自动在内存表示与人能看懂的字符串之间进行转换),而二进制文件要求:我们直接将内存表示写到二进制文件,或者直接将二进制文件读到内存中。

写文件:

write(char*, 长度); 
//类型转为char,因为char占1个字节,方便计数信息长度;

当我们要把内存的信息写到文件,我们要告诉机器两件事情:1.这块信息在机器中的地址是多少,2.这块信息的长度是多少。

读文件:

read(char*, 长度); 
//char* 用于指明将文件中的数据读到机器中的char*所指位置,并且有多大长度的信息要读入内存。

判断读文件结束:
eof()read函数的返回值
在读文本文件时候,如果用>>去读,操作结果返回fin,如果fin.eof()为true可以判断读文件结束。对于二进制文件的读,也是一样的,也是看输入文件流对象的eof()。
读二进制文件判断结束也可以看read函数的返回值(读到文件结束,read返回false)。

比如:

int x=5;
fout.write((char*)&x, sizeof(int));

int y;
fin.read((char*)&y, sizeof(int));

二进制文件读写示例(把一组结构体类型信息写到一个二进制文件中,便于随机访问)
fig9
fig10
fig11
提前说明一个内容:在代码的最后几行里看到
fig12
并且发现分别打印了两行:
fig13
这是因为在读或写二进制文件时,有一个指针,当文件流对象关联文件后,指针默认指向文件开始位置,当读入或写入一定长度信息后,指针停留在 文件起始位置+该长度偏移 的位置。下次对文件的操作就从指针当前位置开始执行。

103.随机读写

我们可以手动设置读写文件的位置
设置读文件的位置:seekg(偏移量, 起始位置); //操作位置=起始位置+偏移量
设置写文件的位置:seekp(偏移量, 起始位置);

对于参数 起始位置

  • ios_base::beg 文件开始处,是函数参数的默认值
  • ios_base::cur 当前位置
  • ios_base::end 文件尾

102.二进制文件访问中的例子继续演示

首先显示原文件的内容:
fig14
修改某一条记录:由于是二进制文件,我们可以很容易去定位
fig15
显示修改后的文件新内容:
fig16
执行结果如下:
fig17

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值