530 likes | 669 Views
第 9 章 C++ 的输入和输出. 9.1 C++ 为何建立自己的输入输出系统. 因为在 C++ 中用户需要定义众多的自定义类型,但是 C 语言的输入输出系统不支持用户自定义的类型。请看下面的类 : class my_class{ int i; float f; char *str; } obj; 对此类类型,在 C 语言中下面的语句是不能接受的 : printf( " % my_class " ,obj);
E N D
9.1 C++为何建立自己的输入输出系统 因为在C++中用户需要定义众多的自定义类型,但是C语言的输入输出系统不支持用户自定义的类型。请看下面的类: class my_class{ int i; float f; char *str; } obj; 对此类类型,在C语言中下面的语句是不能接受的: printf("% my_class",obj); C++的流类比C的输入输出函数具有更大的优越性。
9.2 C++的流库及其基本结构 9.2.1 C++的流 在C++中,流类是为输入输出提供的一组类,它们都放在流库中。 流总是与某一设备相联系的(例如键盘、屏幕或硬盘等),通过使用流类中定义的方法,就可以完成对这些设备的输入输出操作。 流具有方向性:与输入设备(如键盘)相联系的流称为输入流;与输出设备(如屏幕)相联系的流称为输出流;与输入输出设备(如磁盘)相联系的流称为输入输出流。
9.2 C++的流库及其基本结构 C++中包含几个预定义的流对象,: 标准输入流 cin 与标准输入设备相关联 标准输出流 cout 与标准输出设备相关联 非缓冲型的标准出错流 cerr 与标准错误输出设备 相关联(非缓冲方式) 缓冲型的标准出错流 clog 与标准错误输出设备 相关联(缓冲方式) 在缺省情况下,指定的标准输出设备是屏幕,标准输入设备是键盘。
9.2.2 流类库 C++流类库具有两个平行的基类,即streambuf类和ios类,所有其他的流类都是从它们直接或间接地派生出来的 其中: ios类为输入输出操作在用户一方的接口,负责高层操作: streambuf类为输入输出操作在物理设备一方的接口,负责低层操作。
9.3 预定义类型的输入输出 9.3.1无格式输入输出 基于C++类库的输入输出需使用两个流对象cin和cout,还要用与之相配套的两个输入输出运算符“>>”和“<<”,其一般的格式为 cin>>变量; // 输人 cout<<常量或变量; // 输出 cin.operator>>(变量); cout.operator<<(常量或变量);
9.3 预定义类型的输入输出 1. 输出运算符 从运算符角度来看,输出通过输出运算符“<<”来完成的,输出运算符“<<”也称插入运算符,它是一个双目运算符,有两个操作数,左操作数为ostream类的一个对象(如cout),右操作数为一个系统预定义类型的常量或变量。例如 cout<<"This is a string.\n"; 完成的功能为写字符串“This is a string. ”到流对象cout, cout为标准输出流,通常为屏幕。
9.3 预定义类型的输入输出 2. 输入运算符 从运算符角度来看,输入操作通过输入运算符“>>”来完成。输入运算符“>>”也称提取运算符,它也是一个双目运算符,有两个操作数,左面的操作数是istream类的一个对象(cin),右面的操作数是系统预定义的任何数据类型的变量。例如: int x; cin>>x; 此时,用户从键盘输入的数值会自动地转换为变量x的类型,并存入变量x内。
9.3 预定义类型的输入输出 说明: (1)在缺省情况下,运算符“>>”将跳过空白符,然后读入后面与变量类型相对应的值; (2)当输入字符串时,将跳过空白,读入非空白字符,直到遇到另一个空白字符为止; (3)检查输入数据与变量类型匹配情况。 (4)缺省情况可用空白或换行将数值之间分隔。
9.3 预定义类型的输入输出 9.3.2 格式化输入输出 C++提供了两种进行格式控制的方法: 一种是使用ios类中有关格式控制的成员函数进行格式控制; 另一种是使用称为操纵符的特殊类型的函数进行格式控制。
9.3 预定义类型的输入输出 1. 用ios类的成员函数进行格式控制 格式控制主要是通过对状态标志字的操作来完成的。 在ios类的public部分定义了一个枚举,它的每个成员分别定义状态标志字的一个位,每一位都称为一个状态标志位。
9.3 预定义类型的输入输出 enum{ skipws =0x0001, / / 跳过输入中的空白,可用于输出 left =0x0002, // 左对齐输出,可用于输出 right =0x0004, // 右对齐输出,可用于输出 internal =0x0008, // 在符号位和基指示符后填入字符,可用于输出 dec =0x0010, // 转换基制为十进制数,可用于输入或输出 oct =0x0020, // 转换基制为八进制数,可用于输入或输出 hex =0x0040, // 转换基制为十六进制数,可用于输入或输出 showbase =0x0080, // 在输出时显示基指示符,可用于输入或输出 showpoint =0x0100, // 在输出时显示小数点,可用于输出
9.3 预定义类型的输入输出 uppercase =0x0200, //十六进制输出时,表示制式 //和表示数值的字符 一律为大写,可用于输出 showpos =0x0400, //对正整数显示正号,可用于输出 scientific =0x0800, //用科学表示法显示浮点数,可用于输出 fixed =0x1000 , //用定点形式显示浮点数,可用于输出 unitbuf =0x2000, //在输出操作后立即刷新所有流,可用于输出 stdio =0x4000, //在输出操作后刷新stdout和stderr,可用于输出 };
9.3 预定义类型的输入输出 这些枚举元素的值有一个共同的特点:分别使二进制表示的状态标志字的不同位为“1”,如: skipws 0x0001 0000 0000 0000 0001 left 0x0002 0000 0000 0000 0010 right 0x0004 0000 0000 0000 0100
函 数 原 型 功 能 long ios∷setf(long flags); long ios∷unsetf(long flags); long ios∷flags(); long ios∷flags(long flags); int ios∷width(); int ios∷width(int w); int ios∷precision(int p); char ios∷fill(); char ios∷fill(char ch); 设置状态标志flags 清除状态标志,并返回前状态标志 测试状态标志 设置标志flags,并返回前状态标志 返回当前的宽度设置值 设置域宽w,返回以前的设置 设置小数位数p,返回以前的小数位数 返回当前的填充字符 设置填充字符ch,返回当前的填充字符 9.3 预定义类型的输入输出 表9-1 控制输入输出格式的成员函数
9.3 预定义类型的输入输出 (1)设置状态标志 long ios::setf(long flags); 使用时,其一般的调用格式为: 流对象.setf(ios::状态标志) 例9_1 设置状态标志函数的使用。 #include<iostream.h> void main() { cout.setf(ios::showpos|ios::scientific); cout<<567<<" "<<567.89<<endl; } (2)清除标志 long ios::unsetf(long flags) (3)取状态标志 long ios::flags(); long ios::flags(long flag) setf()函数是在原有基础上追加设定,而flags()函数是用新设定覆盖以前的状态标志字
9.3 预定义类型的输入输出 例9_2 取状态标志函数的使用 #include<iostream.h> void showflags(long f) { long i; for(i=0x8000;i;i=i>>1) { if(i&f) cout<<"1"; else cout<<"0"; } cout<<endl; } int main() { long f; f=cout.flags(); showflags(f); cout.setf(ios::showpos|ios::scientific); f=cout.flags(); showflags(f); cout.unsetf(ios::scientific); f=cout.flags(); showflags(f); return 1; }
9.3 预定义类型的输入输出 (4)设置域宽 long ios::width(); long ios::width(int w); 前者用来返回当前域宽值;后者用来设置域宽。注意,所设置的域宽仅对下一个流输出操作有效,操作结束后又恢复为0. (5)填充字符 long ios::fill(); long ios::fill(char ch); 前者返回当前的填充符,后者用ch重新设置填充符,并返回设置前的填充符。 (6)设置显示精度 long ios::precision(int p); 此函数用来重新设置浮点数所需小数的位数,并返回设置前的小数点后位数。
9.3 预定义类型的输入输出 例9_3 成员函数进行格式控制。 #include<iostream.h> main() { cout<<"x_width="<<cout.width()<<endl; cout<<"x_fill="<<cout.fill()<<endl; cout<<"x_precision="<<cout.precision()<<endl; cout<<123<<" "<<123.45678<<endl; cout<<"_________________________________\n"; cout<<"*** x_width=10,x_fill=, x_precision=4 ***\n"; cout.width(10); cout.precision(4); cout<<123<<" "<<123.45678<<" "<<234.567<<endl; cout<<"x_width="<<cout.width()<<endl; cout<<"x_fill="<<cout.fill()<<endl; cout<<"x_precision="<<cout.precision()<<endl;
9.3 预定义类型的输入输出 cout<<"_________________________________\n"; cout<<"*** x_width=10,x_fill=&, x_precision=4 ***\n"; cout.fill('&'); cout.width(10); cout<<123<<" "<<123.45678<<" "<<234.567<<endl; cout.setf(ios::left); cout.width(10); cout<<123<<" "<<123.45678<<" "<<234.567<<endl; cout<<"x_width="<<cout.width()<<endl; cout<<"x_fill="<<cout.fill()<<endl; cout<<"x_precision="<<cout.precision()<<endl; }
9.3 预定义类型的输入输出 程序运行结果如下: x_width=0 x_fill= x_precision=0 123 123.45678 ----------------------------- *** x_width=10, x_fill= , x_precision=4 *** 123 123.4568 234.567 x_width=0 x_fill= x_precision=4 ----------------------------- *** x_width=10, x_fill=&, x_precision=4 *** &&&&&&&123 123.4568 123&&&&&&& 123.4568 x_width=0 x_fill=& x_precision=4
9.3 预定义类型的输入输出 2.使用预定义的操作符进行I/O格式控制 使用ios类中的成员函数进行I/O格式控制时,每个函数的调用需要写一条语句,而且不能将它们直接嵌入I/O语句中,显然使用起来不太方便。 C++提供的预定义操纵符如下: (1) dec以十进制形式输入或输出整型数,可用于输入或输出。 (2) hex以十六进制形式输入或输出整型数,可用于输入或输出。 (3) oct 以八进制形式输入或输出整型数,可用于输入或输出。 (4) ws 用于在输入时跳过前导的空白符,可用于输入。 (5) endl 插入一个换行符并刷新输出流,仅用于输出。 (6) ends 插入一个空字符’\0’,通常用来结束一个字符串,仅用于输出。 (7) flush 刷新一个输出流,仅用于输出。
9.3 预定义类型的输入输出 (8) setbase(int n) 设置转换基格式为为n(n的取值为0、8、10 或16), n的缺省值为0,即表示采用十进制,仅用于输出。 (9) resetiosflags(long f) 关闭由参数f指定的格式标志,可用于输入或输出。 (10) setiosflags(long f) 设置由参数f指定的格式标志,可用于输入或输出。 (11) setfill(int ch) 设置ch为填充字符,缺省时为空格,可用于输入或输出。 (12) setprecision(int n) 设置小数部分的位数,可用于输入或输出。 (13) setw(int n) 设置域宽为n,可用于输入或输出 。 注意:使用以上操作符控制格式输入、输出有写头文件iomanip.h
操作符setiosflags()和resetiosflags()所用的格式标志操作符setiosflags()和resetiosflags()所用的格式标志
9.3 预定义类型的输入输出 例9_4 操纵符的使用。 #include<iostream.h> #include<iomanip.h> main() {cout<<setw(10)<<123<<567<<endl; //① cout<<123<<setiosflags(ios::scientific)<<setw(20) //② <<123.456789<<endl; cout<<123<<setw(10)<<hex<<123<<endl; //③ cout<<123<<setw(10)<<oct<<123<<endl; //④ cout<<123<<setw(10)<<dec<<123<<endl; //⑤ cout<<resetiosflags(ios::scientific)<<setprecision(4) //⑥ <<123.456789<<endl; cout<<setiosflags(ios::left)<<setfill('#')<<setw(8) //⑦ <<123<<endl; cout<<resetiosflags(ios::left)<<setfill('$')<<setw(8) //⑧ <<456<<endl; return 0; }
9.3 预定义类型的输入输出 程序运行结果为: 123567 ① 123 1.234567e+02 ② 123 7b ③ 7b 173 ④ 173 123 ⑤ 123.4568 ⑥ 123##### ⑦ $$$$$456 ⑧
9.4 用户自定义类型的输入输出 若为输出流定义操纵符函数,则定义形式如下: ostream& manip_name(ostream& stream) { … //自定义代码 return stream; }
9.4 用户自定义类型的输入输出 若为输入流定义操纵符函数,则定义形式如下: istream& manip_name(istream& stream) { … //自定义代码 return stream; }
9.4 用户自定义类型的输入输出 例9_5 为输出流定义操纵符函数。 #include<iostream.h> #include<iomanip.h> ostream& output1(ostream& stream) { stream.setf(ios::left); stream<<setw(10)<<hex<<setfill(’&’); return stream; } int main() { cout<<123<<endl; cout<<output1<<123<<endl; return 0; }
9.4 用户自定义类型的输入输出 例9_6 为输入流定义操纵符函数。 #include<iostream.h> #include<iomanip.h> istream& input1(istream& in) { in>>hex; cout<<"Enter number using hex format:"; return in; } int main() { int i; cin>>input1>>i; cout<<"***"<<hex<<i<<"***"<<dec<<i<<"***"<<endl; return 0; }
9.4 用户自定义类型的输入输出 9.4.1 重载输出运算符“<<” 定义输出运算符“<<”重载函数的一般格式如下: ostream& operator<<(ostream& out,class_name& obj) { out<<obj.item1; out<<obj.item2; .. . out<<obj.itemn; return out; }
9.4 用户自定义类型的输入输出 9.4.2 重载输入运算符“>>” 定义输入运算符函数 “>>”重载函数的一般格式如下: istream& operator>>(istream& in,class_name& obj) { in>>obj.item1; in>>obj.item2; . . . in>>obj.itemn; return in; }
例9.7 输出运算符“<<”重载。 #include<iostream.h> class coord { int x,y; public: coord(int i=0,int j=0) { x=i; y=j;} friend ostream& operator<<(ostream& stream,coord& ob); }; ostream& operator<<(ostream& stream,coord& ob) { stream<<ob.x<<","<<ob.y<<endl; return stream; } main() { coord a(55,66), b(100,200); cout<<a<<b; return 0; }
例9.8 输入运算符“>>”重载。 #include<iostream.h> class three_d { public: three_d(int a,int b,int c) { x=a; y=b; z=c; } friend ostream& operator<<(ostream& output,three_d& ob); friend istream& operator>>(istream& itput,three_d& ob); private: int x,y,z; }; ostream& operator<<(ostream& output, three_d& ob) { output<<ob.x<<","; output<<ob.y<<","; output<<ob.z<<endl; return output; }
9.4 用户自定义类型的输入输出 istream& operator>>(istream& input, three_d& ob) { cout<<"Enter x,y,z value:"; input>>ob.x; input>>ob.y; input>>ob.z; return input; } main() { three_d obj(10,20,30); // 定义类three_d的对象obj cout<<obj; // 输出对象obj的成员值 cin>>obj; // 输入对象obj的各成员值,将原值覆盖 cout<<obj; // 输出对象obj的成员值(新值) return 0; }
9.5 文件的输入输出 要执行文件的输入输出,须做以下几件事: (1) 在程序中包含头文件fstream.h; (2) 建立流。建立流的过程就是定义流类的对象,例如: ifstream in; ofstream out; fstream both; 分别定义了输入流对象in;输出流对象out,输入输出流对象both。
9.5 文件的输入输出 (3) 使用open()函数打开文件,也就是使某一文件与上面的某一流相联系。open()函数是上述三个流类的成员函数,其原型是在fstream.h中定义的, 原型为: void open(const unsigned char*,int mode,int access=filebuf::openprot); (4)进行读写,在建立(或打开)的文件上执行所要求的输入或输出操作; (5)关闭文件流对象。
9.5 文件的输入输出 9.5.1 文件的打开与关闭 1.文件的打开 为了对一个文件进行读写操作,应先“打开”该文件;在使用结束后,则应“关闭”文件。在C++中,打开一个文件,就是将这个文件与一个流建立关联;关闭一个文件,就是取消这种关联。
9.5 文件的输入输出 第一个参数是用来传递文件名,第二个参数如下:
9.5 文件的输入输出 第三个参数如下: 0 普通文件 1 只读文件 2 隐含文件 4 系统文件 8 备份文件 例如: ofstream out;out.open(“test”,ios::out,0); out.open(“test”); ofstream out(“test”);
9.5 文件的输入输出 2.文件的关闭 在使用完一个文件后,应该把它关闭。所谓关闭,实际上就是使打开的文件与流“脱钩”。关闭文件可使用close()函数完成,close()函数也是流类中的成员函数,它不带参数,不返回值。 例如: out.close(); 将关闭与流out相联接的文件。
9.5 文件的输入输出 9.5.2 文件的读写 1.文本文件的读写 一旦文件打开了,从文件中读取文本数据与向文件中写入文本数据都十分容易,只需使用运算符“<<”与“>>”就可以了,只是你必须用与文件相联接的流代替cin和cout。
9.5 文件的输入输出 例9.9 把一个整数、一个浮点数和一个字符串写到磁盘文件test中。 #include<fstream.h> int main() { ofstream fout("f:\\ccp\\909\\909.txt"); if (!fout){ cout<<"Cannot open output file\n,"; return 1;} fout<<10<<" "<<123.456<<" "<<"This is a text file.\n"; fout.close(); ifstream fin("f:\\ccp\\909\\909.txt"); if (!fin) { cout<<"Cannot open input file\n,"; return 1;} int i;float f;char str[100]; fin>>i>>f>>str; cout<<i<<"*"<<f<<"*"<<str<<endl; return 0; }
9.5 文件的输入输出 2.二进制文件的读写 (1) 用get()函数和put()函数读写二进制文件 get()函数有许多格式,其中最常用的版本原型如下: istream& get(char& ch); get()函数从相关流中只读一个字节,并把该值放入ch中并返回该流,当到达文件尾时,使该流的值为0。 put()函数的原型如下: ostream& put(char ch); put()函数将ch写入流中并返回该流。
9.5 文件的输入输出 例9.10 将‘a’ 至‘z’ 的26个英文字母写入文件,而后从该文件中读出并显示出来。 #include<iostream.h> #include<fstream.h> void test_write() { ofstream fs("d:\\test.dat"); int i; char c='a'; for (i=0;i<26;i++){ fs.put(c); c++; } fs.close(); } void test_read() { ifstream fs("d:\\test.dat"); char c; while(fs.get(c)) cout<<c; fs.close(); } void main() { test_write(); test_read(); }
9.5 文件的输入输出 (2) 用read()函数和write()函数读写二进制文件 read()和write()原型如下: istream &read(unsigned char* buf,int num); ostream &write(const unsigned char* buf,int num);
例9-11 用write()函数向文件test中写入整数与双精度数。 #include<iostream.h> #include<fstream.h> #include<string.h> main() { ofstream out("f:\\ccp\\911\\test.dat"); if (!out) { cout<<"Cannot open output file.\n"; return 1; } int i=12340; double num=100.45; out.write((char *) &i,sizeof(int)); out.write((char *) &num,sizeof(double)); out.close();ifstream in("f:\\ccp\\911\\test.dat"); if (!in) { cout<<"Cannot open input file.\n"; return 1; } int j;double d; in.read((char *)&j,sizeof(int)); in.read((char *)&d,sizeof(double)); cout<<j<<" "<<d<<endl; in.close();return 0; }
9.5 文件的输入输出 (3) 检测文件结束 在文件结束的地方有一个标志位,记为EOF(End OF)。采用文件流方式读取文件时,使用成员函数eof(),可以检测到这个结束符。如果该函数的返回值非零,表示到达文件尾。为零表示未到达文件尾。该函数的原型是: int eof(); 函数eof()的用法示例如下: ifstream ifs; … if (!ifs.eof()) //尚未到达文件尾 … ifstream ifs; if(ifs)//尚未到达文件尾
9.5 文件的输入输出 (4) 文件的随机读写 在C++的I/O系统中有个文件指针,它有两个名字。 其中一个名字叫get指针,用于指出下一次输入操作的位置; 另一个名字叫做put指针,用于指出下一次输出操作的位置。
9.5 文件的输入输出 put指针: ostream& seekp(streampos pos); ostream& seekp(streamoff off,ios::seek_dir dir); get指针: istream& seekg(streampos pos); //指针直接定位 istream& seekg(streamoff off,ios::seek_dir dir); //指针相对定位 其中,参数dir表示文件指针的起始位置,off表示相对于这个起始位置的位移量。dir的取值有以下3种情况: ①ios::beg 从文件头开始,文件指针移动由off指定的距离。 ②ios::cur 从当前位置开始,文件指针移动由off指定的距离。 ③ios::end 从文件尾开始,文件指针移动由off指定的距离。 例:datafile.seekg(-20L,ios::cur); //表示将文件定位指针从当前位置向文件头部方向移20个字节。