C/C++ IO总结

正如Effective C++所讲:C++就是一个语言联邦。他是一个多面手。对于同一个问题往往有很多种解决办法,具体采用那种办法就交给程序员去选择。因此本文对C++文件IO的整理就得分为两个面:C方式,C++方式。程序员可根据自己所在团队选择合适且统一的IO方式。

 

一. 基础知识

1.1文件类型:ASCII文件和二进制文件

首先我不保证文件类型只有这两种。但理解这两种文件对学习文件IO操作非常重要。

1.1.1ASCII文件

ASCII文件也就是文本文件,每个字节存放一个ASCII代码,代表一个字符。可以使用任何编辑器打开,如记事本或者UE等,打开就是你能看懂的字符。比如姓名"richard"就会存储为7个字节,每个字节分别为对应字母的ASCII码。整数10000就会被存为"10000",每个字节为每个字母的ASCII码即:00110001 00110000 00110000 00110000 00110000。ASCII文件通常是适合人看的,多用于展现给人,所以叫文本文件。

1.1.2二进制文件

把内存中的数据按其在内存中的存储形式原样输出到磁盘上存放。所谓内存中的数据就是我们写的程序中的各种值。比如我定义了int a = 10000;那么a在内存中是什么样子呢?a是一个int类型,32位占4个字节:00000000 00000000 01000111 00010000。这就是内存中a的形式。把这个形式原样存储到文件中就是二进制文件。如下图所示。一般中间结果数据需要暂时保存在外村上,以后又需要输入到内存的,常用二进制文件保存。

1.2缓冲文件系统和非缓冲文件系统

待完善。

1.3文件都包含什么

一个文件不仅仅包含内容。可以从C语言中FILE类型的生命中观察一个文件都有些什么:

typedef struct{

   short level;                        /*缓冲区"满"或"空"的程度*/

   unsigned flags;                 /*文件状态标志*/

   char fd;                             /*文件描述符*/

    unsigned char hold;         /*如无缓冲区不读取字符*/

   short bsize;                       /*缓冲区的大小*/

   unsigned char *buffer;     /*数据缓冲的位置*/

   unsigned char *curp;        /*指针,当前的指向*/

   unsigned istemp;              /*临时文件,指示器*/

   short token;                       /*用于有效性检查*/

}

原来一个文件需要包含如此多的信息。重点关注curp这个指针变量。它就像文件自带了的一个游标,每次从文件读写东西之后,这个游标就会向后移动。才能方便下次读写。

1.4 文件打开的方式

r,w,a,rb,wb,ab,r+,w+,a+,rb+,wb+,ab+

凡是带b的表示读写二进制文件,不带的表示读写ASCII文件。

待完善。

二. C文件IO

C语言中对IO的处理主要通过各种函数,而这些函数的参数往往就是一个文件指针。

2.1文件的打开(fopen函数)

FILE* fp;

fp = fopen(文件名,打开方式);

代码示例:

if((fp = fopen("./file1","r")) == NULL){

   printf("cannot open this file\n");

   exit(0);

}

2.2文件的关闭(fclose函数)

文件使用完必须关闭文件

fclose(文件指针);

代码示例:fclose(fp);

2.3文件的读写

2.3.1 fputc函数和fgetc函数(putc函数和getc函数)

fputc(ch, fp); 把一个字符ch写到文件指针fp中.

ch = fgetc(fp); 读取字符赋给ch.

2.3.2 fread函数和fwrite函数

用fgetc和fputc函数可以用来读写文件中的一个字符。但是常常要求一次读入一组数据(例如,一个实数或者一个结构体变量)。这就要用到fread和fwrite读写一个数据块。

fread(buffer,size,count,fp);

fwrite(buffer,size,count,fp);

fread和fwrite函数一般用于二进制文件的输入输出。因为他们是按数据块的长度来处理输入输出的。以哪种形式写和读必须是对应的,切记这一点。吃过这方面亏得同学体会很深,也就更加理解其原理了。

2.3.3 fprintf函数和fscanf函数

考虑这种情况,我将学生数据存储到文件中,以备后面查阅,注意着是要给人看的。fread和fwrite虽然可以读写结构体这样的数据块。但是如前所说,那个适合的二进制文件的读写。不适合给人看的ASCII文件。

struct student{

   int id;

   char name[15];

   char addr[15];

   int age;

};

 

int main(){

   struct student lily = {1234, "li", "shaanxi", 20};

   FILE* fp;

   if((fp = fopen("studentsTable", "wb")) == NULL){

       printf("%s\n", "error");

       return 0;

    }

   fwrite(&lily, sizeof(struct student), 1, fp);

   fclose(fp);

   return 0;

}

