1文件的概念
根据文件中数据的组织方式,可以将文件分为文本文件和二进制文件。
- 文本文件:又称字符文件或ASCII文件,它的每一个字节放一个ASCII代码,代表一个字符。
- 二进制文件:又称内部格式文件或字节文件,是把内存中的数据按其在内存中的存储形式原样输出到磁盘中存放。
比如:数字 64 在内存中表示为 0100 0000,若将其保存为 文本文件,则要分别存放十位 6 和个位 4 的 ASCII 码,为 0011 0110 0011 0100,占用两个字节;若将其保存为二进制文件,则按内存中形式直接输出,为 0100 0000,占用一个字节。
文本文件中数据与字符一一对应,一个字节代表一个字符,可以直接在屏幕上显示或打印出来,这种方式使用方便,比较直观,便于阅读,但一般占用存储空间较大,而且输出时要将二进制转化为 ASCII 码比较花费时间。
二进制文件,输出时不需要进行转化,直接将内存中的形式输出到文件中,占用存储空间较小,但一个字节并不对应一个文件,不能直观显示文件中的内容。
2 文件流的分类
文件流是以外存文件未输入输出对象的数据流。输出文件流是从内存流向外存文件的数据,输入文件流是从外存文件流向内存的数据。每一个文件流都有一个内存缓冲区与之对应。
C++有三个用于文件操作的文件类:
ofstream
ifstream
fstream
操作的过程:要以磁盘文件(外存文件)为对象进行输入输出,必须定义一个文件流类的对象,通过文件流对象将数据从内存输出到磁盘文件,或者将磁盘文件输入到内存。
定义文件流对象后,我们还需要将文件流对象和指定的磁盘文件建立关联,以便使文件流流向指定的磁盘文件,并确定文件的工作方式(是输入还是输出,二进制还是文本)。我们可以在定义流对象的时候指定参数来调用构造函数,或者通过成员函数open() 来进行文件流对象和指定文件的关联(后面会讲)。
C++中输入输出流关系如下:
以上,箭头表示继承关系。
以istream ,ostream ,iostream 三者为例,看一下具体的继承关系,如下:
template<typename _CharT, typename _Traits>
class basic_istream : virtual public basic_ios<_CharT, _Traits>;
template<typename _CharT, typename _Traits>
class basic_ostream : virtual public basic_ios<_CharT, _Traits>;
template<typename _CharT, typename _Traits>
class basic_iostream
: public basic_istream<_CharT, _Traits>,
public basic_ostream<_CharT, _Traits>;
typedef basic_ios<char> ios;
typedef basic_streambuf<char> streambuf;
typedef basic_istream<char> istream;
typedef basic_ostream<char> ostream;
typedef basic_iostream<char> iostream;
typedef basic_stringbuf<char> stringbuf;
typedef basic_istringstream<char> istringstream;
typedef basic_ostringstream<char> ostringstream;
typedef basic_stringstream<char> stringstream;
typedef basic_filebuf<char> filebuf;
typedef basic_ifstream<char> ifstream;
typedef basic_ofstream<char> ofstream;
typedef basic_fstream<char> fstream;
可以看到basic_istream 和basic_ostream 都是虚继承于basic_ios ,basic_iostream 是继承于basic_istream 和basic_ostream ,注意这里继承于basic_ios 的时候之所以要用虚拟继承,是为了防止多重继承时,多个父类共用基类产生二义性。
2 打开文件
打开文件操作主要是把我们的文件流类对象和一个文件相关联起来,这样这个被打开的文件可以用类对象表示,之后我们对文件流类对象所做的输入和输出操作其实就是对这个文件所做的操作。
2.1 通过类对象调用open 函数打开一个文件
在每个文件流的类中都定义了一个打开文件的成员函数open ,函数原型如下
void open(const char* filename,ios_base::openmode mode);
filename: 要打开的文件名
mode: 要打开文件的方式
其中mode 定义在所有IO 的基类中:即ios 类,它包括如下几种方式:
os::app
ios::ate
ios::binary
ios::in
ios::out
ios::nocreate
ios::noreplace
ios::trunc
mode 参数可以组合起来使用,但是两个参数之间必须要用操作符| 隔开,如下
ofstream out;
out.open("text.txt",ios::out|ios::app);
或者
ofstream out("text.txt",ios::out|ios::app);
2.2 通过类对象构造函数打开文件
类ofstream , ifstream 和 fstream 的对象所进行的第一个操作通常都是打开文件,这些类都有一个构造函数可以直接调用open 函数,并拥有同样的参数。这样,我们就可以通过以下方式进行与上面同样的定义对象和打开文件的操作:
ofstream file ("example.bin", ios::out | ios::app | ios::binary);
fstream file1;
file1.open("c:\\config.sys",ios::binary|ios::in,0);
file1.open("c:\\config.sys");<=>file1.open("c:\\config.sys",ios::in|ios::out,0);
fstream file1("c:\\config.sys");
3 关闭文件
当我们完成对文件的操作后,需要调用成员函数close 来关闭我们的文件流,close 函数的作用其实就是清空该类对象在缓存中的内容并且关闭该对象和文件的关联关系,那个该对象可以和其他文件进行关联。
ofstream file;
file.open("text.txt",ios::out|ios::app);
file.close();
为了防止一个类对象被销毁后,还和某个文件保留关联关系,所以文件流类的析构函数都会自动调用close 函数。
4 读写文件
对文件读写操作要分两种:文本文件的读写、二进制文件的读写
4.1 文本文件的读写
文本文件的读写很简单:用插入器(<<)向文件输出;用析取器(>>)从文件输入。
插入器(<<) 向流输出数据。比如说系统有一个默认的标准输出流(cout),一般情况下就是指的显示器,所以,cout<<“Write Stdout”<<‘n’;就表示把字符串"Write Stdout"和换行字符(‘n’)输出到标准输出流。
析取器(>>) 从流中输入数据。比如说系统有一个默认的标准输入流(cin),一般情况下就是指的键盘,所以,cin>>x;就表示从标准输入流中读取一个指定类型(即变量x的类型)的数据
比如读取写入txt文件,如下
#include "iostream"
#include<fstream>
void main()
{
std::fstream f("d:\\test.txt", ios::out);
f << 1234 << ' ' << 3.14 << 'A' << "How are you";
f.close();
f.open("d:\\try.txt", ios::in);
int i;
double d;
char c;
char s[20];
f >> i >> d >> c;
f.getline(s, 20);
std::cout << i << std::endl;
std::cout << d << std::endl;
std::cout << c << std::endl;
std::cout << s << std::endl;
f.close();
}
还有一些其他的格式如下
操纵符 | 功能 |
---|
dec | 格式化为十进制数值数据 | endl | 输出一个换行符并刷新此流 | ends | 输出一个空字符 | hex | 格式化为十六进制数值数据 | oct | 格式化为八进制数值数据 | setpxecision(int p) | 设置浮点数的精度位数 |
也可以利用文件流对象的成员函数 get, put 等,其用法如下: ①put() put() 函数向流写入一个字符,其原型是ofstream &put(char ch) ,使用也比较简单,如file1.put('c'); 就是向流写一个字符'c' 。
②get() get() 函数比较灵活,有3种常用的重载形式:
一种就是和put() 对应的形式:ifstream &get(char &ch); 功能是从流中读取一个字符,结果保存在引用ch 中,如果到文件尾,返回空字符。如file2.get(x); 表示从文件中读取一个字符,并把读取的字符保存在x 中。
另一种重载形式的原型是: int get(); 这种形式是从流中返回一个字符,如果到达文件尾,返回EOF ,如x=file2.get(); 和上例功能是一样的。
还一种形式的原型是: ifstream &get(char *buf,int num,char delim='n'); 这种形式把字符读入由buf 指向的数组,直到读入了num 个字符或遇到了由 delim 指定的字符,如果没使用 delim 这个参数,将使用缺省值换行符'n' 。例如: file2.get(str1,127,'A'); //从文件中读取字符到字符串str1,当遇到字符’A’或读取了127个字符时终止。
4.2 二进制文件的读写
二进制文件的操作需要在打开文件的时候指定打开方式为ios::binary ,并且还可以指定为既能输入又能输出的文件,我们通过成员函数 read 和 write 来读写二进制文件。
istream& read ( char * buffer, streamsize size);
ostream& write (char * buffer, streamsize size);
|