您的位置:首页 > 编程语言 > C语言/C++

【c++笔记十三】c++中的输入、输出和文件操作

2015-02-07 16:09 736 查看


2015年2月6日 周五

很快到星期五了,感觉这一个星期都是在复习,这两周过后我觉得c++的基本知识应该掌握的差不多了,可以追求一点更高层次的东西了。

今天讲一讲c++中的输入、输出和文件操作,差不多c++基本语法就结束了。可能以后的笔记中不会再强调基本的语法知识。

——————————————分割线——————————————

其实在c语言中,我们就已经学习了基本的输入输出和文件操作,像什么printf、scanf、fopen、fclose之类的。其实c++的IO和文件都和c差不多,只不过吧c中的这些都封装到类中去了。我们先看一张图:




这张图就是所有与c++相关的IO和文件的操作了。我们一起来简单的认识一下他们:

(1)标准的输入输出:

istream ostream --> #include <iostream>

标准对象有cin cout

(2)操作字符串的流:

istringstream ostringstream --> #include <sstream>

(3)操作文件的流:

ifstream ofstream fstream --> #include <fstream>

图中外圈的七个流(类),是我们c++中会经常用到的。现在我为大家一一介绍一下他们:



一.标准的输入输出


1.标准的输入输出对象

C++的iostream文件中自动创建了8个流对象但我们一般用到其中4个:cin、cout、cerr、clog

还有四个是用于宽字符流的:wcin、wcout、wcerr、wclog。(知道即可)

cin和cout代表标准的输入输出流,cerr和clog代表标准错误流。他们之间是有区别的,cout是带有缓冲的并且可以重定向,而cerr和clog是没有缓冲且不可重定向的。我们先看代码再解释什么是重定向:

[cpp] view
plaincopy

#include <iostream>

using namespace std;

int main()

{

cout<<"hello";

clog<<" world";

cerr<<"test";

return 0;

}

这是一个简单的io小程序,分别用cout clog cerr在屏幕上输出几个单词:




但是,如果我们用到重定向的话,输出的结果就不是这样了。我们先把编译后生成的.exe(可执行文件)复制一份放到c盘的根目录下,以管理员身份运行命令提示符(cmd)。操作如图:




先切换到根目录(cd /),再运行01io.exe(上面那段代码的可执行文件) > a.txt 。这“>”就是一个输出重定向符,将01io.exe的输出结果输出到a.txt这个文件中去。(一开始输出都是输出在屏幕上的,但是现在我将输出的目标改为文件,所以叫重定向(重新设定输出的方向))。我们发现屏幕上只输出了“ worldtest”,cout输出
的“hello”不见了。我们再打开a.txt这个文件看看里面的内容:




我们发现a.txt文件中只有“hello”。这就说明了只有cout能够重定向,cerr和clog是不具备重定向的。所以用cerr和clog的好处就是无论什么情况下我都能输出任何信息(一般是错误信息)。cin类似cout,这里就不再重复了。



2.其他IO类方法

除了上面要讲的四个对象之外,还有给大家补充几点常用IO类的方法(成员函数)。


(1)get和put

get是得到一个字符,put是输出一个字符。我们看看c++帮助手册里面关于它们的介绍:







get方法是用在输入流中的(input stream),put方法是用在输出流中(out stream)。put使用方法很简单,就是把一个字符写到输出流中而已。而get我们一般用它的前两个方法:

A.int get();

该方法是读入一个字符,并且返回这个字符的整数值(ACSII码)。

[cpp] view
plaincopy

#include <iostream>

using namespace std;

int main()

{

int c;

while(1){

c = cin.get();

cout<<"c="<<c<<endl;

cout.put(c);

cout<<endl;

}

return 0;

}



我们用get获取了“0、a、A”这三个字符的ACSII码并用cout输出,我们又用put输出了这个三个整数的字符形式。并且我们可以发现一个很奇怪的现象,每两次输入之间空了好几行,为什么呢?

因为,get把我们回车也输入进去了。所以我们可以看到,“回车”的ACSII码是10,put输出的是回车,cout<<endl又回了一次车,所以每两次输入间空了两行。

如果get读取到的字符是EOF(end of file,文件结束符),就会自动结束。