执行这段代码后,打开studentsTable之后,发现除了中间lishaanxi之外全都是二进制1,0.根本无法识别,因为他存储的是整数1234和20在内存中的32位二进制。当然无法识别了,而char型在内存中和ASCII文件以及二进制文件都是一个字节。恰好显示出来。人能够识别。所以你打开二进制文件studentsTable之后看到那样的结果。

因此给人看的话就要按照ASCII文件读写学生数据,你当然可以用fread和fwrite以ASCII文件方式打开studentsTable.即使成功写入结果就是:1234lishaanxi20.(往往会出错)。你看这个数据黏在一起多么难区分。所以我们需要能格式化输出数据到文件中,比如:1234|li|shaanxi|20。这个时候就要用到fprintf和fscanf了。

fprintf(文件指针,格式字符串,输出列表);

fsacnf(文件指针,格式字符串,输入列表);

那么代码可以改良为:

if((fp = fopen("studentsTable","w")) == NULL){

   printf("%s\n", "error");

   return 0;

}

fprintf(fp, "%d|%s|%s|%d",lily.id, lily.name, lily.addr, lily.age);

这下打开studentsTable文件后就会看到1234|li|shaanxi|20了。

fsacnf当然就是读取对应格式的文件了。

2.3.4其他函数

putw,getw,fgets,fputs。这些函数当你掌握上面的只是之后看看声明自然就会用了。也用不了那么多。

2.4 文件的定位

如前面基础部分所述,文件中有一个位置指针,指向当前读写的位置。如果顺序读写一个文件,每次读写一个字符,则读写完一个字符之后,该位置指针自动移动指向下一个字符位置。如果想改变这样的规律,强制使位置指针指向其他指定的位置,可以使用以下函数。

rewind,fseek,ftell等。

三. C++文件IO

C++除了具有C的文件IO功能之外,还提供了面向对象的处理方式。即使IO流类库。首先学习C++文件IO之前一定要记住这张图,这是非常重要的。其中stream就不解释了。i表示in, o表示out, f表示file.

3.1温故知新

3.1.1 小知识

"cin >>" ,"cout<< "大家都用过吧,其实cin是istream类的对象,cout是ostram类的对象。至于"<<",">>"只是这两个类的重载操作符而已。因为"<<",">>"就像流水一样只是一个流向。比如cin >> a; 从cin流向了a,就是从标准输入即键盘输入流向了变量a,这不就是从键盘输入赋值给a吗。又如cout << a;从a流向了cout,就是从变量a流向了标准输出即显示其屏幕,这不就是把a打印到屏幕吗。那么由此推出istream和ostream及其子类们的对象都可以对应的使用"<<"或">>"。

3.1.2 标准输出(屏幕输出)和标准输入(键盘输入)

流对象<<操作数

流对象.put(char c);

流对象.wirte(const char *str, int n);

流对象>>操作数

流对像.get();

流对象.get(char c);

流对象.getline(char* buf, int n, char deline='\n');

流对象.read(char *buf, int size);

注意以上流对象的操作除了get()之外多数均返回本流对象的引用,可连续使用。比如cout<

3.2 文件的打开和关闭操作

3.2.1打开文件操作

a)使用fstream类对象打开文件的方法

fstream <对象名>

<对象名>.open("<文件名>", <方式>);

方式:io, out, app, ate, binary …… ios::in|ios:out,ios::out|ios::binary……

例子:

写方式打开一个文本文件

fstream outfile;

outfile.open("file.txt",ios::out)

或者 fstream outfile("file.txt", ios::out)

读方式打开二进制数据文件file.dat

fstream infile("file.dat",ios::binary|in);

b)使用ofstream类或ifstream类对象打开文件的方法

ofstream <对象名>

<对象名>.open("<文件名>")

为什么这块没有打开方式呢?因为已经指明是输出流了。就不需要告诉是写还是读了。

例子:

ofstream out1;

out1.open("file.cpp");

或者 ofstream out1("file.cpp");

ifsteam in1;

in1.open("file1.cpp");

或者 ifstream in1("file1.cpp")

3.2.2 关闭文件的操作

<流对象名>.close();

例子:out1.close();

3.3 文本文件的读写操作

既然cout,cin是ofstream,ifstream的对象,那么他们的那些方法也同样适用于我们的文件流。so easy。

3.4 二进制文件的读写操作

同上。

3.5 格式控制

流对象的格式控制函数太多了,需要用的时候查就可以了。

3.6 随机读写

随机读写就相当于C语言中的文件的定位,更改文件里面的那个游标。流对象都相应的用友这些方法。随用随学吧。

你可能感兴趣的:(C/C++)