File Processing Chapter 17 http://www.cnblogs.com/kzloser/archive/2012/07/16/2593133.html
Topics 17.1 Introduction 17.2 The Data Hierarchy 17.3 Files and Streams 17.4 Creating a Sequential File 17.5 Reading Data from a Sequential File 17.6 Input/Output of Objects
17.1 Introduction 临时存储:变量和数组 永久存储:文件 保存大量的数据 存储在二级存储设备中 Magnetic disks Optical disks Tapes
文件的基本概念: 本节中文件指的是磁盘文件。C++文件(file)分为两类:二进制文件和文本文件。 2018年11月29日8时56分 文件的基本概念: 本节中文件指的是磁盘文件。C++文件(file)分为两类:二进制文件和文本文件。 文本文件由字符序列组成,也称ASCII码文件,在文本文件中存取的最小信息单位为字符(character) 二进制文件中存取的最小信息单位为字节(Byte),如.obj C++把每一个文件都看成一个有序的字节流,每一个文件或者以文件结束符(EOF)结束,或者在特定的字节号处结束。 EOF在C++中的定义为:const int EOF = -1; 它这么定义是有道理的: 举个例子来说 int ch; //注意:这里定义了ch是int型的,而不是char型的 while((ch = cin.get()) != EOF) { cout << ch; } cin.get()是读取输入流中的一个字节,但是时常忽略的,也是必须注意的是:那些C++的设计者们将cin.get()设计成返回int型,而不是char型,这不是令人挺奇怪的吗?感觉char ch = cin.get()比较顺其自然吧?原因就是为了能够使cin.get()能够返回一个EOF。 因为对于一个字节的长度,已经定义了256个字符了,即扩展的ASCII码,已经不能再弄出一个字节表示EOF了,而且对于上面的程序,我们如果256个字符都得用,那么拿什么与这些字符比较去结束这个循环呢? 那些设计者门将cin.get()读取的char型提升为一个int型,那么肯定是正的,然后EOF定义为-1,在windows下的ctrl+z表示输入EOF,即输入ctrl+z的时候cin.get()才等于-1,其余的字符输入的时候cin.get()是一个正数。这是设计者门提供EOF的主要原因 1 2 4 3 6 5 7 8 … n-1 文件结束符 C++把文件看作有序的n个字节的流
Topics 17.1 Introduction 17.2 The Data Hierarchy 17.3 Files and Streams 17.4 Creating a Sequential File 17.5 Reading Data from a Sequential File 17.6 Input/Output of Objects
17.2 The Data Hierarchy Bits (二进制位) Characters(字符) 0 or 1 计算机支持的最小数据项 计算机电路执行位处理 所有数据项最终由位组成 Characters(字符) 数字、字母和专门的符号称为“字符” 能够在特定计算机上用来编写程序和代表数据项的所有字符的集合称为“字符集” Char以字节形式存储 (8 bits) Wchar_t 占多个字节(用于非英文字符集的表示)
17.2 The Data Hierarchy (Cont.) Fields(字段或数据项) 由字符组成 代表一定的含义 Example 姓名,颜色等等 Records(记录) 由多个字段组成 C++中表现为:类 An employee’s record might include id#, name, address, etc. 记录的关键字:A record key is a field unique to each record
Fig. 17.1 | Data hierarchy.
17.2 The Data Hierarchy 文 件3 文 件2 文 件1 记录: 字段1(关键字) 字段2 字段3 …… … 数据库:一组相关的文件 数据库管理系统:为建立和管理数据库而设计的文件集合
Topics 17.1 Introduction 17.2 The Data Hierarchy 17.3 Files and Streams 17.4 Creating a Sequential File 17.5 Reading Data from a Sequential File 17.6 Input/Output of Objects
2018年11月29日8时56分 17.3 Files and Streams 当打开一个文件时,该文件就和某个流关联起来了。对文件进行读写实际上受到一个文件定位指针(file position pointer)的控制。 输入流的指针也称为读指针,每一次提取操作将从读指针当前所指位置开始,每次提取操作自动将读指针向文件尾移动。 输出流指针也称写指针,每一次插入操作将从写指针当前位置开始,每次插入操作自动将写指针向文件尾移动。 画一个图
17.3 Files and Streams -- I/O流模板之间的继承关系 为了在C++中执行文件处理,必须包含头文件<iostream>和<fstream>
17.3 Files and Streams --文件处理 必须包含头文件: #include <fstream> <fstream> 包括三种类模板的定义 basic_ifstream (for file input) basic_ofstream (for file output) basic_fstream (for file input and output) 提供了处理字符流的模板特化 ifstream:从文件中读入字符(读文件) ofstream:向文件输出字符(写文件) fstream:支持文件中字符的输入和输出 生成这些流类模板特化的对象,即可打开文件 程序和文件之间通过流对象交互
Topics 17.1 Introduction 17.2 The Data Hierarchy 17.3 Files and Streams 17.4 Creating a Sequential File 17.5 Reading Data from a Sequential File 17.6 Input/Output of Objects
17.4 Creating a Sequential File File structure 需要程序员对文件结构化 C++ :对文件作字节流处理 顺序文件 vs 随机存取文件 顺序文件:记录一般按照关键字顺序存放 随机存取文件:不需查找,直接读取
17.4 Creating a Sequential File 写数据到文件 创建ofstream对象 构造函数ofstream(const char* filename,int mode) filename:文件名由文件的主名和扩展名两部分组成。 mode: ios::out :缺省模式,覆盖文件中已有数据 ios::app:向文件末尾添加数据 例:ofstream outClientFile( "clients.dat", ios::out ); 建立了一个到文件的“通信通道” 如果该文件名不存在,则新建一个同名文件 对于已创建的文件对象,使用成员函数打开文件 与构造函数的参数相同,可以先创建再打开 例:ofstream outClientFile; outClientFile.open(“clients.dat”, ios::out);
Fig. 17.5 | File open modes.
out: (写入文件) 如文件不存在,则建立新文件,如文件存在,如果同时未设定app, in,则文件清空。 2018年11月29日8时56分 in :(从文件读取) 打开方式只要含in,如文件不存在则返回失败。在打开为输入输出方式时(同时用out),编程应注意判断是否失败,失败时千万不可再写入文件。 out: (写入文件) 如文件不存在,则建立新文件,如文件存在,如果同时未设定app, in,则文件清空。 trunc:(打开文件,并清空它)文件不存在则建立新文件,与out默认操作相同。但与in配合,文件不存在则返回失败。 app:(写入文件,添加在末尾)原文件内容保留,新数据接在尾部。 ate: (at end,打开文件,文件指针在文件尾) 文件指针可以移动,即新数据可写到任何位置。文件是否清空由其它标识决定。 trunc/app/ate最好配合out、in等一起用,因为不同的C++平台,要求不同,一起用不会出错。如不一起用,至少VC++不认这种格式。 binary标识以二进制方式打开文件。同时用out时,如文件不存在,则建立新文件,并且新文件能用,不必清状态字。 ?
文件打开方式: fstream iofile;//既输入又输出用 由在ios类中定义的公有枚举成员决定: enum open_mode{ in=0x01, out=0x02, ate=0x04, app=0x08, trunc=0x10, binary=0x80 }; fstream iofile;//既输入又输出用 iofile.open(“myfile.txt”,ios::in|ios::out);
17.4 Creating a Sequential File --流对象和磁盘文件关联 打开文件时应该判断是否成功,若成功,文件流对象值为非零值,不成功为0(NULL),文件流对象值就是指它的地址。 //打开一个文件完整的程序 int main() { fstream iofile(”myfile.txt”,ios::in|ios::out); if(!iofile) { //“!”为重载的运算符 cout<<”不能打开文件:”<<”myfile,txt”<<endl; return -1; } //失败退回操作系统 }
17.4 Creating a Sequential File --文件的打开与关闭 1. 说明一个文件流对象,这又被称为内部文件: ifstream ifile;//只输入用 ofstream ofile;//只输出用 fstream iofile;//既输入又输出用 2. 使用文件流对象的成员函数打开一个磁盘文件。 iofile.open(“myfile.txt”,ios::in|ios::out); 3. 使用提取(>>)和插入(<<)运算符对文件进行读写操作,或使用成员函数(get\put)进行读写。 4. 关闭文件。三个文件流类各有一个关闭文件的成员函数 : void ifstream::close(); void ofstream::close(); void fstream::close(); 使用很方便,如:iofile.close(); 也可直接通过构造函数打开文件: fstream iofile(”myfile.txt”,ios::in|ios::out);
17.4 Creating a Sequential File --文件关闭 关闭文件时,系统把该文件相关联的文件缓冲区中的数据写到文件中,保证文件的完整,收回与该文件相关的内存空间,把磁盘文件名与文件流对象之间的关联断开,可防止误操作修改了磁盘文件。 关闭文件并没有取消文件流对象,该文件流对象又可与其他磁盘文件建立联系。文件流对象在程序结束时,或它的生命期结束时,由析构函数撤消。它同时释放内部分配的预留缓冲区。 调用析构函数也会关闭相应正在打开的文件
(1 of 2) Fig17_04.cpp 创建ofstream对象,打开文件 Overloaded operator! will return true if the file did not open successfully
重载operator void * ,遇到文件结束符EOF时(Ctrl+Z) ,返回一个空指针。 2018年11月29日8时56分 重载operator void * ,遇到文件结束符EOF时(Ctrl+Z) ,返回一个空指针。 使用“<<”向文件中写入数据 ofstream 的析构函数被自动调用,隐式地关闭文件 Ctrl+Z 读入最后一个 分析:a在不能直接转换为bool型的情况下,会试图先进行 (void*)a 操作,这个操作会直接调用成员函数operator void*(),这个函数返回一个void*返回值,所以这里的if(a)实际上是if((void*)a),并根据强制转换函数的结果进行if判断,如果上述函数改为return (void*)this, 那么就可以看到代码输出 ac. 在in读完字符'j'的时候,其实已经到达文件的末尾了,但此时流in的状态还是good,也就是说还可以继续读入下一个字节。而当in继续往下读的时候,就会读到EOF了,当读完EOF并赋值给c后,流in的状态就变成了eofbit,不可再读,在条件判断语句中流in也就成了false了。 http://blog.csdn.net/jakiechen68/article/details/14643305
Topics 17.1 Introduction 17.2 The Data Hierarchy 17.3 Files and Streams 17.4 Creating a Sequential File 17.5 Reading Data from a Sequential File 17.6 Input/Output of Objects
17.5 Reading Data from a Sequential File 创建 ifstream 对象 ifstream构造函数 A filename A file-open mode ios::in – 缺省模式,只能从文件读取数据,最小权限原则 例:ifstream inClientFile( "clients.dat", ios::in ); 对于已经存在的ifstream对象,使用成员函数打开文件 例: ifstream inClientFile; inClientFile.open("clients.dat", ios::in );
Fig17_07.cpp
Open clients.dat for input 重载 !:如果文件打开不成功,返回false 重载 operator void * ,returns,遇到文件结束符,返回一个空指针。 ifstream的析构函数隐式调用,关闭文件
Outline Fig17_07.cpp (3 of 3)
17.5 Reading Data from a Sequential File --顺序文件的读取示例 题目要求: 简单文件的写、读、查找
读写文件 ifstream fin("output.txt"); int number; char name2[20]; 2018年11月29日8时56分 ifstream fin("output.txt"); int number; char name2[20]; fin >> number; fin.ignore(); fin.getline(name2,'\n'); //fin>>number>>name2; cout<<number<<" "<<name2<<endl; strcpy(name2,"aaaa bbb"); cout<<"now name2="<<name2<<endl; fin.seekg(5); cout<<"new name2="<<name2<<endl; return 0; } #include <iostream> using std::cerr; using std::cout; using std::endl; using std::ios; #include <fstream> // file stream using std::ofstream; // output file stream using std::ifstream; #include <cstdlib> #include <cstring> int main(){ ofstream fout; fout.open("output.txt"); if (!fout) {cerr<<"File could not be opened"<<endl; exit(1); } int num = 150; char name[] = "John Doe"; fout << num << "\n"; fout << name << "\n"; fout.close(); 在Windows平台下如果以“文本”方式打开文件,当读取文件的时候,系统会将所有的"\r\n"转换成"\n";当写入文件的时候,系统会将"\n"转换成"\r\n"写入。如果以"二进制"方式打开文件,则读/写都不会进行这样的转换。 fin.getline(name2,'\n');
17.5 Reading Data from a Sequential File --顺序文件的读取示例 题目要求: 实现文件的复制
int main() // 实现从源文件到目的文件的复制 { char ch; ifstream sfile("d:\\Ex\\in.cpp"); ofstream dfile(“e:\\out.cpp"); //只能创建文件,不能建立子目录,如路径不存在则失败 if(!sfile){ cout<<"不能打开源文件:"<<"d:\\Ex\\in.cpp"<<endl; return -1;} if(!dfile){ cout<<"不能打开目标文件:"<<"e:\\out.cpp"<<endl; sfile.unsetf(ios::skipws); //把跳过空格控制位置0,即不跳过空格,否则空格全部未复制 while(sfile>>ch) dfile<<ch; sfile.close(); //如没有这两个关闭函数,析构函数也可关闭 dfile.close(); return 0; }
将abc.txt复制到xyz.txt 2018年11月29日8时56分 #include<iostream.h> #include<fstream.h> int main() { ifstream ifile("abc.txt"); if (!ifile) { cout << "abc.txt文件不能打开"<<endl; return 0; } ofstream ofile("xyz.txt"); if(!ofile) { cout << "xyz.txt文件不能打开"<< endl; return 0; char ch; while(ifile.get(ch)) ofile.put(ch); ifile.close(); ofile.close(); return 1; Get函数每次会往后一位么?? 使用输入流成员函数get()从文本文件abc.txt中读取一个字符ch,然后使用输出流成员函数put()将字符ch写入文本文件xyz.txt中,继续这一过程直到get()读完为止。
17.5 Reading Data from a Sequential File --顺序文件的读取示例 题目要求: 顺序读取文件内容,按照要求显示数据 程序解读 P618 17.8
Topics 17.1 Introduction 17.2 The Data Hierarchy 17.3 Files and Streams 17.4 Creating a Sequential File 17.5 Reading Data from a Sequential File 17.6 Input/Output of Objects
17.6 Input/Output of Objects 规范化操作: 在面向对象的程序设计中,信息总是放在对象的数据成员里。这些信息最终需要保存到文件中。 读取文件中对象的信息时,必须重新创建对象,把数据读入对象,在运行过程中,对放在对象的数据成员里的信息利用和修改,运行结束时必须把这些信息重新保存到文件中,然后关闭文件 在面向对象的C++程序设计中,文件应该在构造函数中打开,并创建对象;而在析构函数中保存和关闭文件,并撤销对象。
Summary 文件流分为三种 文件处理步骤 文件输入流(ifstream), 文件输出流(ofstream),文件输入/输出流(fstream) 文件处理步骤 定义文件流对象 打开文件 void open(const unsigned char *filename, int mode, int access=filebuf::openprot); 关闭文件 调用成员函数close()
Summary 文件读写 顺序文件操作:这种操作方式只能从文件的开始处依次顺序读写文件内容,而不能任意读写文件内容。 随机文件操作 读:文件流类的get、getline、read成员函数以及抽取符“>>” 写: put、write函数以及插入符“<<” 随机文件操作 读,写:read, write
Homework! 实验必选题目(交实验报告): 实验任选题目(不交实验报告): 见实验报告 作业题目(Homework):
重定位文件定位定位指针 文件定位指针: istream、ostream都提供了修改文件定位指针的成员函数 文件中下一个被读取或写入的字节号 seekg(long pos, int mode)—istream seekp(long pos, int mode)—ostream Return