首页 > 代码库 > 《好学的C++ 第2版》 第8章 文件-电子存储

《好学的C++ 第2版》 第8章 文件-电子存储

主内存(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));

 

《好学的C++ 第2版》 第8章 文件-电子存储