580 likes | 742 Views
流类库与输入/输出. 本讲主要内容. I/O 流的概念 输出流 输入流. 为什么要有 C++ 流类库. C 语言的输入输出系统不支持拥护自定义的对象或数据类型,如一定义: struct sample{ int i ; float f; char *ch; }s1; 不能用输出函数来输出以上结构变量: printf(“%sample”,s1);. I/O 流的概念. 为什么要有 C++ 流类库.
E N D
本讲主要内容 • I/O流的概念 • 输出流 • 输入流
为什么要有C++流类库 • C语言的输入输出系统不支持拥护自定义的对象或数据类型,如一定义: struct sample{ int i ; float f; char *ch; }s1; • 不能用输出函数来输出以上结构变量: printf(“%sample”,s1); I/O流的概念
为什么要有C++流类库 • C++中,用户自定义的对象很多。C++的类机制允许它可以建立一个可控制的输入输出系统。可以通过重载“<<”和“>>”算符来解决以上的问题。 I/O流的概念
流类库和流 • 流 • 在数据从一个对象流动到另一个对象的过程中,无论是否对数据进行缓冲或对数据进行格式变换,这种流动都被抽象为流. • 每个流都和一种与设备相联系。 • 与输入设备(如键盘)联系的流为输入流;与输出设备(如屏幕)联系的流为输出流。 • C++中预定义了标准输入流cin、标准输出流cout、非缓冲的出错流cerr和缓冲的出错流clog。 I/O流的概念
流类库和流 • 在缺省情况下,标准输出设备是显示终端,标准输入设备是键盘。 • 在任何情况下,标准错误输出设备总是显示终端。 • 标准输出设备可以不是显示终端;但错误信息总是在显示屏幕上显示。 • 流在使用前要被建立,在使用后要被删除,使用特定的操作能够从流中获取数据(称为提取操作),或向流中添加数据(称为插入操作). I/O流的概念
输出流 • 输出流对象是信息流动的目标. ostream, ofstream和ostrstream是三个最重要的输出流. • ostream类通过派生类ostream_withassign支持预先定义的流对象 • cout 标准输出 • cerr 标准错误输出,没有缓冲,发给它的内容立即输出 • clog 类似于cerr,但有缓冲,缓冲区满时被输出. • ofstream类支持磁盘文件输出. • ostrstream类支持输出字符串。
文件 在磁盘上保存的信息是按文件的形式组织的,每个文件都对应一个文件名,并且属于某个物理盘或逻辑盘的目录层次结构中一个确定的目录之下。 一个文件名由文件主名和扩展名两部分组成,它们之间用圆点(即小数点)分开,扩展名可以省略,当省略时也要省略掉前面的圆点。文件主名是由用户命名的一个有效的C++标识符,为了同其他软件系统兼容,一般让文件主名为不超过8个有效字符的标识符,同时为了便于记忆和使用,最好使文件主名的含义与所存的文件内容相一致。 输出流
文件扩展名也是由用户命名的、1至3个字符组成的、有效的C++标识符,通常用它来区分文件的类型。 如在C++系统中,用扩展名h表示头文件,用扩展名cpp表示程序文件,用obj表示程序文件被编译后生成的目标文件,用exe表示连接整个程序中所有目标文件后生成的可执行文件。 对于用户建立的用于保存数据的文件,通常用dat表示扩展名,若它是由字符构成的文本文件则也用txt作为扩展名,若它是由字节构成的、能够进行随机存取的内部格式文件则可用ran表示扩展名。
在C++程序中使用的保存数据的文件按存储格式分为两种类型,一种为字符格式文件,简称字符文件,另一种为内部格式文件,简称字节文件。字符文件又称ASCII码文件或文本文件,字节文件又称二进制文件。 在字符文件中,每个字节单元的内容为字符的ASCII码,被读出后能够直接送到显示器或打印机上显示或打印出对应的字符,供人们直接阅读。 在字节文件中,文件内容是数据的内部表示,是从内存中直接复制过来的。当然对于字符信息,数据的内部表示就是ASCII码表示,所以在字符文件和在字节文件中保存的字符信息没有差别,但对于数值信息,数据的内部表示和ASCII码表示截然不同,所以在字符文件和在字节文件中保存的数值信息也截然不同。
要在程序中使用文件时,首先要在开始包含#include<fstream.h>命令。由它提供的输入文件流类ifstream、输出文件流类ofstream和输入输出文件流类fstream定义用户所需要的文件流对象,然后利用该对象调用相应类中的open成员函数,按照一定的打开方式打开一个文件。文件被打开后,就可以通过流对象访问它了,访问结束后再通过流对象关闭它。
文件 • 在C++中,要进行文件的输入输出操作,应该: • 先创建一个流对象,如构造一个ofstream类的对象,以便进行文件的输出 • 打开文件,就是将这个流对象和具体的文件相关联 • 进行文件的读写操作 • 关闭文件 输出流
构造输出流对象和打开文件 • 如果输出到标准输出设备,即使用预先定义的cout、cerr、clog对象,则不需要构造流对象。 • 要将信息输出到文件,就要构造和建立流对象。 • 可以先构造对象,再打开文件;也可以在构造对象时,同时打开文件。 输出流
构造输出流对象和打开文件 • 使用缺省构造函数,再调用ofstream类的open成员函数. • 打开文件时要指定和对象关联文件的文件名,文件的打开方式(iosmode): • ofstream 对象名; • 对象名.open(“文件名”,iosmode); • 如:ofstream outfile; • outfile.open(“data.dat”,ios::binary); 输出流
构造输出流对象和打开文件 • 也可使用指针,即指向输出文件流对象的指针,通过指针调用open函数: ofstream *pmyFile = new ofstream; pmyFile->open(“filename”,iosmode); 输出流
构造输出流对象和打开文件 • 也可以一次操作就完成构造输出流对象和打开文件。即只调用构造函数,而不使用open函数。 • 在调用构造函数时指定文件名和模式 ofstream 对象名(“文件名”,iosmode); 如:ofstream outfile (“data.dat”,ios;;binary); (常用方式) 输出流
使用插入运算符和控制格式 • 插入运算符<<是预先设计的,用来将数据输出到一个流对象。 • 插入运算符与预先定义的操纵符一起工作,用来控制输出格式 • 通过已定义的操纵符可以控制输出宽度、输出格式、输出精度等。 输出流
控制输出宽度 • 可使用setw操纵符或width成员函数来控制每个输出项的输出宽度: #include<iostream.h> void main( ) { doubl evalues[]={1.23,35,36,653.7,4358.24}; for (int i=0;i<4;i++) { cout.width(10); cout<<values[I]<<'\n'; } } 输出流
控制输出宽度 • 用setw操纵符来控制输出宽度: #include<iostream.h> #include <iomanip.h> void main( ) { doubl evalues[]={1.23,35,36,653.7,4358.24}; char *name[]={“Jan”, “Ani”, “Pa”, “Yekan”}; for (int i=0;i<4;i++) cout<<setw(6)<<name[i] <<setw(10)<<values[i]<<'\n'; } • 可以对数据项指定不同的输出宽度。 • 必须调用iomanip.h头文件。 输出流
控制输出格式 • 用setiosflags操纵符来设置输出格式,输出结束后,再用 resetiosflags操纵符取消设置的格式,恢复缺省的格式。 • 具体的格式由格式标志值指定 • 可以设置的格式包括:对齐方式,数据的数制(十进、八进、十六进制)、浮点数显示格式等。 • 格式标志值可以用OR运算(|)进行组合。 输出流
ios::skipws Skip white space on input. ios::left Left-align values; pad on the right with the fill character. ios::right Right-align values; pad on the left with the fill character (default alignment). ios::internal Add fill characters after any leading sign or base indication, but before the value. ios::dec Format numeric values as base 10 (decimal) (default radix). ios::oct Format numeric values as base 8 (octal). ios::hex Format numeric values as base 16 (hexadecimal).
ios::showbase Display numeric constants in a format that can be read by the C++ compiler. ios::showpoint Show decimal point and trailing zeros for floating-point values. ios::uppercase Display uppercase A through F for hexadecimal values and E for scientific values. ios::showpos Show plus signs (+) for positive values. ios::scientific Display floating-point numbers in scientific format. ios::fixed Display floating-point numbers in fixed format. ios::unitbuf Cause ostream::osfx to flush the stream after each insertion. By default, cerr is unit buffered. ios::stdio Cause ostream::osfx to flush stdout and stderr after each insertion.
例 设置对齐方式 #include<iostream.h> #include<iomanip.h> void main( ) { double values[]={1.23, 35.36,653.7,4358.24}; char*names[]={"Zoot", "Jimmy", "Al", "Stan"}; for(int i=0;i<4;i++) cout<<setiosflags(ios::left) //左对齐 <<setw(6)<<names[i] <<resetiosflags(ios::left) //恢复默认 <<setw(10)<<values[I]<<endl; }
控制输出精度 控制输出精度:用setprecision操纵符 #include<iostream.h> #include<iomanip.h> void main( ) { double values[]={1.23,35.36,653.7,4358.24}; char*names[]={"Zoot", "Jimmy", "Al", "Stan"}; for(int i=0;i<4;i++) cout<<setiosflags(ios::left) <<setw(6)<<names[i] <<resetiosflags(ios::left) <<setw(10)<<setprecision(1)<<values[I]<<endl; } 输出流
程序运行结果: • Zoot 1 • Jimmy 4e+001 • Al 7e+002 • Stan 4e+003 • 若改为setprecision(3),即3位有效数字,结果成为: • Zoot 1.23 • Jimmy 35.4 • Al 654 • Stan 4.36e+003 • 原数据为: 1.23, 35.36, 653.7, 4358.24 • 不必使用resetprecision,可直接修改精度。
控制输出数制 • 控制输出进制用dec,oct和hex操纵符 #include<iostream.h> #include<iomanip.h> void main() { cout<<234<<setw(10)<<hex<<234<<endl <<234<<setw(10)<<oct<<234<<endl <<234<<setw(10)<<dec<<234<<endl; } 输出流
程序运行结果: • 234 ea • ea 352 • 352 234
综合示例:ASCII码表 #include <iostream.h> #include <iomanip.h> int main() { int i,j=0; for(i=32;i<127;i++) { cout <<setw(4)<< hex<<i<< 'H'<< setw(2) << char(i); j++; if (j%8==0) cout<<endl; } return 0; }
程序运行结果: • 20H 21H ! 22H " 23H # 24H $ 25H % 26H & 27H ' • 28H ( 29H ) 2aH * 2bH + 2cH , 2dH - 2eH . 2fH / • 30H 0 31H 1 32H 2 33H 3 34H 4 35H 5 36H 6 37H 7 • 38H 8 39H 9 3aH : 3bH ; 3cH < 3dH = 3eH > 3fH ? • 40H @ 41H A 42H B 43H C 44H D 45H E 46H F 47H G • 48H H 49H I 4aH J 4bH K 4cH L 4dH M 4eH N 4fH O • 50H P 51H Q 52H R 53H S 54H T 55H U 56H V 57H W • 58H X 59H Y 5aH Z 5bH [ 5cH \ 5dH ] 5eH ^ 5fH _ • 60H ` 61H a 62H b 63H c 64H d 65H e 66H f 67H g • 68H h 69H i 6aH j 6bH k 6cH l 6dH m 6eH n 6fH o • 70H p 71H q 72H r 73H s 74H t 75H u 76H v 77H w • 78H x 79H y 7aH z 7bH { 7cH | 7dH } 7eH ~
输出文件流成员函数(一) • 输出流成员函数有三种类型 • 1.与操纵符等价的成员函数 • 2. 执行非格式化写操作的成员函数 • 3. 其他修改流状态且不同于操纵符或插入 运算符的成员函数 输出流
Open函数 • 输出流的open函数 • 若要用一个输出文件流,必须在构造函数或open函数中把该流与一个特定的磁盘文件关联起来. • 文件可以在构造流对象时打开,也可以用成员函数open来打开。 • 用open函数打开文件时要指定文件名和打开方式。 输出流
Open函数 • 文件打开方式: • ios::app:输出文件的追加方式 • ios::ate:到输入或输出文件的文件尾 • ios::in: 打开输入文件 • ios::out: 打开输出文件,隐含方式 • ios::nocreate : 只打开已存在的文件 • ios::noreplace : 如果文件已存在则操作失败 • ios::trunc : 打开时替换已存在的同名文件 • ios::binary : 二进制模式打开文件 输出流
文件的读写 • 文件打开后,直接用<<或>>读写,但要用与文件关联的流对象作为目标: #include <fstream.h> int main() { ofstream fout(“data.dat”); if(! fout) {cout<<“Can’t open file test.txt\n”; return 1 } fout<<10<<“ “<<123.456<<“\”This is a file.\”\n”; fout.close(); return 0 }
输出文件流成员函数(二) • 输出流的open函数 • 若要用一个输出文件流,必须在构造函数或open函数中把该流与一个特定的磁盘文件关联起来. • 输出流的put函数 • 把一个字符写到输出流中 • 输出流的write函数 • 把内存中的一块内容写到一个输出文件流中,长度参数指出写的字节数. 输出流
例 输出二进制文件 #include<fstream.h> struct Date { int mo,da,yr; }; //不要忘掉‘;’ void main( ) { Date dt = {6,10,92}; ofstream tfile(“date.dat”,ios::binary); //打开二进制文件 tfile.write((char*)&dt,sizeof dt); }
程序运行结果: • 文件date.dat的内容是: • 06 00 00 00 0A 00 00 00-5C 00 00 00 • 正好是十进制数6、10、92的二进制表示,每个数4个字节。
如果以文本方式打开,程序运行结果: • 文件date.dat的内容是: • 06 00 00 00 0D 0A 00 00-00 5C 00 00 00 • 十进制数10前面增加了一个0D,即换行符的ASCII码,其余不变。可见并不能直接按文本方式输出。而且使10的输出(认为是ASCII码0A)变为回车换行符。
输出文件流成员函数(三) • 输出流的seekp和tellp函数 • 一个输出文件流保存一个内部指针指出下一次写数据的位置.seekp设置这个指针.tellp返回该文件位置. • 输出流的close函数 • close关闭与一个输出文件流关联的磁盘文件. • 输出流的错误处理函数 • bad,fail,good,eof,clear,rdstate. 输出流
二进制输出文件(一) • 流的缺省输出模式是文本模式.以文本模式输出时,遇到10便自动扩充为回车换行符.而使用二进制模式输出时,所写的字符是不转换的. • 使用二进制模式输出到文件的几种方法: • 1.构造一个流,然后用setmode函数. ofstream ofs(“test.dat”); ofs.setmode(filebuf::binary); • 2.使用ofstream构造函数中的模式参量. • 3.使用二进制操作符代替setmode成员函数. ofs<<binary; 输出流
二进制输出文件(二) • 4.使用open函数带一个二进制模式标志打开文件. filedesc fd=open(“test.dat”,OBINARY| OCREAT|OWRONLY); ofstream ofs(fd); ofs.write((char*)iarray,4); //向二进制文件中写入4字节数据 输出流
输入流 • 输入流对象是信息流出的源头.istream, ifstream和istrstream是三个最重要的输入流类. • istream类通过派生类istream_withassign支持预先定义的流对象cin. istream类用于顺序文本模式输入是最好的. • ifstream类支持磁盘文件输入.基类ios和istream的所有功能都包括在ifstream中.
构造输入流对象 • 构造输入文件流的常用方法 • 使用缺省构造函数,再调用open成员函数. ifstream myFile; myFile.open(“filename”,iosmode); ifstream* pmyFile=new ifstream; pmyFile->open(“filename”,iosmode); • 在调用构造函数时指定文件名和模式 ifstream myFile(“filename”,iosmode); 输入流
提取运算符和输入流操纵符 • 提取运算符(>>)是从一个输入流对象获取字节的最容易方法. • >>用于格式化文本输入,提取数据时以空白符为分隔. • 成员函数getline可以读一个含有空格的文本块,并对其进行分析. • 前面介绍的输出错误处理函数,同样可应用于输入流. • 实际上,只有dec,oct和hex等几个操纵符对输入流对象具有影响. 输入流
输入文件流成员函数(一) • 输入流成员函数用于从磁盘文件中输入. • 输入流的open函数 • 若要用一个输入文件流,必须在构造函数或open函数中把该流与一个特定的磁盘文件关联起来. • 输入流的get函数 • 功能与“>>”相像.但get函数在读入数据时包括空白字符,而“>>”在缺省时拒绝接受. 输入流
#include<iostream.h> void main( ) { char ch; while ((ch=cin.get())!=EOF) cout.put(ch); } 运行时输入: abc xyz 123 则输出: abc xyz 123 注:先输入到一个缓冲区,再由get读入,put输出。
输入文件流成员函数(二) • 输入流的getline函数 • 允许从输入流中读取多个字符,可以指定输入终止字符,读取完成后,从读取的内容中删除该终止字符. • 输入流的read函数 • 从一个文件读字节到指定的存储器区域,所读字节数由长度参数确定.若没给长度参数,当遇到文件结束时读结束. 输入流
#include<iostream.h> void main( ) { char ch; while ((ch=cin.get())!=EOF) cout.put(ch); } 运行时输入: abc xyz 123 则输出: abc xyz 123 注:先输入到一个缓冲区,再由get读入,put输出。
例 读入一串字符到line数组,以‘t’终止 • #include <iostream.h> • void main() • { • char line[100]; • cout<<“Type a line terminated by ‘t’”<< endl; • cin.getline(line,100,’t’); • cout<<line; • }
例 从一个payroll文件读一个二进制记录到一个结构中 • #include<fstream.h> • #include<fcntl.h> • #include<io.h> • void main( ) • { • struct • { • double salary; • char name[23]; • }employee;
ifstream is ("payroll",ios::binary |ios::nocreate); if(is) { is.read((char*)&employee,sizeof(employee)); cout<<employee.name<<' '<<employee.salary<<endl; } else { cout<<"ERROR:Cannot open file 'payroll'."<<endl; } }