B.istream& get( char& ch );

这个get方法读入一个字符的引用,返回的却是一个输入流。当get遇到EOF的时候,返回的istream变为0。例子可以参考c++帮助手册的那个例子。



(2)getline

这种方法是用来读取一行数据的。




A.istream& getline( char* buffer, streamsize num );

这种方式的getline将一行字符串放在buffer中,读取num-1个字符或遇到换行或EOF时结束。如:

[cpp] view
plaincopy

#include <iostream>

using namespace std;

int main()

{

char str[20];

cin.getline(str,10);

cout<<str<<endl;

return 0;

}



规定读入10个字符,实际只读入9个字符(还有一个‘\0’)。

B.istream& getline( char* buffer, streamsize num, char delim );

这种方法和上面的方法类似,但是输入了num-1个字符或遇到delim字符或遇到EOF时就终止输入,就算换行也不会影响它的输入。如:

[cpp] view
plaincopy

#include <iostream>

using namespace std;

int main()

{

char str[20];

cout<<"请输入:";

cin.getline(str,10,'$');

cout<<"输入的字符串是:"<<str<<endl;

return 0;

}



我们可以看见,中间我们换了一行可是输入还在继续,直到读到’$’符号的时候,getline才结束,且不会把‘$’字符读进去。

但是有一点一定要特别注意:如果我们输入的字符串长度超过num-1,就会发生流对象错误,拒绝IO访问。所以遇到流对象发生错误的时候我们要有所处理:

首先,我们需要纠正流(复位),用到.clear()方法。接着,我们还需要清理缓冲区,用到.ignore()方法。特别需要注意ignore的用法:

istream& ignore( streamsize num=1, int delim=EOF )

我们先来看看这种流对象错误,并且是不是真正的拒绝了IO访问。

[cpp] view
plaincopy

#include <iostream>

using namespace std;

int main()

{

char str[20];

cout<<"cin="<<cin<<endl;

cin.getline(str,10);

cout<<str<<endl;

cout<<"cin="<<cin<<endl;

int num=100;

cin>>num;

cout<<"num="<<num<<endl;

return 0;

}



cin既然是一个对象,它重载了>>运算符,我们就可以输出cin的值看看。我们发现一开始cin的值正常的,可是一旦我们getline的字符串超出了10个字符,cin流就发生错误,值变为了0。并且cin>>num操作也没有进行(拒绝了输入操作),直接输出num的值(num值没有输入,因此不会改变)。

我们再来更正这个程序:

[cpp] view
plaincopy

#include <iostream>

using namespace std;

int main()

{

char str[20];

cout<<"cin="<<cin<<endl;

cin.getline(str,10);

cout<<str<<endl;

cout<<"cin="<<cin<<endl;

if(!cin){

cin.clear();

cin.ignore(100,'\n');

}

cout<<"cin="<<cin<<endl;

int num=100;

cin>>num;

cout<<"num="<<num<<endl;

return 0;

}



在getline超出10个字符后cin的值变为0,我们通过纠正cin流并清除输入缓冲区,cin的值又变为正常的值了,最后输入num的值使num值发生改变。


二.字符串的IO类

C中你可能用过sprintf和sscanf这样的格式化字符串操作(不懂的自己百度学习哦)。我们可以一起写个程序回顾一下这两个函数:

[cpp] view
plaincopy

#include <stdio.h>

int main()

{

char str[100];

char name[5]="zm";

int age=20;

double height=1.75;

sprintf(str,"%s:%d:%lf",name,age,height);

printf("%s\n",str);



int num;

sscanf("12345","%d",&num);

printf("%d\n",num);

return 0;

}



在c++中对应的两个类分别是istringstream,ostringstream。因为c++中使用string代替字符数组,所以一般字符串类型都是在操作string。ostringstream是将数据写入字符串,istringstream是把数据从字符串中读出

看代码就很快学会了:

[cpp] view
plaincopy

#include <iostream>

#include <sstream>

#include <iomanip>

using namespace std;

class Date{

int year;

int month;

int day;

public:

Date(int year=0,int month=0,int day=0)

:year(year),month(month),day(day){}

friend ostream& operator<<(ostream&,const Date&);

};

