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

来源:互联网 发布:月下的妖樱阿波罗数据 编辑:程序博客网 时间:2024/05/16 14:39

使用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或falseios_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 ch2ch2 = 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>  //必须包含头文件fstreamofstream 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。此就是内核格式话。
这个后续用到再说吧。

原创粉丝点击