第17章-输入、输出和文件

使用cout进行输出:

最简单的情况,输出基本类型:

int a = 10;
cout<<a;

输出进本类型,其实本质上是<<运算符的重载为插入运算符。将右方的变量类型进行处理,最后以字符的形式输出至控制台。也即上方的输出基本是对应这个重载原型:ostream& operator<< (int);
右方的类型包括:
unsigned char、signed char、char、short、unsigned short、int、unsigned int、long、unsigned long、unsigned long long、float、double、long double。
可以说基本上所有整型和浮点型都包括了。

另外一种常见的情况就是输出字符串,操作上就是输出指针,因为字符串用首字符的指针表示,原型有四个:

const signed char*;
const unsigned char*;
const char*;
void*;

简单栗子:

char name[10] = "james";
char* pr = "kobe";

//这三个对应前三种,基本就是输出字符串
cout<<"i like play basketball";
cout<<name;
cout<<pr;

//第四种比较特殊。对于其他类型的指针,都对应于void*,打印地址!
 int egg = 12;
 char* amount = "dozen";
 cout << &egg;  //输出egg的地址
 cout << amount;  //输出字符串"dozen"
 cout << (void*)amount;  //输出字符串"dozen"的地址,也就是amount指针的值。

拼接输出就不说了:cout<<a<<b<<c;

cout.put()用于显示字符、cout.write()用于显示字符串。没看出来有啥大用处。先写在这里,有用后面再补。

刷新输出缓冲区:

cout<<"im robin";
cout<<"im kobe";
cout<<"im james";

看上面三句,流程上其实是将这三句依次推入输出缓冲区,也就是在执行第二句输出时,第一句的”im robin”是在输出缓冲区,并没有输出到屏幕上。但运行完还是能看到输出,是因为程序结束时,输出缓冲区被刷新,所以在屏幕上输出了。整个过程屏幕并不是依次显示这三条信息,而是在执行完后,存留在输出缓冲区,程序接受缓冲区被刷新 ,一次性全部显示在屏幕。

此时,问题来了,如果想手动控制刷新输出缓冲区呢?!flush和endl。
flush强制刷新缓冲区,endl强制刷新缓冲区,并插入一个换行符(原来endl还有这么多用处,之前只是简单认为它可以换行。。。)。
用法上:

cout<<flush;
cout<<endl;//基本全是用endl,flush基本没见过。。。

格式化输出:
书上关于格式化输出写的比较多,也比较全,例如有:

修改计数系统:cout<< hex;长期有效
调整字段宽度:cout.width(12);一次性有效
填充字符:cout.fill('*');长期有效
设置浮点数精度:cout.precision(2);长期有效
打印末尾的0和小数点:cout.setf(ios_base::showpoint);长期有效
由setf()函数扩展出来的开关设置和选项设置:

ios_base::boolalpha   //输入输出bool值,可以为true或false
ios_base::showbase    //显示基数前缀
ios_base::showpoint    //显示末尾的小数点
ios_base::uppercase    //对于16进制输出,就是大写字母E表示法
ios_base::showpos    //正数前加+

ios_base::basefield对应的基数设置:

 - ios_base::dec  //10进制
 - ios_base::oct  //8进制
 - ios_base::hex  //16进制

ios_base::folatfield对应的浮点数计数法设置:

 - ios_base::fixed  //定点计数法
 - ios_base::scientific  //科学计数法

ios_base::adjustfield对应的对齐方式设置:

 - ios_base::left  //左对齐
 - ios_base::right  //右对齐
 - ios_base::internal  //符号或基数前缀左对齐,值右对齐

用法:
cout.setf(ios_base::showpos);  //显示正数前的正号。其余类似
cout.setf(ios_base::left, ios_base::adjustfield);//左对齐

说几个点:

  1. 精度的意义在计数法不同时意义不同。默认计数法时,精度指的是显示总位数、定点和科学计数法时指的是小数位数。所以在设置精度时一定先设置好计数法,在固定计数法的基础上再设置精度。
  2. 定点和科学计数法显示末尾的0。
  3. 计数法并没有对应的设置回默认模式的参数符。若要恢复使用cout.unsetf(ios_base::floatfield);

其实unsetf()函数就是用于各项设置的清除,回归默认的方法。不再举例。

OK!说了这么一大堆是不是感觉天昏地暗,没啥规律不好记但是瞅着还挺常用。好了,这时候C++告诉你,其实前面的东西都被革新掉了,我们有新货。。。。。。
那特么你还说这么多?!
那必须说啊,不然怎么体现C++牛逼(当然最大的用处就是可以看懂别人写的代码)。。。。。