ostream& operator<<(ostream& os,const Date& date){

return os<<setfill('0')<<date.year<<"-"

<<setw(2)<<date.month<<"-"<<setw(2)<<date.day;

}

int main()

{

string name="zm";

int age=20;

double height=1.75;

ostringstream ostr1;

ostr1<<name<<":"<<age<<":"<<height;

string str = ostr1.str();

cout<<str<<endl;



ostringstream ostr2;

ostr2<<Date(2015,2,6);

str=ostr2.str();

cout<<str<<endl;



istringstream istr("zhc 21 1.76");

istr>>name>>age>>height;

cout<<"姓名:"<<name<<endl;

cout<<"年龄:"<<age<<endl;

cout<<"身高:"<<height<<endl;

return 0;

}




ostringstream类的ostr1用来拼接name(string),age(int),height(double),像普通流对象一样的使用<<运算符(注意观察和sprintf的区别)。.str()方法,是将stringstream对象转换为string字符串。ostr2对象是用来是输出类Date的,我们在Date类中重载了运算符>>,所以ostringstream对象用起来一样没有问题。

最后我们定义了istringstream类对象istr,用来从字符串“zhc 21 1.76”读出name,age,height(注意和sscanf的区别,<<会自动识别类型)。



三.文件操作


C++中我们使用ifstream创建文件读取流(读文件),用ofstream创建文件输出流(写文件):

ifstream( const char *filename, openmode mode );

ofstream( const char *filename, openmode mode );

第二个参数mode,是指打开文件的模式。基本的打开模式如下图:





我们使用write方法写文件,read方法读文件:

istream& read( char* buffer, streamsize num );

ostream& write( const char* buffer, streamsize num );

我们经常还会用到:int gcount(); 获取实际读取的字节数。

文件操作基本和c类似,基本知识还不是很熟悉的自己学哦(百度),我只是讲讲c++中怎么操作而已。

我们一起来做一道题,把一个结构体整体为单位写入文件中,再从文件中读取出来:

[cpp] view
plaincopy

#include <iostream>

#include <fstream>

using namespace std;

struct Date{

int year;

int month;

int day;

Date(int year=0,int month=0,int day=0)

:year(year),month(month),day(day){}

void show(){

cout<<year<<"-"<<month<<"-"<<day<<endl;

}

};

int main()

{

ofstream ofs("a.txt");

Date date1(2015,2,6);

ofs.write((const char*)&date1,sizeof(Date));

ofs.close();



ifstream ifs("a.txt");

Date date2;

ifs.read((char*)&date2,sizeof(Date));

ifs.close();

date2.show();

return 0;

}



我们可以看见,我们先把结构体强制类型转换为const char*后写入文件中(按字节写入),最后再用char* 方式从文件中读出来(按字节读)。大家一定要体会,用类的方法去操作文件,原来使用的什么wirte、open都是文件流的成员函数。



最后,我们再综上所有的知识点做一道题目:现在有一个配置文件(如下图),从文件中读出各参数的值。




我们一起来看代码:

[cpp] view
plaincopy

#include <iostream>

#include <fstream>

using namespace std;

int main()

{

string name;

int price;

string cpu;

double size;

ifstream ifs("xiaomi.txt");

char temp[20];

ifs.getline(temp,20,'=');

ifs>>name;

ifs.getline(temp,20,'=');

ifs>>price;

ifs.getline(temp,20,'=');

ifs>>cpu;

ifs.getline(temp,20,'=');

ifs>>size;

cout<<name<<endl;

cout<<"价格:"<<price<<"元"<<endl;

cout<<"CPU:"<<cpu<<endl;

cout<<"屏幕大小:"<<size<<endl;

ifs.close();

return 0;

}



运用>>运算符自动识别类型的特性,我们很轻松读取出来了所有值。注意本程序中getline的用法,遇到”=”停止。



————————————————结束语———————————————————

C++中的IO和文件操作,就是用类的思想,把所有以前c中使用的各种函数全部转换为成员函数。大家一定要体会,先建立IO流和文件流再操作的思想。

总结一下:我们主要是讲了c++中基本IO、字符串IO和文件操作的方式,体验和c中这些操作的区别。学会用面向对象(类)的思维去体会这些操作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: