1、第十一章第十一章 流类库与输入流类库与输入/输出输出清华大学清华大学 郑郑 莉莉C+语言程序设计C+语言程序设计清华大学 郑莉2本章主要内容本章主要内容lI/O流的概念流的概念l输出流输出流l输入流输入流l输入输入/输出流输出流C+语言程序设计清华大学 郑莉3I/O流的概念流的概念l当程序与外界环境进行信息交换时,存在着两个当程序与外界环境进行信息交换时,存在着两个对象,一个是对象,一个是程序中的对象程序中的对象,另一个是,另一个是文件对象文件对象。l流是一种抽象,它负责在数据的流是一种抽象,它负责在数据的生产者生产者和数据的和数据的消费者消费者之间建立联系,并管理数据的流动。之间建立联系,并
2、管理数据的流动。l程序建立一个程序建立一个流对象流对象,并指定这个流对象与某个,并指定这个流对象与某个文件对象建立连接,程序操作流对象,流对象通文件对象建立连接,程序操作流对象,流对象通过文件系统对所连接的文件对象产生作用。过文件系统对所连接的文件对象产生作用。l读操作在流数据抽象中被称为(从流中)读操作在流数据抽象中被称为(从流中)提取提取,写操作被称为(向流中)写操作被称为(向流中)插入插入。C+语言程序设计清华大学 郑莉4输出流输出流l最重要的三个输出流是最重要的三个输出流是 ostream ofstream ostringstreamC+语言程序设计清华大学 郑莉5输出流对象输出流对象
3、l预先定义的输出流对象预先定义的输出流对象:cout 标准输出 cerr 标准错误输出,没有缓冲,发送给它的内容立即被输出。clog 类似于cerr,但是有缓冲,缓冲区满时被输出。输出流C+语言程序设计清华大学 郑莉6输出流对象输出流对象lofstreamofstream类支持磁盘文件输出类支持磁盘文件输出l如果在构造函数中指定一个文件名,当构造这个文件如果在构造函数中指定一个文件名,当构造这个文件时该文件是自动打开的时该文件是自动打开的ofstream myFile(filename);ofstream myFile(filename);l可以在调用默认构造函数之后使用可以在调用默认构造函数
4、之后使用openopen成员函数打开成员函数打开文件文件ofstream myFile;/ofstream myFile;/声明一个静态文件输出流对象声明一个静态文件输出流对象myFile.open(filename);myFile.open(filename);/打开文件,使流对象与文件建立联系打开文件,使流对象与文件建立联系l在构造对象或用在构造对象或用openopen打开文件时可以指定模式打开文件时可以指定模式ofstream myFile(filename,ios_base:out|ofstream myFile(filename,ios_base:out|ios_base:binar
5、y);ios_base:binary);输出流C+语言程序设计清华大学 郑莉7插入运算符(插入运算符()l插入插入()运算符是所有运算符是所有标准标准C+数据数据类型预先设计的。类型预先设计的。l用于传送字节到一个输出流对象。用于传送字节到一个输出流对象。输出流C+语言程序设计清华大学 郑莉8控制输出格式控制输出格式l控制输出宽度控制输出宽度为了调整输出,可以通过在流中放入setw操纵符或调用width成员函数为每个项指定输出宽度。l例例11-1 11-1 使用使用widthwidth控制输出宽度控制输出宽度#include using namesoace std;int main()doub
6、le values=1.23,35.36,653.7,4358.24;for(int i=0;i 4;i+)cout.width(10);cout valuesi endl;return 0;输出流输出结果输出结果:1.23 1.23 35.36 35.36 653.7 653.7 4358.24 4358.24C+语言程序设计清华大学 郑莉9例:使用例:使用*填充填充#include#include using namespace std;using namespace std;int main()int main()double values=1.23,35.36,653.7,4358.2
7、4;double values=1.23,35.36,653.7,4358.24;for(int i=0;i 4;i+)for(int i=0;i 4;i+)cout.width(10);cout.width(10);cout.fill(cout.fill(*););cout valuesi n;cout valuesi n;return 0;return 0;输出流输出结果:输出结果:*1.231.23*35.3635.36*653.7653.7*4358.244358.24C+语言程序设计清华大学 郑莉10例例11-2使用使用setw指定宽度指定宽度#include#include#inc
8、lude#include#include#include using namespace std;using namespace std;intint main()main()double values=1.23,35.36,653.7,4358.24;double values=1.23,35.36,653.7,4358.24;string names=string names=ZootZoot,Jimmy,Al,Jimmy,Al,Stan;Stan;for(for(intint i i=0;=0;i i 4;4;i i+)+)coutcout setwsetw(6)names(6)name
9、si i setwsetw(10)values(10)valuesi i endlendl;return 0;return 0;输出流输出结果:输出结果:Zoot 1.23Zoot 1.23 Jimmy 35.36 Jimmy 35.36 Al 653.7 Al 653.7 Stan 4358.24 Stan 4358.24C+语言程序设计清华大学 郑莉11例例11-3设置对齐方式设置对齐方式#include#include#include#include#include#include using namespace std;using namespace std;intint main()
10、main()double values=1.23,35.36,653.7,4358.24;double values=1.23,35.36,653.7,4358.24;string names=string names=ZootZoot,Jimmy,Al,Stan;,Jimmy,Al,Stan;for(for(intint i i=0;i4;i+)=0;i4;i+)coutcout setiosflagssetiosflags(ios_baseios_base:left):left)setwsetw(6)names(6)namesi i resetiosflagsresetiosflags(i
11、os_baseios_base:left):left)setwsetw(10)values(10)valuesi i endlendl;return 0;return 0;输出流输出结果输出结果:Zoot 1.23Zoot 1.23Jimmy 35.36Jimmy 35.36Al 653.7Al 653.7Stan 4358.24Stan 4358.24C+语言程序设计清华大学 郑莉12例例11-4控制输出精度控制输出精度#include#include#include#include#include#include using namespace std;using namespace st
12、d;intint main()main()double values=1.23,35.36,653.7,4358.24;double values=1.23,35.36,653.7,4358.24;string names=string names=ZootZoot,Jimmy,Al,Stan;,Jimmy,Al,Stan;for(for(intint i i=0;i4;i+)=0;i4;i+)coutcout setiosflagssetiosflags(ios_baseios_base:left):left)setwsetw(6)names(6)namesi i resetiosflags
13、resetiosflags(ios_baseios_base:left):left)setwsetw(10)(10)setprecisionsetprecision(1)values(1)valuesi i endlendl;return 0;return 0;输出流输出结果:输出结果:Zoot 1Zoot 1Jimmy 4e+001Jimmy 4e+001Al 7e+002Al 7e+002Stan 4e+003Stan 4e+003C+语言程序设计清华大学 郑莉13进制进制dec、oct和和hex操纵符设置输入和输出操纵符设置输入和输出的默认进制。的默认进制。输出流C+语言程序设计清华大学
14、 郑莉14文件输出流成员函数文件输出流成员函数l输出流成员函数有三种类型:输出流成员函数有三种类型:与操纵符等价的成员函数。执行非格式化写操作的成员函数。其它修改流状态且不同于操纵符或插入运算符的成员函数。输出流C+语言程序设计清华大学 郑莉15文件输出流成员函数文件输出流成员函数lopen函数函数把流与一个特定的磁盘文件关联起来。需要指定打开模式。lput函数函数把一个字符写到输出流中。lwrite函数函数把内存中的一块内容写到一个文件输出流中lseekp和和tellp函数函数操作文件流的内部指针lclose函数函数关闭与一个文件输出流关联的磁盘文件l错误处理函数错误处理函数在写到一个流时进
15、行错误处理输出流C+语言程序设计清华大学 郑莉16例例11-5向文件输出向文件输出#include#include using namespace std;using namespace std;structstruct Date Date intint mondymondy,day,year;,day,year;intint main()main()Date Date dtdt=6,10,92;=6,10,92;ofstreamofstream file(date.dat,file(date.dat,ios_baseios_base:binary);:binary);file.writefi
16、le.write(reinterpret_castreinterpret_castchar(&(&dtdt),),sizeofsizeof(dtdt););file.closefile.close();();return 0;return 0;输出流C+语言程序设计清华大学 郑莉17二进制输出文件二进制输出文件l以通常方式构造一个流,然后使用以通常方式构造一个流,然后使用setmodesetmode成员函数,在文件打开后改成员函数,在文件打开后改变模式。变模式。l使用使用ofstreamofstream构造函数中的模式参量构造函数中的模式参量指定二进制输出模式指定二进制输出模式输出流C+语言程
17、序设计清华大学 郑莉字符串输出流字符串输出流ostringstreaml用于构造字符串用于构造字符串l功能功能 支持ofstream类的除open、close外的所有操作 str函数可以返回当前已构造的字符串l典型应用典型应用 将数值转换为字符串18输出流C+语言程序设计清华大学 郑莉19例例11-6 字符串输出流应用字符串输出流应用#include#include#include#include#include#include using namespace std;using namespace std;template template inline string inline stri
18、ng toStringtoString(const T&v)(const T&v)ostringstreamostringstream osos;/创建字符串输出流创建字符串输出流osos v;v;/将变量将变量v v的值写入字符串流的值写入字符串流return os.str();return os.str();/返回输出流生成的字符串返回输出流生成的字符串 intint main()main()string str1=string str1=toStringtoString(5);(5);coutcout str1 str1 endlendl;string str2=string str2=
19、toStringtoString(1.2);(1.2);coutcout str2 str2)l提取运算符提取运算符()对于所有标准对于所有标准C+数数据类型都是预先设计好的。据类型都是预先设计好的。l是从一个输入流对象获取字节最容易是从一个输入流对象获取字节最容易的方法。的方法。lios类中的很多操纵符都可以应用于输类中的很多操纵符都可以应用于输入流。但是只有少数几个对输入流对入流。但是只有少数几个对输入流对象具有实际影响,其中最重要的是进象具有实际影响,其中最重要的是进制操纵符制操纵符dec、oct和和hex。输入流C+语言程序设计清华大学 郑莉23输入流成员函数输入流成员函数lopen函
20、数把该流与一个特定磁盘文件相关联。函数把该流与一个特定磁盘文件相关联。lget函数的功能与提取运算符(函数的功能与提取运算符()很相像,主要的)很相像,主要的不同点是不同点是get函数在读入数据时包括空白字符。(第函数在读入数据时包括空白字符。(第6章介绍过)章介绍过)lgetline的功能是从输入流中读取多个字符,并且允许的功能是从输入流中读取多个字符,并且允许指定输入终止字符,读取完成后,从读取的内容中删指定输入终止字符,读取完成后,从读取的内容中删除终止字符。(第除终止字符。(第6章介绍过)章介绍过)lread成员函数从一个文件读字节到一个指定的内存区成员函数从一个文件读字节到一个指定的
21、内存区域,由长度参数确定要读的字节数。域,由长度参数确定要读的字节数。如果给出长度参数,当遇到文件结束或者在文本模式如果给出长度参数,当遇到文件结束或者在文本模式文件中遇到文件结束标记字符时结束读取。文件中遇到文件结束标记字符时结束读取。输入流C+语言程序设计清华大学 郑莉24输入流成员函数输入流成员函数lseekg函数用来设置文件输入流中读函数用来设置文件输入流中读取数据位置的指针。取数据位置的指针。ltellg函数返回当前文件读指针的位置。函数返回当前文件读指针的位置。lclose函数关闭与一个文件输入流关函数关闭与一个文件输入流关联的磁盘文件。联的磁盘文件。输入流C+语言程序设计清华大学
22、 郑莉25例例11-9 从文件读二进制记录从文件读二进制记录#include#include#include#include#include#include using namespace std;using namespace std;struct SalaryInfo struct SalaryInfo unsigned id;unsigned id;double salary;double salary;int main()int main()SalaryInfo employee1=600001,8000;SalaryInfo employee1=600001,8000;ofstrea
23、m os(payroll,ios_base:out|ofstream os(payroll,ios_base:out|ios_base:binary);ios_base:binary);os.write(reinterpret_castchar os.write(reinterpret_cast(&employee1),(&employee1),sizeof(employee1);sizeof(employee1);os.close();os.close();输入流C+语言程序设计清华大学 郑莉26ifstream is(payroll,ios_base:in|ios_base:binary)
24、;ifstream is(payroll,ios_base:in|ios_base:binary);if(is)if(is)SalaryInfo employee2;SalaryInfo employee2;is.read(reinterpret_castchar is.read(reinterpret_cast(&employee2),(&employee2),sizeof(employee2);sizeof(employee2);cout employee2.id employee2.salary endl;cout employee2.id employee2.salary endl;e
25、lse else cout ERROR:Cannot open file payroll.endl;cout ERROR:Cannot open file payroll.endl;is.close();is.close();return 0;return 0;输入流C+语言程序设计清华大学 郑莉27例例11-10 设置位置指针设置位置指针#include#include#include#include using namespace std;using namespace std;intint main()main()intint values=3,7,0,5,4;values=3,7,0,
26、5,4;ofstreamofstream osos(integers,(integers,ios_baseios_base:out|:out|ios_baseios_base:binary);:binary);os.writeos.write(reinterpret_castreinterpret_castchar(values),(values),sizeofsizeof(values);(values);os.closeos.close();();ifstreamifstream is(integers,is(integers,ios_baseios_base:in|:in|ios_bas
27、eios_base:binary);:binary);if(is)if(is)is.seekgis.seekg(3(3*sizeofsizeof(intint););intint v;v;is.readis.read(reinterpret_castreinterpret_castchar(&v),(&v),sizeofsizeof(intint););coutcout The 4th integer in the file integers is The 4th integer in the file integers is v v endlendl;else elsecoutcout ER
28、ROR:Cannot open file integers.ERROR:Cannot open file integers.endlendl;return 0;return 0;输入流C+语言程序设计清华大学 郑莉28例例11-11 读文件并显示其中空格的位置读文件并显示其中空格的位置#include#include#include#include using namespace std;using namespace std;intint main()main()ifstreamifstream file(integers,file(integers,ios_baseios_base:in|
29、:in|ios_baseios_base:binary);:binary);if(file)if(file)while(file)while(file)streamposstreampos here=here=file.tellgfile.tellg();();intint v;v;file.readfile.read(reinterpret_castreinterpret_castchar(&v),(&v),sizeofsizeof(intint););if(file&v=0)if(file&v=0)coutcout Position here is 0 Position here is 0
30、 endlendl;else else coutcout ERROR:Cannot open file integers.ERROR:Cannot open file integers.endlendl;file.closefile.close();();return 0;return 0;输入流C+语言程序设计清华大学 郑莉字符串输入流字符串输入流istringstreaml用于从字符串读取数据用于从字符串读取数据l在构造函数中设置要读取的字符串在构造函数中设置要读取的字符串l功能功能 支持ifstream类的除open、close外的所有操作l典型应用典型应用 将字符串转换为数值29输入流
31、C+语言程序设计清华大学 郑莉例例11-12 字符串输入流应用字符串输入流应用30#include#include#include#include#include#include using namespace std;using namespace std;template template inline T inline T fromStringfromString(const string&(const string&strstr)istringstreamistringstream is(is(strstr););/创建字符串输入流创建字符串输入流T v;T v;is v;is v;/
32、从字符串输入流中读取变量从字符串输入流中读取变量v vreturn v;return v;/返回变量返回变量v v intint main()main()intint v1=v1=fromStringfromString(5);(5);coutcout v1 v1 endlendl;double v2=double v2=fromStringfromString(1.2);(1.2);coutcout v2 v2 endlendl;return 0;return 0;输出结果:输出结果:5 51.21.2输入流C+语言程序设计清华大学 郑莉宽字符、宽字符串与宽流宽字符、宽字符串与宽流l普通字符
33、和字符串的缺陷普通字符和字符串的缺陷 一个汉字被拆成两个字符 例:string s=“这是一个中文字符串”;s.size():返回18 s.substr(3,2):得到的结果是“且”s.find(“且”):返回331深度探索C+语言程序设计清华大学 郑莉宽字符与宽字符串宽字符与宽字符串l宽字符:宽字符:wchar_t类型类型 一般占2个字节,可以直接存下一个汉字 宽字符的文字以“L”开头,例:lwchar_t c=L人;l宽字符串:宽字符串:wstring类型类型 与string同源ltypedef basic_string string;ltypedef basic_string wstri
34、ng;例lwstring s=L这是一个中文字符串;ls.size():返回932深度探索C+语言程序设计清华大学 郑莉宽流宽流l宽流:以宽字符为基本单位的流宽流:以宽字符为基本单位的流 wistream、wifstream、wistringstream、wostream、wofstream、wostringstream、wios wcin、wcout、wcerr、wclogl宽字符和宽字符串需要通过宽流输入输出宽字符和宽字符串需要通过宽流输入输出l宽流与普通流一一对应,彼此同源宽流与普通流一一对应,彼此同源 typedef basic_ifstream ifstream;typedef ba
35、sic_ifstream wifstream;33深度探索C+语言程序设计清华大学 郑莉为宽文件流配置编码方案为宽文件流配置编码方案l文件以字节为单位,编码方案决定了宽字符文件以字节为单位,编码方案决定了宽字符和字节的对应关系和字节的对应关系 例:L“ABCD”占4个字节,L“甲乙丙丁”占8个字节,这由编码方案体现l配置方法:配置方法:用“代码页”编号构造locale对象 执行流的imbue成员函数l示例示例 locale loc(.936);/创建本地化配置方案对象 wcout.imbue(loc);/设置wcout对象的编码方案 wcout L这是一个中文字符串 endl;/输出字符串34
36、深度探索C+语言程序设计清华大学 郑莉例例11-14宽流的应用宽流的应用35#include#include#include#include#include#include#include#include using namespace std;using namespace std;intint main()main()locale loc(.936);locale loc(.936);/创建本地化配置方案创建本地化配置方案wcout.imbuewcout.imbue(loc);(loc);/为为wcoutwcout设置编码方案设置编码方案wifstreamwifstream in(“art
37、icle.txt”);/in(“article.txt”);/创建文件宽输入流,打开文件创建文件宽输入流,打开文件in.imbuein.imbue(loc);(loc);/为为inin设置编码方案设置编码方案wstringwstring line;line;/用来存储一行内容用来存储一行内容unsigned number=0;unsigned number=0;/记录行号记录行号while(while(getlinegetline(in,line)(in,line)number+;number+;/行号加行号加1 1if(if(line.find_first_ofline.find_first
38、_of(L(L人人)!=)!=wstringwstring:nposnpos)wcoutwcout number L“:”line number L“:”line endlendl;return 0;return 0;深度探索C+语言程序设计清华大学 郑莉对象的串行化对象的串行化l串行化:将对象写入文件,使得在适当串行化:将对象写入文件,使得在适当的时候对象能从文件中读出并恢复的时候对象能从文件中读出并恢复l直接用直接用write将对象内容输出、用将对象内容输出、用read将将对象恢复的问题对象恢复的问题 对象中存在指针时,指针所指对象内容不会被保存;对象的成员本身可能是存在指针的对象;对象不
39、仅是数据的集合,还包括一系列行为,用read只能恢复数据,不能触发相应行为36深度探索C+语言程序设计清华大学 郑莉串行化的基本方法串行化的基本方法l手工串行化的基本方法手工串行化的基本方法 手工编写save和load函数 按照相同的顺序保存/恢复数据成员 碰到指针时,首先保存指针是否为空的标志,如非空,将指针对象的内容保存,load做相反操作l完全手工编写串行化函数的困境完全手工编写串行化函数的困境 save和load对成员的操作顺序完全相同,存在逻辑上的重复 处理指针等操作过于繁琐37深度探索C+语言程序设计清华大学 郑莉boost:serializationl用用Serializatio
40、n库将下列结构体串行化:库将下列结构体串行化:struct SalaryInfo string name;double salary;TaxInfo*tax;l只需增加一个成员函数模板(需要只需增加一个成员函数模板(需要TaxInfo也实也实现了同样的成员函数模板):现了同样的成员函数模板):template void SalaryInfo:serialize(Archive&ar,unsigned int version)ar&name&salary&tax;38深度探索C+语言程序设计清华大学 郑莉理解理解Serializationlserialize函数函数 serialize是模板,串
41、行化和恢复都通过这一段源代码“&”被Serialization重载了,能够处理各种基本数据类型、标准库类型“&”碰到指针时,如果指针的目的类型也有serialize成员函数,则用该函数将指针内容串行化/恢复39深度探索C+语言程序设计清华大学 郑莉Serialization的文档类的文档类l文档类文档类 用于实际执行串行化操作l支持支持5种串行化格式种串行化格式 普通文本:text_oachive/text_iachive 宽文本:text_woachive/text_wiachive 普通字符XML:xml_oachive/xml_iachive 宽字符XML:xml_woachive/xm
42、l_wiachive 二进制:binary_oachive/binary_iachive40深度探索C+语言程序设计清华大学 郑莉文档类的使用文档类的使用l保存对象:用保存对象:用“”ofstream ofs(salary.txt,ios_base:out);text_oarchive oa(ofs);oa”ifstream ifs(salary.txt,ios_base:in);text_iarchive ia(ifs);SalaryInfo s2;ia s2;41深度探索C+语言程序设计清华大学 郑莉Serialization的其它功能的其它功能lSerialization的其它功能的其它功能 可以进行版本控制 全面支持对STL容器的串行化 允许将serialize分开定义为两个不同的模板(save和load)进行“对象追踪”,如有两个指针指向同一对象,它能保证这个对象只被串行化一次,而且恢复时也只生成一个对象42深度探索C+语言程序设计清华大学 郑莉43小结与复习建议小结与复习建议l主要内容主要内容 I/O流的概念、输出流、输入流、输入/输出流。l达到的目标达到的目标 理解I/O流的概念,学会使用I/O流类库实现文件输入/输出及格式控制。l实验任务实验任务 实验十一