使用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);//左对齐
说几个点:
- 精度的意义在计数法不同时意义不同。默认计数法时,精度指的是显示总位数、定点和科学计数法时指的是小数位数。所以在设置精度时一定先设置好计数法,在固定计数法的基础上再设置精度。
- 定点和科学计数法显示末尾的0。
- 计数法并没有对应的设置回默认模式的参数符。若要恢复使用
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');
说明:
- 第二个参数最大字符数、第三个参数分界符、换行符,这三个先遇到哪个都停止输入。
- get将停止后的换行符和分界符留在输入流中。
- 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。此就是内核格式话。
这个后续用到再说吧。