主内存(RAM,随机访问内存)当计算机关闭时其数据就丢失。
对于cin和cout支持的函数调用和操作,c++提供的文件流也同样支持。前者需要#include <iostream>,后者需要#include <fstream>。流(stream)在写数据时是目的地,读数据时就是源头。
打开badgirl.txt文件:
ofstream fout("badgirl.txt"); //以独占方式打开该文件,默认文件在当前目录(运行程序的那个目录),也可给出完整路径(加上驱动器盘符也可以,这就是绝对路径,例如"c:\\badgirl.txt"就是c:\badgirl.txt文件的绝对路径)。还有ifstream、fstream
fout << "you bad but sexy girl!" << endl;
fout << "and you need bad boy!"
fout.close();
fout对象提供了一条通向磁盘文件的道路。用面向对象的术语说,fout封装了那个文件并使之具备了接收输出数据的能力。另外,虽然程序成功退出是、时C++会自动关闭仍处於打开状态的文件,但是文件不用时最好及时关闭,使程序放弃对该文件的拥有权,让其他进程可以去访问它。 //it:一个进程打开了某文件,其他进程是否可读可写该文件?试图打开该文件是否会出错?
char filename[MAX_PATH+1];
预定义常数MAX_PATH:系统所能支持的文件名(包括其路径在内)的最大长度。
若文件打开操作不成功,fout就会是NULL,可用于判断。非法路径或者像以写的方式打开只读文件都会返回NULL.
if(!fout){/*...*/}
ifstream fin;//文件以文本和输入方式打开。
fin.getline(input_line, COL_WIDTH+1);//读出一行
if(fin.eof()){/*...*/}; //到达文件末尾,fin.eof()就是true了
库函数atoi可以将字符串转为整数,这可以用来处理用户输入。
以上都是文本流,操作与控制台IO大同小异。写入文件的每一个字节都是可打印字符的ASCII编码。
二进制文件:读写这类文件时使用的都是数据的实际数值,无需进行ASCII转换(it:数字0就是写入数字0,而不是0的ASCII码值)。
####文本模式里,换行符在写时会被转换为\n\r(回车加换行wins)或\n(unix)或\r(mac)。而二进制模式里无需也不能进行此种转换。####注:回车原意是光标回到当前行的开头,换行原意是光标移动到下一行。所以不要嘲讽wins,含义上是完整的,虽然多一个字符。不过导致的问题就是跨系统的文件格式问题,产生了dos2unix这种命令。
创建文件流对象时,可将它设置为文本模式(默认)或二进制模式。前者应该使用<<、>>及getline,后者只应该使用read和write成员函数(是直接的读写操作)。
范例:(记录指的是在文件里不断重复出现的数据格式。由于数据有规律排列,通过记录号获取数据会很容易,而用结构或类来保存它们是很自然的(后续讨论)。)
#include <iostream>
#include <fstream>
using namespace std;
int get_int(int default_value);
int main(){
char filename[MAX_PATH+1];
int n = 0;
char name[20];
int age = 0;
int recsize = sizeof(name) + sizeof(int);//计算记录的长度
//打开文件
cout << "Enter file name: ";
cin.getline(filename,MAX_PATH);
fstream fbinout(filename, ios::binary | ios::out); //ios::out是要对文件进行写操作,注意会覆盖现有文件内容,也用于打开新文件
if(!fbinout){
cout << "Could not open " << filename <<endl;
system("PAUSE");
return -1;
}
//获取要记录的数据
cout << "Enter record number: ";
n = get_int(0); //获取记录号
cout << "Enter name: "
cin.getline(name, sizeof(name)); //获取名字
cout << "Enter age: ";
age = get_int(0); //获取年龄
//写入数据
fbinout.seekp(n * recsize); //根据记录号偏移到该记录应在的位置,否则会覆盖0号记录(it:光标或者指针默认的在最开始即0号记录处)
fbinout.write(name, sizeof(name));
fbinout.write((char*)(&age),sizeof(int));
fbinout.close();
system("PAUSE");
return 0;
}
#define COL_WIDTH 80 //80是典型行宽 //it:预编译从定义处开始生效
int get_int(int default_value){
char s[COL_WIDTH+1];
cin.getline(s, COL_WIDTH+1);
if (strlen(s) == 0)
return default_value;
return atoi(s);
}
读取数据类似,不同之处只在于:
fstream fbinin(filename, ios::binary | ios::in); //ios::in模式文件不存在会出错
fbinin.seekp(n*recsize);
fbinin.read(name, sizeof(name));
fbinin.read((char*)(&age), sizeof(int));
fbinin.close();
以下fbin将既支持读又支持写:
fstream fbin(filename, ios::binary | ios::out | ios::in);
小结捡漏:
#include <fstream>会将C++标准库提供的文件流支持功能激活(把必要的函数原型和声明引入程序)
//it:通过文件指针可实现随机访问模式(读写(包括覆盖)文件任意部分而不影响其他数据)。如果文件指针被移动到了超出文件当前长度的位置,文件会自动扩展以满足长度需要。
seekp成员函数用于移动文件指针,其入参是从文件头开始的偏移量(以字节计算)。
read和write:入参相同,都是数据地址(char*类型)和需要复制的字节数。//it:写入int要取其地址转为char*类型,但复制字节数仍是int所占用字节数(建议用sizeof获取)。
fbin.write((char*)(&x), sizeof(x));