先列一个表:

  • boolalpha
  • noboolalpha
  • showbase
  • noshowbase
  • showpoint
  • nooshowpoint
  • showpos
  • noshowpos
  • uppercase
  • nouppercase
  • internal
  • left
  • right
  • dec
  • hex
  • oct
  • fixed
  • scientific
  • setprecision()
  • setfill()
  • setw()

好了,这个表怎么用呢?

cout<<showbase;
cout<<left;
cout<<hex;
cout<<fixed;
cout<<setprecision(8);
cout<<setfill('*');
cout<<setw(10);

还可以穿串。。。。

cout<<showbase<<left<<hex<<fixed<<setprecision(8)<<setfill('*')<<setw(10)<<10;

总结一下:你特么倒是早说啊!!!

使用cin输入:

cin基本就是cout的反向过程,将输入的字符转换成对应的目标变量类型,存入其中。字符串的话就是指针,跟cout道理相同。

检查输入:

int elevation;
cin >> elevation;

假如输入 -1223z(注意最开头有个空格)。将跳过空格,直到z不满足条件结束,并且会返回false!
也就是可以用while(cin>>elevation){...}这种形式来控制循环输入。

流状态:

流状态的成员和方法如下表:
这里写图片描述
clear()将清除3个状态位(eofbit,badbit,failbit)。
clear(eofbit)将状态设置为eofbit,另外两个被清除
setstate(eofbit)将设置状态为eofbit,另外两个无影响

重新设置流状态是为了在输入不匹配或到达文件尾部时,用clear()重新打开输入(不然会关闭,不好用)。setstate()主要是提供手动设置途径。

IO和异常:
先说一下,假设某个输入把流状态设置为了eofbit或是failbit或是badbit。但是,并不会引发异常!
要想让其被设置时引发异常,需要用到exceptions()。

#include<iostream>
#include<exception>  //exception()函数需要引进此头文件 

int main()
{
    using namespace std;
    cin.exception(ios_base::failbit);//这里将failbit设置为可以引发异常。
    int sum = 0;
    int input;
    try
    {
        while(cin>>input)
            sun += input;
    }
    catch(ios_base::failure & bf)//这里捕获异常
    {
        cout<<bf.what()<<endl;//输出异常内容
    }
    cout<<"last value entered = "<<input<<endl;
    return 0;
}

异常的触发和捕获看情况使用。

流状态的影响:
只有在流状态良好(所有位都被清除)的情况下,cin >> input;才会返回true。

在触发了一些状态位时,相应的函数会返回bool判断值:if(cin.eof()){cout<<"error";}。可以用于判断输出信息。

当流状态位被设置后,有一个很严重的后果,就是流会将后续的输入(或输出)关闭,直到位被清除。若不清除,后面的输入(或输出)是不工作的!!!此时就要用到重置流状态位,cin.clear()。

还有一个经常用到的就是,将空白或这整行直接略除掉;

while(cin.get() != ' ')
    continue;//略除空格,一般就是直到有效信息处。不管跳不跳行!

while(cin.get() != '\n')
    continue;//略除当前行剩下的东西,到换行处!

其他istream方法:
对象方法 get(char&)和get(void),不跳过空白的单字符输入。
对象方法 get(char*,int,char)和getline(char*,int, char)读取整行。

首先,对象方法意味着调用方式都是cin.get()或者cin.getline()这种形式。

重载的>>运算符是会跳过空白符的!

首先看一下单字符读取:

附表17.5

//cin.get(ch)简单用法:
char ch1;
while(cin.get(ch1)) //由于到达文件尾部时会返回false,所以可以用于到达尾部判断。
{...}

//cin.get()简单用法:
char ch2
ch2 = cin.get();
while(ch2 != '\n')  //由于返回字符编码值,所以跟具体字符对比会让读取在中间停下。
{
    ...
    ch = cin.get();
}

//cin.get()在到达文件末尾时,可以这样用:
while(ch2 = cin.get() != EOF)
(多一嘴,是不是中间具体字符停止也能这样用?)
while(ch2 = cin.get() != '\n') //换行停下?

cin.get()和cout.put()其实就是为了进化C语言中getchar()和putchar()所设计。所以还是尽量的用cin.get(char*)这种。
(再啰嗦一嘴,C++真的无底洞,只要确切的掌握一种方法,而且满足使用需求即可,千万不要作死想知道所有特性。。。)

字符串输入:

char line[50];
cin.get(line, 50, 'z');
cin.getline(line, 50, 'z');

说明:

  1. 第二个参数最大字符数、第三个参数分界符、换行符,这三个先遇到哪个都停止输入。
  2. get将停止后的换行符和分界符留在输入流中。
  3. getline将停止后的换行符和分界符从流中读出并丢弃。

还有个ignore方法:cin.ignore(255, '\n');读取并丢弃接下来的255个字符,或者丢弃到下一个换行处。。。感觉又是坑,之前不还有while continue法丢弃字符麽?!怎瞅着功能这么像。。。

文件输入输出

简单的文件IO:

说明一下,
写入文件对程序来说,是输出,用fout之类。
读取文件对程序来说,是输入,用fin之类。

简单介绍:

#include<fstream>  //必须包含头文件fstream

ofstream fout;  //创建文件输出(对于程序而言)流对象,用于文件写入。
fout.open("jar.txt");  //调用open()链接打开文档
fout<<"robin";  //将数据输出写入到文档中
fout.close();  //关闭链接的文档,可以理解为关闭文档

ifstream fin;  //创建文件输入流(对于程序而言)对象,用于文件读取。
fin.open("her.txt");  //链接打开文档
fin>>ch;  //从文档中输入数据至ch变量
fin.close();  //关闭文档

关闭文档链接时,就可以重新再用open()和close()来链接另外一个文件了。不用重新创建文件IO流对象。

文件打开时有打开模式,后面再说。

is_open()返回文件是否成功打开。

if(!fin.is_open())
{
    cout<<"file failed to open!"<<endl;
}

这是最新的用法,之前的还有一些:

if(fin.fail());
if(!fin.good());
if(!fin);

知道啥意思就可以了。。。

对于fout基本不存在这个问题,因为没有文件的话会自动创建一个。。。一般就是用于打开输入文件,读取数据时,检查一下是否打开了。

命令行处理:
在终端中:run.exe robin kobe
程序中:int main(int argc, char* argv[])
argc:argument count,命令行参数个数。包括命令(run.exe)
argv:argument value,命令行值。

所有的命令行参数都解析为字符串。也就说上面的程序和命令行:
argc = 3 //因为三个命令。
argv = {run.exe,robin,kobe} //命令行字符串数组。
调用时,argv[0]即为”run.exe”,argv[1]即为”robin”,argv[2]即为”kobe”。

文件模式:
将流与文件关联时,可以提供第二个参数,指定文件模式:读、写、追加等。

ifstream fin("haha.txt", ios_base::in);  //创建流时直接链接并设置。

ofstream fout;
fout.open("hehe.txt", ios_base::out);  //在open方法中设置也可以。

这里写图片描述

ios_base::trunc的截断会删除之前文件夹内容,若要是追加,用ios_base::app。用|运算符开启多模式。

//典型的打开已存在文件,并且追加写入信息。
ofstream fout("haha.txt", ios_base::out|ios_base::app);

各种组合如下图:
这里写图片描述

ios_base::ate和ios_base::app都将文件指针指向打开文件的末尾,区别在于:
ios_base::app只允许在文件尾部添加数据
ios_base::ate将指针指向文件尾后,可以进行其他的任何操作

二进制文件用到再说吧(主要是懒)。。。

随机存取:简单说就是定位到文件的任何一个位置。
seekg()输入指针移动到指定位置。
seekp()输出指针移动到指定位置。

seekg()和seekp()都是模板函数,一般使用char类型模板具体化:

istream& seekg(streamoff, ios_base::seekdir);  //定位位置后指定偏移量
istream& seekg(streampos);  //直接指定从开头算起的位置。

栗子:

//原型1对应使用:
fin.seekg(30, ios_base::beg);  //文件开始位置往后30位
fin.seekg(-1, ios_base::cur);  //文件当前位置后退一位
fin.seekg(0, ios_base::end);   //定位到文件末尾

//原型2对应使用:
fin.seekg(112);  //直接将文件指针指向第112个字节,因为从0开始计数,所以,指向的是文件的第113个字节(跟数组定位差不多。。。)
fin.seekg(0);  //同理,这就是指向文件开头了

如何知道文件指针的当前位置呢?
tellg()返回输入流文件指针当前位置。
tellp()返回输出流文件指针当前位置。
它们都返回一个表示当前位置的streampos值(以字节为单位,从文件开始处算起)。

内核格式化
iostream族支持程序与终端之间的IO。
fstream族支持程序与文件之间的IO。
sstream族支持程序与string对象之间的IO。此就是内核格式话。
这个后续用到再说吧。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值