500 likes | 713 Views
大学 C++ 程序设计教程. 西安交通大学 计算机教学实验中心 http://ctec.xjtu.edu.cn. 第 13 章 标准库和输入输出流. 教学目标 1. C++ 标准库 2. 掌握文件的打开和关闭、读和写的操作方法; 3. 掌握文本文件和二进制文件; 4. 格式化 I/O 系统。. 授 课 内 容 . 13.1 标准库概述 13.2 流概述 13.3 输入输出流 13.4 格式化 I/O 13.5 文件处理 13.6 对象的输入 / 输出 13.7 标准模板库( STL )简介. 13.1 标准库概述.
E N D
大学C++程序设计教程 西安交通大学 计算机教学实验中心http://ctec.xjtu.edu.cn
第13章 标准库和输入输出流 • 教学目标 • 1. C++标准库 • 2. 掌握文件的打开和关闭、读和写的操作方法; • 3. 掌握文本文件和二进制文件; • 4. 格式化I/O系统。
授 课 内 容 • 13.1 标准库概述 • 13.2 流概述 • 13.3 输入输出流 • 13.4 格式化I/O • 13.5 文件处理 • 13.6 对象的输入/输出 • 13.7 标准模板库(STL)简介
13.1 标准库概述 • 用C++语言编写的类和函数库 • 由编译器厂商提供,与平台、厂商和编译器版本无关
标准库构成 • 标准函数库 • 从C语言中继承下来 • C格式的输入输出函数、字符与字符串处理函数、数学函数、时间日期函数、动态分配函数以及一些实用函数 • 标准类库 • 标准C++的I/O流类、字符串类、数字类、异常处理和杂项类以及STL容器类
标准库的使用 • 包含相应的头文件 • C++的头文件来源: • 标准C语言库函数的头文件,带有.h后缀; • 标准C++语言类库的头文件,不带.h后缀; • 由标准C语言库函数头文件变成的标准C++的头文件,把原有标准C语言库函数头文件去掉.h后缀而加上c前缀。
10.4 命名空间 • 命名空间(namespace,又称名字空间)是C ++的声明区域,基本意义在于避免程序中的标识符重名,这是结构化程序的基本目标之一。 • C ++引入的类声明,实际上可以将全局变量括起来,使这些全局变量局部于该类中。C ++新的标准将这一方式推广了,不再需要借助一个能产生对象的类,就可通过纯粹起变量命名划分作用的命名空间来做到这一点。 • 命名空间的声明与类的声明非常类似,同样,指定其中的变量或函数需以命名空间的名字作为域作用限定符。例如:
namespace spaceA{ • int m,n; • void fun1(){m+=5;} • void fun2(){fun1();} • } //没有“;”号 • void main(){ • spaceA∷m=12; • spaceA∷fun1(); • }
可以看出,在命名空间外面对命名空间中的变量与函数访问时,需加上命名空间作用域限定符。命名空间结束处不应该加分号,一个命名空间可以分散在多个文件中。可以看出,在命名空间外面对命名空间中的变量与函数访问时,需加上命名空间作用域限定符。命名空间结束处不应该加分号,一个命名空间可以分散在多个文件中。 • 在命名空间引用到命名空间的变量时,都要加上命名空间前缀,为了解决这件令人感到麻烦的事,C ++引入如下语句: • using namespace 命名空间名;
事实上,目前C ++标准中的标准类库的变量与函数都属于命名空间std,如cout、cin等,由于相应的头文件中都有语句“using namespace std;”,对上述变量的引用无需再写成: std∷cout和std∷cin。 • 新的C ++标准引入了一种新的头文件载入方式: 没有.h。
C ++标准类库的头文件名称 <algorithm> <bitset> <complex> <deque> <exception> <fstream> <functional> <iomanip> <ios> <iosfwd> <iostream> <istream> <iterator> <list> <locate> <limits> <map> <memory> <new> <numeric> <ostream> <queue> <streambuf> <string> <set> <sstream> <stack> <stdexcept> <typeinfo> <utility> <valarray> <vector> <cmath>
13.2 流 • 在C++中,输入输出是通过流来完成的。C++的输出操作将一个对象的状态转换成一个字符序列,输出到某个地方。输入操作也是从某个地方接收到一个字符序列,然后将其转换成一个对象的状态所要求的格式。 • 这看起来很像数据在流动,于是把接收输出数据的地方叫做目标,把输入数据来自的地方叫做源。而输入和输出操作可以看成字符序列在源、目标以及对象之间的流动。 • C++将与输入和输出有关的操作定义为一个类体系,放在一个系统库里,以备用户调用。这个执行输入和输出操作的类体系就叫做流类,提供这个流类实现的系统库就叫做流类库
输入/输出流类的继承层次结构 iostream流类库的层次结构
输入输出流 • 头文件iostream中含有4个对象: • cin:标准输入流 • cout :标准输出流 • cerr :非缓冲标准错误流 • clog :经缓冲标准错误流 • 格式化I/O :包含头文件iomanip • 文件处理操作 :包含头文件fstream
输入输出流的成员函数 • 屏幕输出(写操作) • 1. 使用预定义的插入符(<<) • 2. 使用成员函数put( )写一个字符。 • 3. 使用成员函数write( )写一个字符串。
键盘输入(读操作) • 1. 使用预定义的提取符(>>)。 • 2. 使用成员函数get( )读一个字符。 • 3. 使用成员函数getline( )读一行字符。 • 4. 使用成员函数read( )读若干个字符。
输入输出流的成员函数 • get():可读取输入的空格; • getline():读取一行信息到字符数组中; • put():输出一个字符; • ignore():跳过制定数量的字符; • putback() • peek() :返回输入流中的下一个字符,但不将其从输入流中删除; • read()/write():无格式输入、输出; • gcount():统计输入个数;
13.4 格式化I/O • 1. 使用状态标志和成员函数进行格式化输出 • (1) 控制格式的标志位。 • 定义标志位的组合(静态变量): • basefield del|oct|hex • adjustfield left|right|internal • floatfield seientific|fixed
(2) 设置标志字的成员函数 • long flags( ):返回流格式标志的当前设置; • 例:cout.flags(ios::scientific|ios::showpos); • long setf():设置流格式标志 • 例:cout.setf(ios::uppercase|ios::scientific); • long unsetf(long):清除流格式标志
#include <iostream> using namespace std; int main() { cout.precision (4); cout.width(10); cout<<10.12345<<endl; cout.fill ('*'); cout.width (10); cout<<10.12345<<endl; cout<<"Hi"<<endl; cout.width (10); cout.setf(ios::left); cout<<10.12345<<endl; return 0; } (3) 控制输出格式和成员函数 int width( ) 设置域宽; int fill( ) 填充字符; int precision( ) 设置精度;
2. 使用流操作符进行格式输出 • 系统I/O流类库中所定义的操作符被放在iomanip.h文件中。 • 常量名 含 义 • skipws 跳过输入中的空白 • left 输出数据按输出域左边对齐输出 • right 输出数据按输出域右边对齐输出 • internal 在指定任何引导标志或基之后填充字符 • dec 转换基数为十进制形式 • oct 转换基数为八进制形式 • hex 转换基数为十六进制形式 • showbase 输出带有一个表示制式的字符 • showpoint 浮点输出时必须带有一个小数点和尾部的0
常量名 含 义 • uppercase 十六进制数值输出使用大写A~F,科学计数 • 显示使用大写字母E • showpos 在正数前添加一个“+”号 • fixed 使用定点形式表示浮点数 • scientific 使用科学计数法表示浮点数 • untibuf 每次插入之后, ostream∷osfx刷新该 • 流的缓冲区。默认缓冲单元为cerr • setbase(base) 将基数设置为base
例13-2 设置整数流的基数 • #include <iostream> • #include <iomanip> • using namespace std; • int main() • { int n; • cout << "Enter a decimal number: "; • cin >> n; • cout << n << " in hexadecimal is: " • << hex << n << '\n' • << dec << n << " in octal is: " • << oct << n << '\n' • << setbase( 10 ) << n << " in decimal is: " • << n << endl; • return 0; • }
文件处理 • 基本概念 • 打开和关闭文件 • 读 • 写 • 文件指针 • 缓冲区
1. 磁盘文件打开关闭操作 • (1) 打开文件的两种方法 • 方法一: • fstream 〈流对象名〉; • 〈流对象名〉.open(″〈文件名〉″,〈方式〉); • 或者 • fstream〈流对象名〉.open(″〈文件名〉″,〈方式〉)
方法二: • ① 打开写文件 • ofstream 〈流对象名〉; • 〈流对象名〉.open(″〈文件名〉″); • 或者 • ofstream〈流对象名〉.open(″〈文件名〉″);
② 打开读文件 • ifstream〈流对象名〉; • 〈流对象名〉.open(″〈文件名〉″); • 或者 • ifstream 〈流对象名〉.open(″〈文件名〉″);
(2) 关闭文件方法 • 〈流对象名〉.close( );
2. 文本文件的操作,见例13-4 #include <iostream.h> #include <fstream.h> int main() { ofstream out("grade"); if(!out) { cout << "Cannot open grade file.\n"; return 1; } out << "C++ " << 89.5 << endl; out << "English " << 93.5 << endl; out << "Maths " << 87 << endl; out.close(); return 0; }
例13-5 读取例13-4创建的“grade”文件,并将文件内容显示在屏幕上。 int main() { ifstream in("grade"); if(!in){ cout << "Cannot open grade file.\n"; return 1; } char course[20]; float grade; in >> course >> grade; cout << course << " " << grade << "\n"; in >> course >> grade; cout << course << " " << grade << "\n"; in >> course >> grade; cout << course << " " << grade << "\n"; in.close(); return 0; }
3. 二进制文件的操作 • 在使用>>运算符读取文本文件时,会发生一些字符转换,空白字符被忽略。如果想要阻止进行字符转换,必须打开一个以二进制方式访问的文件。 • 参见例13-6
下面以将内存中存放数组a的内容写入data.dat文件为例。a在VC中占20个字节,用len表示。下面以将内存中存放数组a的内容写入data.dat文件为例。a在VC中占20个字节,用len表示。 • ① 使用ofstream构造函数中的模式参量指定二进制输出模式: • ofstream wf(″data.dat″,ios∷binary); • wf.write((char *)a, len);
② 使用ofstream构造一个输出流,然后使用open函数指定写入方式: • ofstream wf; • wf.open(″data.dat″,ios∷binary); • wf.write((char *)a, sizeof a);
③ 构造一个流之后,再使用setmode成员函数改变模式: • ofstream wf(″data.dat″); • wf.setmode(filebuf∷binary); • wf.write((char *)a, len);
④ 使用二进制操作符代替setmode成员函数: • ofstream wf(″data.dat″); • wf<<binary; • wf.write((char *)a, len); • 建议不要使用后面两种,因为它们不能使用默认的命名空间。
二进制文件的操作 • int main(int argc, char *argv[]) • { char ch; • if(argc!=2) { • cout << "Usage: ProgramName <filename>\n"; • return 1;} • ifstream in(argv[1], ios::in | ios::binary); • if(!in) { • cout << "Cannot open file."; • return 1;} • while(in) { // in will be false when eof is reached • in.get(ch); • if(in) cout << ch; • } • return 0; • }
随机文件操作 • 4. 随机文件操作 • C++不仅可以顺序访问文件,还可以随机访问。为实现对文件的随机访问,I/O流类库提供了定位读指针和定位写指针的成员函数。
(1) 定位读指针的成员函数 • istream & istream ∷seekg(〈流中位置〉); • istream & istream ∷seekg(〈偏移量〉,〈参照位置〉); • long istream ∷tellg( ); • 其中,〈流中位置〉和〈偏移量〉都是long型量,用字节数表示。〈参照位置〉有如下几种: • cur=1相对于当前指针位置 • beg=0相对于流的开始位置 • end=2相对于流的结尾位置
(2) 定位写指针的成员函数 • ostream & ostream ∷seekp(〈流中位置〉); • ostream & ostream ∷seekp(〈偏移量〉,〈参照位置〉); • long ostream ∷tellp( );
5.错误处理函数 • 在对一个流对象进行I/O操作时,可能会产生错误。例如,遇到不期望的输入字符或因为磁盘已满而无法再向文件中输出数据等。
get函数和put函数 • #include <iostream> • using namespace std; • int main() • { • char ch; • while((ch=cin.get())!='X') • cout.put(ch); • return 0; • }
Read函数()和write函数() • #include <iostream> • #include <fstream> • using namespace std; • int main() • { int a[10]={0,1,2,3,4,5,6,7,8,9}; • ofstream my("test.dat",ios::out|ios::binary); • my.write ((char *)a,sizeof(a)); • my.close(); • int b[10]; • ifstream my1("test.dat",ios::in|ios::binary); • my1.read ((char *)b,sizeof(b)); • my1.close(); • for(int i=0;i<10;i++) cout<<b[i]<<endl; • return 0; • }
程序设计举例 • 例13-4 创建一个名为“grade”的文本文件,并写入了3门课程的名字和成绩。 • 例13-5 读取例13-4创建的“grade”文件,并将文件内容显示在屏幕上。 • 例13-6 :以二进制方式访问的文件 • 例13-7 :随机访问 • 例13-8 修改第11章实例编程的Date类,重载<<运算符,输出Date对象
自学内容 • 13.6 对象的输入/输出 • 重载iostream的>>和<<运算符; • 13.7 标准模板库(STL)简介 • STL是由惠普公司的Alexander Stepanov和Meng Lee开发的,并且David Musser也有重大贡献。STL已经成为ANSI/ISO C++标准的一部分,得到越来越广泛的使用。
例13-8 修改第11章实例编程的Date类,重载<<运算符,输出Date对象。 • 在Date.h文件中添加如下代码: • class Date { • friend ostream &operator<<( ostream &, const Date & ); • int day,month,year; • 在Date.cpp文件中添加如下代码: • ostream &operator<<( ostream &output, const Date &d ) • { static char *monthName[ 12 ] = {"January", "February", "March", "April", "May", • "June", "July", "August", "September", "October", "November", "December" }; • output << monthName[ d.month-1 ] << ' ' << d.day << ", " << d.year; • return output; • }
实例编程:电话簿管理程序 • struct TVChannel • { int channelNum; char channelName[20];}; • void outputLine( ostream &output, const TVChannel &c ); • int main() • { ofstream outTV( "tv.dat", ios::ate ); • if ( !outTV ) {cerr << "File could not be opened." << endl; • exit( 1 ); • } • cout << "Enter channel number " << "(1 to 100, 0 to end input)\n? "; • TVChannel tv; • cin >> tv.channelNum; • while ( tv.channelNum > 0 && tv.channelNum <= 100 ) { • cout << "Enter Channel Name\n? "; • cin >> tv.channelName;
outTV.seekp( ( tv.channelNum - 1 ) * sizeof( TVChannel ) ); • outTV.write( • reinterpret_cast<const char *>( &tv ), • sizeof( TVChannel ) ); • cout << "Enter channel number\n? "; • cin >> tv.channelNum; • } • outTV.close();
作业 • 课后习题268页1、2、3、5