如何正确的通过 C++ Primer 学习 C++

本人即将读研,虽然考的是本专业(GIS),但是研究生读的专业在计算机学院,要求所有研究生都要会编程。本科期间学过C++,但是基本忘的差不多。现在暑假已经被要求到学院搞项目,但是因为对编程只有概念上的认识,所以现在都是在自学C++。这几天开始从头看C++ primer,但是因为基础实在薄弱,很多内容看很久都看不懂,上网查资料也是一片模糊。看了几天之后,觉得我学习的方法有问题(我是每天都在练习他的习题)。我知道学习程序语言要多敲代码,但是这个敲代码不知道从哪里入手,看书也不知道哪些是重点,哪些现在可以不看的。完全没有一个详细的规划,很乱。求大神给些意见!谢谢!!————————————————————————————————7.13 更新我现在跟着项目,就是修改一些BUG,对于这样的实战,不知道对学习C++的帮助有多大?————————————————————————————————7.15 更新前几天把第三章看了一半,刚刚开始一直没弄懂什么是容器,后来通过上网查资料,获得了一些比较通俗的解释,不知道这样会不会对概念的理解有偏差。
从比较清楚的地方继续,完全都不懂的地方,辅以百度,做一些笔记,尝试用一下新学到做一些范例工程。再从比较清楚的地方继续,停下,整理,练习。继续……最重要的是整理,整理会使你每一次都会觉得自己有一点前进,知道什么掌握了,什么还很糊涂。记笔记我想你应该比我厉害。祝好运。祝好运。
个人经验,C++ primer 第一次可以跳着看。关键是要尽快用起来,在使用中熟练,而不是在细节中迷失。&br&以C++ Primer第五版为例,第一遍读的时候:&br&Part1也就是前七章,除了6.6,6.7节,都要通读。尤其是第三章初步介绍了vector和string,简直就是新手福音,搞定这两个容器就能写一些简单的程序。&br&Part2基本就是数据结构和算法,如果有基础读起来很轻松。&br&9,11两章介绍的容器,以及12.1节的智能指针要通读。多用智能指针和容器,远离segment fault. 第10章里的泛型算法可以慢慢读,读完以后可以写出高逼格的函数式风格C++。12.2节讲了怎么用new和delete分配空间,题主作为新手,知道这种写法就行,写程序时尽量用容器代替原始数组,尤其是代码里最好不要有delete。&br&Part3是块硬骨头,标题就是Tools for Class Authors. 作为一个&class user&,有些部分第一次是可以略过的。&br&13章很重要,要细读。初始化,复制,赋值,右值引用是C++里很微妙很重要的部分,别的语言对于这些概念很少有区分得这么细的。这一章不但要精读,还要完全掌握。&br&14章的操作符重载第一次可以观其大略;14.9节第一次可以跳过。&br&15章讲OOP,重要性不言而喻。如果之前一点概念都没有,学起来会觉得比较抽象。网上关于OOP有很多通俗有趣的文章,可以一起看看。&br&16章讲泛型编程,第一次读16.1节,掌握最基本的函数模板和类模板就行了。&br&Part4就更高档了,很多内容第一次就算啃下来,长久不用又忘了。第一次读推荐把18.2节读懂,命名空间简单易用效果好。别的内容可以观其大略,用时再看。17.1节的tuple是个有趣的东东,可以读一读。17.3节的正则表达式和17.4节的随机数也许有用,也可以读一读。如果需要读写文件,要读一下17.5.2节的raw I/O和17.5.3节的random I/O。&br&&br&最后给题主的建议是,写C++,要尽量避免C的写法。用static_cast而不是括号转换符;用vector而不是C里面的数组;用string而不是char *;用智能指针而不是原始指针。当然I/O是个例外,printf()还是比cout好用的;转换数字和字符串时sprintf()也比stringstream快
个人经验,C++ primer 第一次可以跳着看。关键是要尽快用起来,在使用中熟练,而不是在细节中迷失。以C++ Primer第五版为例,第一遍读的时候:Part1也就是前七章,除了6.6,6.7节,都要通读。尤其是第三章初步介绍了vector和string,简直就是新手福音,搞定…
首先,看c++primer&br&一定要看第5版&br&&br&1.从头开始看,别跳&br&c++11穿插在书的每一个角落,跳到后面你可能看到新标准又返回来看.&br&而且这种经典书看几遍每次也会有新收获&br&&br&2.课后习题一定要做&br&课后习题才是查露补缺.一定要做,做题和不做差别太大了,而且课后很多经典题&br&&br&3.最好记笔记,这种大部头不能保证所有都常用,所以有些会忘的很快..记了笔记可以快速记忆起来自己看的&br&&br&以上是我看完c++primer第4版没做习题和第5版做完课后习题的感受&br&这么做比较费时间,但是c++语法层应该没问题了。&br&习题答案github有很多&br&&br&第5版6-16章笔记我都记录成博客了,记的比较详细,如果你需要可以发给你&br&&br&补充:笔记链接&a href=&///?target=http%3A//blog.csdn.net/column/details/cccplusplus.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&专栏:c++primer&i class=&icon-external&&&/i&&/a&
首先,看c++primer一定要看第5版1.从头开始看,别跳c++11穿插在书的每一个角落,跳到后面你可能看到新标准又返回来看.而且这种经典书看几遍每次也会有新收获2.课后习题一定要做课后习题才是查露补缺.一定要做,做题和不做差别太大了,而且课后很多经典题3.…
已有帐号?
无法登录?
社交帐号登录
其实我用的是别名推荐这篇日记的豆列
······trackbacks-0
&&& 学习本章内容之前有必要对缓冲区的概念做一个基本了解,我引用了网上一片文章《C++编程对缓冲区的理解》,内容如下:&&&
什么是缓冲区
 & &缓冲区又称为缓存,它是空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区。  
&&&& &缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。
为什么要引入缓冲区
  比如我们从磁盘里取信息,我们先把读出的数据放在缓冲区,计算机再直接从缓冲区中取数据,等缓冲区的数据取完后再去磁盘中读取,这样就可以减少磁盘的读写次数,再加上计算机对缓冲区的操作大大快于对磁盘的操作,故应用缓冲区可大大提高计算机的运行速度。
  又比如,我们使用打印机打印文档,由于打印机的打印速度相对较慢,我们先把文档输出到打印机相应的缓冲区,打印机再自行逐步打印,这时我们的可以处理别的事情。
  现在您基本明白了吧,缓冲区就是一块内存区,它用在输入输出设备和CPU之间,用来缓存数据。它使得低速的输入输出设备和高速的CPU能够协调工作,避免低速的输入输出设备占用CPU,解放出CPU,使其能够高效率工作。
缓冲区的类型
  缓冲区 分为三种类型:全缓冲、行缓冲和不带缓冲。
  1、全缓冲
&&&&   在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。
  2、行缓冲
&&&&   在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是输入数据。
  3、不带缓冲
&&&&   也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。
缓冲区的刷新
  下列情况会引发缓冲区的刷新:
  1、缓冲区满时;
  2、执行flush语句;
  3、执行endl语句;
  4、关闭文件。
  可见,缓冲区满或关闭文件时都会刷新缓冲区,进行真正的I/O操作。另外,在C++中,我们可以使用flush函数来刷新缓冲区(执行I/O操作并清空缓冲区),如:
  cout&&flush ; // 将显存的内容立即输出到显示器上进行显示
  cout&&endl ; // endl控制符的作用是将光标移动到输出设备中下一行开头处,并且清空缓冲区
  相当于
  cout&&”\n” &&flush ;
  通过实例演示说明
  1、文件操作演示全缓冲
  创建一个控制台工程,输入如下代码:
#include &fstream& 
using namespace  
int main() 
&&& //创建文件test.txt并打开 
 &&ofstream outfile("test.txt"); 
&&& //向test.txt文件中写入4096个字符’a’ 
 & for(int n=0; n & 4096; n++) 
&&&&&& &outfile && 'a'; 
&&& //暂停,按任意键继续 
 &&system("PAUSE"); 
&&& //继续向test.txt文件中写入字符’b’,也就是说,第4097个字符是’b’ 
 &&outfile && 'b'; 
&&& //暂停,按任意键继续 
 &&system("PAUSE"); 
&&return 0; 
  上面这段代码很容易理解,已经在代码内部作了注释。
  编写这段小代码的目的是验证WindowsXP下全缓冲的大小是4096个字节,并验证缓冲区满后会刷新缓冲区,执行真正的I/O操作。
&&&& 编译并执行,运行结果如下:
此时打开工程所在文件夹下的test.txt文件,您会发现该文件是空的,这说明4096个字符“a”还在缓冲区,并没有真正执行I/O操作。敲一下回车键,窗口变为如下:
  此时再打开test.txt文件,您就会发下该文件中已经有了4096个字符“a”。这说明全缓冲区的大小是4K(4096),缓冲区满后执行了I/O操作,而字符“b”还在缓冲区。
  再次敲一下回车键,窗口变为如下:
  此时再打开test.txt文件,您就会发现字符“b”也在其中了。这一步验证了文件关闭时刷新了缓冲区。
  2、操作演示行缓冲
  先介绍getchar()函数。
  函数原型:int getchar(void) ;
  说明:当程序调用getchar()函数时,程序就等着用户按键,用户输入的字符被存放在键盘缓冲区中,直到用户按回车为止(回车字符也放在缓冲区中)。当用户键入回车之后,getchar()函数才开始从键盘缓冲区中每次读入一个字符。也就是说,后续的getchar()函数调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完后,才重新等待用户按键。
  不知道您明白了没有,再通俗一点讲,当程序调用getchar()函数时,程序就等着用户按键,并等用户按下回车键返回。期间按下的字符存放在缓冲区,第一个字符作为函数返回值。继续调用getchar()函数,将不再等用户按键,而是返回您刚才输入的第2个字符;继续调用,返回第3个字符,直到缓冲区中的字符读完后,才等待用户按键。
  如果您还没有明白,只能怨我表达能力有限,您可以结合以下实例体会。
  创建一个控制台工程,输入如下代码:
#include&iostream& 
using namespace  
int main() 
&&char c;  
&&&&//第一次调用getchar()函数,程序执行时,您可以输入一串字符并按下回车键,按下回车键后该函数返回。返回值是用户输入的第一个字符 (假设用户输入了&abcdef,函数返回a)
 &&c&=&getchar(); 
&&& //显示getchar()函数的返回值
 &&cout&&&c&&&& //&输出&a &
&&&&//&循环多次调用getchar()函数,将每次调用getchar()函数的返回值显示出来,直到遇到回车符才结束。&这时函数执行不会让用户输入而是顺序读取缓冲区字符内容。第一个字符用户输入结束后已经读取,所以会从第二个字符开始读
&& while((c&=&getchar())!='\n') 
&&&& &&cout&&&","&&&&c&&&endl 
&& return 0;
&&&&& 这段小代码也很简单,同样在代码内部都有注释。最后输出结果是
  getchar()函数的执行就是采用了行缓冲。第一次调用getchar()函数,会让程序使用者(用户)输入一行字符并直至按下回车键 函数才返回。此时用户输入的字符和回车符都存放在行缓冲区。
  再次调用getchar()函数,会逐步输出行缓冲区的内容。
  3、标准错误输出不带缓冲
  如错误输出时使用:
  cerr&&”错误,请检查输入的参数!” ;
  这条语句等效于:
  fprintf(stderr, ”错误,请检查输入的参数!”) ;
&&&& 看完了缓冲区内容我们来学习C++标准IO库。
8.1 面向对象的标注库
&&& 先来看IO操作类图:
&&&& IO库大致可操作三类数据: 控制台流(stream)&, 文件(file) , 字符串&(string)。
&&&&&操作类型又可分三类:输入(in), 输出(out) ,输入与输出(in/out)。
&&&&&ostream 是所有类型输出操作的基类
&&&&&&&& 它扩展出两个子类: ofstream针对文件的输出操作类;ostringstream针对string的输出操作类
&&&&&istream 是所有类型输入操作的基类
&&&&&&&& 它扩展出两个子类: ifstream针对文件的输入操作类;istringstream针对string的输入操作类
&&&& ostream 和&istream共同扩展出类 iostream 它负责处理控制台stream的输入和输出
&&&&&&&& iostream 扩展出两个子类:stringstream专门处理string的输入输出; fstream专门处理文件的输入输出
&&&& 所有类定义在不同头文件,正确使用类必须引入对应的头文件
&&&&&&&& iosteam头文件中定义了:
&&&&&&&&& &&& istream(从流中读取);
&&&&&&&&&& && ostream(写到流中);
&&&&&&&&&&& & iostream(对流进行读写,从istream和osteam派生而来)
&&&&&&&& fsteam头文件中定义了:
&&&&&&&&&&&&& ifstream(从文件中读取,由istream派生而来);
&&&&&&&&&&&&& ofstream(写到文件中去,由ostream派生而来);
&&&&&&&&&&&&& fstream(对文件进行读写,由iostream派生而来);
&&&&&&&& ssteam头文件中定义了:
&&&&&&&&&&&&& istringstream(从string对象中读取,由istream派生而来);
&&&&&&&&&&&&& ostringstream(写到string对象中去,由ostream派生而来);
&&&&&&&&&&&&& stringstream(对string对象进行读写,由iostream派生而来);
&&&& 要想正确使用IO类一定要理解他们的对应关系和每个类的职责。&&&&& c++的标准输入输出库iostream 是一个类库,以类的形式组织,使用该库中的类要先引用命名空间:&&& 最常使用的是cin和cout,这两个都是对象,cin是istream类的对象,cout是ostream类的对象,而输入的 cin&& 与输出时的 cout&& 中的左移 && 与右移 && 分别是 istream 类与 ostream 类的操作符重载。&&& iostream库里面创建了3个标准流对象:&&&&& cin 表示标准输入的istream对象,cin可以使我们从设备读取数据。&&&&&&cout 表示标准输出的ostream对象,cout可以使我们向设备写入数据。&&&&&&cerr 表示标准错误的ostream对象,cerr是导出程序错误消息的地方,只能向屏幕设备写数据。&&& 标准的流对象都有默认的设备:&&& cout && data ;& cout默认的设备是显示器缓冲区。&&& cin && data ;& cin默认的设备是键盘缓冲区。
&&&& 不管是输出到屏幕还是写入string或者保存到txt文本,字符都是不可获取的。通常我们不仅仅使用英文标准字符,我们还可能输入输出中文字符或其他非英文字符。这时候需要国际字符支持。
&&&& 例如我们要保存一个字符 'a' 可以定义 char, 但是我们要保存字符 '家'&就无法按使用char 而要使用 wchar_t 了。IO类也有这样的区分,例如我们要在控制台输出中文字符就只能用 wcout && "你好" && endl想要将中文保存到文本就要用wofstream 或 wfstream 。要正确读取包含中文字符文件要使用 wifstream 类。
&&&& IO对象无法复制或者赋值,所以io对象作为函数形参或返回值时只能使用指针或引用。
&&& &iostream &Getio(iostream &io, fstream *fs){ ...} // 正确,参数和返回以引用或指针形式传递
&&&& iostream Getio(iostream io){ ...}& // 错误,参数和返回以拷贝方式传递会发生复制和赋值操作
8.2 条件状态
&&& IO对象在任意时候都对应一种状态:比如有效状态(还未处理或者正确处理完毕时的状态),比如失败状态(处理失败时),比如数据流被破坏(文件错误)等等。看下面的表
&&&&strm::iostate&& & // 机器相关的整型名,由各个iostream类定义,用于定义条件状态
&&& strm::badbit&&& &&// strm::iostate类型的值,用于指出被破坏的流
&&& strm::failbit&&&& &&// strm::iostate类型的值,用于指出失败的IO操作
&&& strm::eofbit&&&&& &//
strm::iostate类型的值,用于指出流已经到达文件结束符
&&& s.eof()&&&&&&&&&&&& & // 如果设置了流s的eofbit值,则该函数返回true
&&& s.fail()&&&&&&&&&&&& &&// 如果设置了流s的failbit值,则该函数返回true
&&& s.bad()&&&&&&&&&&& &&// 如果设置了流s的badbit值,则该函数返回true
&&& s.good()&&&&&&&&& &&// 如果流s处于有效状态,则该函数返回true
&&& s.clear()&&&&&& &&& &// 将流s中的所有状态值都重设为有效状态
&&& s.clear(flag)&&&&&&// 将流s中的某个指定条件状态设置为有效。flag的类型是strm::iostate
&&& s.setstate(flag)&// 给流s添加指定条件。flag的类型是strm::iostate
&&& s.rdstate()&&&&&&&&// 返回流s的当前条件,返回值类型为strm::iostate
&&& 这么多操作我们该如何理解呢。
&&& 上面说过,任何一个IO对象在任意时刻都有一种状态。iostate 就是代表状态的枚举。badbit,failbit,eofbit,goodbit是iostate的一个具体值,看下面代码
int&main()
&&&&cout&&&&std::iostream::good&&&&std::iostream::badbit&&&&std::iostream::eofbit&&&&std::iostream::failbit&&&&&&//&输出&0&1&2&4
&&&&std::iostream::iostate&coutstate&=&cin.rdstate();&//&得到cin对象的原始状态值
&&&&cout&&&&coutstate&&&&&&//&输出&0&cin的状态值是&std::iostream::good
&&&&int&i;
&&&&cin&&&&i;&//&输入"123"
&&&&cout&&&&cin.rdstate()&&&&&&//&输出&0&因为可以被正确转成int并被存入i&所以cin的状态置为&std::iostream::good&
&&&&cout&&&&cin.good()&&&&cin.eof()&&&&cin.fail()&&&&cin.bad()&&&&&//&输出&1&0&0&0
&&&&cin&&&&i;&//&输入"abd"
&&&&cout&&&&cin.rdstate()&&&&&&//&输出&4&&因为“abc”无法转成int存入i&所以cin的状态置为&std::iostream::failbit&
&&&&cout&&&&cin.good()&&&&cin.eof()&&&&cin.fail()&&&&cin.bad()&&&&&//&输出&0&0&1&0
&&&&cin.clear();&//&重置cin状态为std::iostream::good&否则下面的cin&&&&i&不会执行,&或者这样设置cin.clear(std::iostream::failbit)
&&&&cin&&&&i;&//&输入"568"
&&&&cout&&&&cin.rdstate()&&&&&&//&输出&0&因为可以被正确转成int并被存入i&所以cin的状态置为&std::iostream::good
&&&&cout&&&&cin.good()&&&&cin.eof()&&&&cin.fail()&&&&cin.bad()&&&&&//&输出&1&0&0&0
8.3 输出缓冲区的管理
&&& 关于缓冲区内容本章开头有介绍。
&&& 这里介绍tie()函数用法,函数可将输出流与输入流关联起来,在这种情况下在读取输入流时将刷新关联的输出流,下面代码解释了含义
#include &fstream& 
using namespace  
int main() 
&&& //创建文件test.txt并打开 
ofstream outfile("test.txt"); 
&&& //将输出流对象 outfile 和输入cin关联起来
&&& cin.tie(&outfile);
&&& //向test.txt文件中写入字符串
 & outfile && “abcdef”; 
&&& // 执行输入语句时会立刻刷新关联的输出流,字符串被写到文件中 
 & cin && in; 
&&& return 0; 
&&& 如果不用tie()函数做关联,main 方法执行完毕字符串"abcedf"才会被刷新到test.txt文本文件(执行完毕后系统会自动关闭文件从而刷新缓冲区内容到文件)。
&&& tie()关联之后第一次执行到 cin 语句系统会立刻刷新关联的输出流,所以内容会立刻写入文件不必等到main执行完毕。
8.4 文件流对象的使用&&&
&&& C++ 通过以下几个类支持文件的输入输出:
&&& ofstream: 写操作(输出)的文件类 (由ostream引申而来)
&&& ifstream: 读操作(输入)的文件类(由istream引申而来)
&&& fstream: 可同时读写操作的文件类 (由iostream引申而来)
&&& 打开文件(Open a file)
&&&&&&&&& 对这些类的一个对象所做的第一个操作通常就是将它和一个真正的文件联系起来,也就是说打开一个文件。被打开的文件在程序中由一个流对象(stream object)来表示 (这些类的一个实例) ,而对这个流对象所做的任何输入输出操作实际就是对该文件所做的操作。
&&&&&&&&& 要通过一个流对象打开一个文件,我们使用它的成员函数open() :
&&&&&&&void open (const char *filename, openmode mode) ;
&&&&&&&&& 这里filename 是一个字符串,代表要打开的文件名,mode 是以下标志符的一个组合:
为输入(读)而打开文件
为输出(写)而打开文件
初始位置:文件尾
所有输出附加在文件末尾
ios::trunc
如果文件已存在则先删除该文件
ios::binary
二进制方式
&&&&&&&&& 这些标识符可以被组合使用,中间以”或”操作符(|)间隔。例如,如果我们想要以二进制方式打开文件"example.bin" 来写入一些数据,我们可以通过以下方式调用成员函数open() 来实现:
&&&&&&&&&&& ofstream file ;
&&&&&&&&&&& file.open ("example.bin", ios::out | ios::app | ios::binary) ;
&&&&&&&& &ofstream, ifstream 和 fstream所有这些类的成员函数open 都包含了一个默认打开文件的方式xxstream.open("filepath") ,这三个类的默认方式各不相同:
类参数的默认方式
ios::out | ios::trunc
ios::in | ios::out
&&&&&&&&& 只有当函数被调用时没有声明方式参数的情况下,默认值才会被采用。如果函数被调用时声明了任何参数,默认值将被完全改写,而不会与调用参数组合。
&&&&&&&&& 由于对类ofstream, ifstream 和 fstream 的对象所进行的第一个操作通常都是打开文件,这些类都有一个构造函数可以直接调用open 函数,并拥有同样的参数。这样,我们就可以通过以下方式进行与上面同样的定义对象和打开文件的操作:
&&&&&& ofstream file("example.bin", ios::out | ios::app | ios::binary);&
&&&&&&&&& &// 等价于
& &&&&&&&& ofstream file ;
&&&&&&&&& &file.open("example.bin",ios::out | ios::app | ios::binary) ;
&&&&&&&&& 两种打开文件的方式都是正确的。你可以通过调用成员函数is_open()来检查一个文件是否已经被顺利的打开了:
&&&&&& bool is_open() ;
&&&&&&&&& 它返回一个布尔(bool)值,为真(true)代表文件已经被顺利打开,假( false )则相反。
&&& 关闭文件(Closing a file)
&&&&&&&&& 当文件读写操作完成之后,我们必须将文件关闭以使文件重新变为可访问的。关闭文件需要调用成员函数close(),它负责将缓存中的数据排放出来并关闭文件。它的格式很简单:void close();
&&&&&&&&& 这个函数一旦被调用,原先的流对象(stream object)就可以被用来打开其它的文件了,这个文件也就可以重新被其它的进程(process)所有访问了。
&&&&&&&&& 为防止流对象被销毁时还联系着打开的文件,析构函数(destructor)将会自动调用关闭函数close。
&&& 文本文件(Text mode files)
&&&&&&&&& 类ofstream, ifstream 和fstream 是分别从ostream, istream 和iostream 中引申而来的。这就是为什么 fstream 的对象可以使用其父类的成员来访问数据。
&&&&&&&&& 一般来说,我们将使用这些类与同控制台(console)交互同样的成员函数(cin 和 cout)来进行输入输出。如下面的例题所示,我们使用重载的插入操作符&&
#include&&fiostream.h&
&&&&&&&&&&&&
int&main&()&
&&&&ofstream&examplefile&("example.txt");
&&&&if&(examplefile.is_open())&
&&&&&&&&//&输出到(写入)文件
&&&&&&&&examplefile&&&&"This&is&a&line.\n";&
&&&&&&&&examplefile&&&&"This&is&another&line.\n";
&&&&&&&&examplefile.close();
&&&&return&0;
&&&&&&&&&文件内容是:&&&
&&&&&&&&& This is a line.
&&&&&&&& &This is another line.&&
&&&&&&&&& 从文件中读入数据也可以用与 cin的使用同样的方法:
#include&&iostream.h&
#include&&fstream.h&
#include&&stdlib.h&
&&&&&&&&&&&&
int&main&()&
&&&&char&buffer[256];
&&&&ifstream&examplefile&("example.txt");
&&&&if&(!&examplefile.is_open())
&&&&&&&&cout&&&&"Error&opening&file";&exit&(1);&
&&&&while&(!&examplefile.eof()&)&
&&&&&&& // 从文件输输入(读出)一行内容到 buffer&&&&&&&&
&&&&&&& examplefile.getline&(buffer,100);
&&&&&&&&cout&&&&buffer&&&&
&&&&return&0;
exit()退出程序,把控制权交给OS&
return结束当前函数,返回函数值,把控制权交给调用函数
在main函数中return&与exit用法差不多
&&&&&&&&& 上面的例子读入一个文本文件的内容,然后将它打印到屏幕上。注意我们使用了一个新的成员函数叫做eof ,它是ifstream 从类 ios 中继承过来的,当到达文件末尾时返回true 。
&&&&状态标志符的验证(Verification of state flags)
&&&&&&&&& 除了eof()以外,还有一些验证流的状态的成员函数(所有都返回bool型返回值):
如果在读写过程中出错,返回 true 。例如:当我们要对一个不是打开为写状态的文件进行写入时,或者我们要写入的设备没有剩余空间的时候。
除了与bad() 同样的情况下会返回 true 以外,加上格式错误时也返回true ,例如当想要读入一个整数,而获得了一个字母的时候。
如果读文件到达文件末尾,返回true。
这是最通用的:如果调用以上任何一个函数返回true 的话,此函数返回 false 。
&&&&&&& 要想重置以上成员函数所检查的状态标志,你可以使用成员函数clear(),没有参数。
&&& 获得和设置流指针(get and put stream pointers)
&&&&&&&&& 所有输入/输出流对象(i/o streams objects)都有至少一个流指针:
ifstream, 类似istream, 有一个被称为get pointer的指针,指向下一个将被读取的元素。 ofstream, 类似 ostream, 有一个指针 put pointer ,指向写入下一个元素的位置。 fstream, 类似 iostream, 同时继承了get 和 put
&&&&&&&&&我们可以通过使用以下成员函数来读出或配置这些指向流中读写位置的流指针:
tellg() 和 tellp()
这两个成员函数不用传入参数,返回pos_type 类型的值(根据ANSI-C++ 标准) ,就是一个整数,代表当前get 流指针的位置 (用tellg) 或 put 流指针的位置(用tellp).
seekg() 和seekp()
这对函数分别用来改变流指针get 和put的位置。两个函数都被重载为两种不同的原型:
seekg ( pos_type position ) ;
seekp ( pos_type position ) ;
使用这个原型,流指针被改变为指向从文件开始计算的一个绝对位置。要求传入的参数类型与函数 tellg 和tellp 的返回值类型相同。
seekg ( off_type offset, seekdir direction ) ;
seekp ( off_type offset, seekdir direction ) ;
使用这个原型可以指定由参数direction决定的一个具体的指针开始计算的一个位移(offset)。它可以是:
从流开始位置计算的位移
从流指针当前位置开始计算的位移
从流末尾处开始计算的位移
&&&&&&&&&流指针 get 和 put 的值对文本文件(text file)和二进制文件(binary& file)的计算方法都是不同的,因为文本模式的文件中某些特殊字符可能被修改。由于这个原因,建议对以文本文件模式打开的文件总是使用seekg 和 seekp的第一种原型,而且不要对tellg 或 tellp 的返回值进行修改。对二进制文件,你可以任意使用这些函数,应该不会有任何意外的行为产生。
&&&&&&&&&以下例子使用这些函数来获得一个二进制文件的大小:
#include&&iostream.h&
#include&&fstream.h&
&&&&&&&&&&&&
const&char&*&filename&=&"example.txt";
&&&&&&&&&&&&
int&main&()&
&&&&long&l,m;
&&&&ifstream&file(filename,&ios::in|ios::binary);&&&&
&&&&l&=&file.tellg();
&&&&file.seekg(0,&ios::end);
&&&&m&=&file.tellg();
&&&&file.close();
&&&&cout&&&&"size&of&"&&&&filename&&&&"&is&"&&&&(m-l)&&&&"&bytes.\n";&//&输出&size&of&example.txt&is&40&bytes.&
&&&&return&0;
&&& 二进制文件(Binary files)
&&&&&&&&& 在二进制文件中,使用&& 和 &&,以及函数(如getline)来操作符输入和输出数据,没有什么实际意义,虽然它们是符合语法的。
&&&&&&&&& 文件流包括两个为顺序读写数据特殊设计的成员函数:write 和 read。第一个函数 (write) 是ostream 的一个成员函数,都是被ofstream所继承。而read 是istream 的一个成员函数,被ifstream 所继承。类 fstream 的对象同时拥有这两个函数。它们的原型是:
&&&&&&&&& write ( char * buffer, streamsize size ) ;
&&&&&&&&& read ( char * buffer, streamsize size ) ;
&&&&&&&&& 这里 buffer 是一块内存的地址,用来存储或读出数据。参数size 是一个整数值,表示要从缓存(buffer)中读出或写入的字符数。
#include&&iostream&
#include&&fstream.h&
&&&&&&&&&&&&
const&char&*&filename&=&"example.txt";
&&&&&&&&&&&&
int&main&()&
&&&&char&*&
&&&&ifstream&file&(filename,&ios::in|ios::binary|ios::ate);&//&ios::ate&表示指向文件末尾
&&&&size&=&file.tellg();&//&取得文件大小
&&&&file.seekg&(0,&ios::beg);
&&&&buffer&=&new&char[size];
&&&&file.read(buffer,&size);
&&&&file.close();
&&&&&&&&&&&&
&&&&cout&&&&"the&complete&file&is&in&a&buffer";
&&&&&&&&&&&&
&&&&delete[]&
&&&&return&0;
&&& 缓存和同步(Buffers and Synchronization)
&&&&&&&&& 当我们对文件流进行操作的时候,它们与一个streambuf 类型的缓存(buffer)联系在一起。这个缓存(buffer)实际是一块内存空间,作为流(stream)和物理文件的媒介。例如,对于一个输出流, 每次成员函数put& (写一个单个字符)被调用,这个字符不是直接被写入该输出流所对应的物理文件中的,而是首先被插入到该流的缓存(buffer)中。
&&&&&&&&& 当缓存被排放出来(flush)时,它里面的所有数据或者被写入物理媒质中(如果是一个输出流的话),或者简单的被抹掉(如果是一个输入流的话)。这个过程称为同步(synchronization),它会在以下任一情况下发生:
当文件被关闭时: 在文件被关闭之前,所有还没有被完全写出或读取的缓存都将被同步。 当缓存buffer 满时:缓存Buffers 有一定的空间限制。当缓存满时,它会被自动同步。 控制符明确指明:当遇到流中某些特定的控制符时,同步会发生。这些控制符包括:flush 和endl。 明确调用函数sync(): 调用成员函数sync() (无参数)可以引发立即同步。这个函数返回一个int 值,等于-1 表示流没有联系的缓存或操作失败。
&&&&&在C++中,有一个stream这个类,所有的I/O都以这个“流”类为基础的,包括我们要认识的文件I/O,stream这个类有两个重要的运算符:
&&&& 1、插入器 (&&)
&&&&&&& &向流输出数据。比如说系统有一个默认的标准输出流(cout),一般情况下就是指的显示器,所以,cout&&"Write Stdout"&&'n';就表示把字符串"Write Stdout"和换行字符('n')输出到标准输出流。
&&&& 2、析取器 (&&)
&&&&&&&& 从流中输入数据。比如说系统有一个默认的标准输入流(cin),一般情况下就是指的键盘,所以,cin&&x;就表示从标准输入流中读取一个指定类型(即变量x的类型)的数据。
&&&&&&&&& 在C++中,对文件的操作是通过stream的子类fstream(file stream)来实现的,所以,要用这种方式操作文件,就必须加入头文件fstream.h。下面就把此类的文件操作过程一一道来。
&&&&&&&& &一、打开文件
&&&&&&&&&&&&&&& 在fstream类中,有一个成员函数open(),就是用来打开文件的,其原型是:
&&&&&&&&&&&&&&& void open(const char* filename, int mode, int access) ;
&&&&&&&&&&&&&&& 参数:
&&&&&&&&&&&&&&& filename: 要打开的文件名
&&&&&&&&&&&&&&& mode: 要打开文件的方式
&&&&&&&&&&&&&&& access: 打开文件的属性
&&&&&&&&&&&&&&& 打开文件的方式在类ios(是所有流式I/O类的基类)中定义,常用的值如下:
&&&&&&&&&&&&&&&&ios::app:&&&&&&&&& 以追加的方式打开文件
&&&&&&&&&&&&&&& ios::ate:&&&&&&&&&& 文件打开后定位到文件尾,ios:app就包含有此属性
&&&&&&&&&&&&&&& ios::binary:&&&&& 以二进制方式打开文件,缺省的方式是文本方式。两种方式的区别见前文
&&&&&&&&&&&&&&& ios::in:&&&&&&&&&&&& 文件以输入方式打开
&&&&&&&&&&&&&&& ios::out:&&&&&&&&&& 文件以输出方式打开
&&&&&&&&&&&&&&& ios::nocreate:& 不建立文件,所以文件不存在时打开失败
&&&&&&&&&&&&&&& ios::noreplace:不覆盖文件,所以打开文件时如果文件存在失败
&&&&&&&&&&&&&&& ios::trunc:&&&&& & 如果文件存在,把文件长度设为0
&&&&&&&&&&&&&&& 可以用“或”把以上属性连接起来,如 ios::out| ios::binary
&&&&&&&&&&&&&& 打开文件的属性取值是:
&&&&&&&&&&&&&& 0:普通文件,打开访问
&&&&&&&&&&&&&& 1:只读文件
&&&&&&&&&&&&&& 2:隐含文件
&&&&&&&&&&&&&& 4:系统文件
&&&&&&&&&&&&&& 可以用“或”或者“+”把以上属性连接起来 ,如3或 1|2 就是以只读和隐含属性打开文件。
&&&&&&&&&&&&&& 例如:以二进制输入方式打开文件c:config.sys
&&&&&&&&&&&&& &fstream file1 ;
&&&&&&&&&&&&& &file1.open("config.sys", ios::binary | ios::in,0) ;
&&&&&&&&&&&&&& 如果open函数只有文件名一个参数,则是以读/写普通文件打开,即:
&&&&&&&&&&&&&& file1.open("config.sys") ; 等价于 file1.open("config.sys",ios::in|ios::out,0) ;&
&&&&&&&&&&&&&& 另外,fstream还有和open()一样的构造函数,对于上例,在定义的时侯就可以打开文件了:
&&&&&&&&&&&&&& fstream file1("config.sys") ;
&&&&&&&&&&&&&& 特别提出的是,fstream有两个子类:ifstream(input file stream)和ofstream(outpu file stream),ifstream默认以输入方式打开文件,而ofstream默认以输出方式打开文件。
&&&&&&&&&&&&&& ifstream file2("dos.def") ; //以输入方式打开文件
&&&&&&&&&&&&&&ofstream file3("x.123") ;&&&//以输出方式打开文件
&&&&&&&&&&&&&& 所以,在实际应用中,根据需要的不同,选择不同的类来定义:如果想以输入方式打开,就用ifstream来定义;如果想以输出方式打开,就用ofstream来定义;如果想以输入/输出方式来打开,就用fstream来定义。
&&&&&&&&& 二、关闭文件
&&&&&&&&&&&&&&&打开的文件使用完成后一定要关闭,fstream提供了成员函数close()来完成此操作,如:file1.close();就把file1相连的文件关闭。
&&&&&&&&& 三、读写文件
&&&&&&&&&&&&&&& 读写文件分为文本文件和二进制文件的读取,对于文本文件的读取比较简单,用插入器和析取器就可以了;而对于二进制的读取就要复杂些,下要就详细的介绍这两种方式
&&&&&&&&&&&&&&& 1、文本文件的读写
&&&&&&&&&&&&&&&&&&&& 文本文件的读写很简单:用插入器(&&)向文件输出;用析取器(&&)从文件输入。假设file1是以输入方式打开,file2以输出打开。示例如下:
&&&&&&&&&&&&&&&&&&&& file2&&"I Love You" ;& // 向文件写入字符串"I Love You"
&&&&&&&&&&&&&&&&&&& &int i ;
&&&&&&&&&&&&&&&&&&&& file1 && i ;& // 从文件输入一个整数值。
&&&&&&&&&&&&&&&&&&&& 这种方式还有一种简单的格式化能力,比如可以指定输出为16进制等等,具体的格式有以下一些
&&&&&&&&&&&&&&&&&&&& 操纵符 功能 输入/输出
&&&&&&&&&&&&&&&&&&& dec&&& 格式化为十进制数值数据 输入和输出
&&&&&&&&&&&&&&&&&&& endl&& 输出一个换行符并刷新此流 输出
&&&&&&&&&&&&&&&&&&& ends& 输出一个空字符 输出
&&&&&&&&&&&&&&&&&&& hex&& 格式化为十六进制数值数据 输入和输出
&&&&&&&&&&&&&&&&&&& oct&&& 格式化为八进制数值数据 输入和输出
&&&&&&&&&&&&&&&&&&& setpxecision(int p) 设置浮点数的精度位数 输出
&&&&&&&&&&&&&&&&&&&& 比如要把123当作十六进制输出:file1&&hex&&123;要把3.位精度输出:file1&&setpxecision(5)&&3.1415926。
&&&&&&&&&&&&&&&&2、二进制文件的读写
&&&&&&&&&&&&&&&&&&& ①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个字符时终止。
&&&&&&&&&&&&&&&&&&& ③读写数据块
&&&&&&&&&&&&&&&&&&&&&& 要读写二进制数据块,使用成员函数read()和write()成员函数,它们原型如下:
&&&&&&&&&&&&&&&&&&&&&& read(unsigned char *buf,int num) ;
&&&&&&&&&&&&&&&&&&&&&& write(const unsigned char *buf,int num) ;
&&&&&&&&&&&&&&&&&&&&&& read() 从文件中读取 num 个字符到 buf 指向的缓存中,如果在还未读入 num 个字符时就到了文件尾,可以用成员函数 int gcount();来取得实际读取的字符数;而 write() 从buf 指向的缓存写 num 个字符到文件中,值得注意的是缓存的类型是 unsigned char *,有时可能需要类型转换。
&&&&&&&&&&&&&&&&&&&&&&&例:
&&&&&&&&&&&&&&&&&&&&&& unsigned char str1[]="I Love You";
&&&&&&&&&&&&&&&&&&&&&& int n[5];
&&&&&&&&&&&&&&&&&&&&&& ifstream in("xxx.xxx");
&&&&&&&&&&&&&&&&&&&&&& ofstream out("yyy.yyy");
&&&&&&&&&&&&&&&&&&&&&& out.write(str1,strlen(str1));//把字符串str1全部写到yyy.yyy中
&&&&&&&&&&&&&&&&&&&&&& in.read((unsigned char*)n,sizeof(n));//从xxx.xxx中读取指定个整数,注意类型转换
&&&&&&&&&&&&&&&&&&&&&& in.close();& out.close();
&&&&&&&&&&四、检测EOF
&&&&&&&&&&&&&& 成员函数eof()用来检测是否到达文件尾,如果到达文件尾返回非0值,否则返回0。原型是int eof();
&&&&&&&&&&&&&&&例: if(in.eof()) ShowMessage("已经到达文件尾!") ;
&&&&&&&&& 五、文件定位
&&&&&&&&&&&&&&& 和C的文件操作方式不同的是,C++& I/O系统管理两个与一个文件相联系的指针。一个是读指针,它说明输入操作在文件中的位置;另一个是写指针,它下次写操作的位置。每次执行输入或输出时相应的指针自动变化。
&&&&&&&&&&&&&&&&所以,C++的文件定位分为读位置和写位置的定位,对应的成员函数是 seekg()和 seekp(),seekg()是设置读位置,seekp是设置写位置。它们最通用的形式如下:
&&&&&&&&&&&&&&& istream &seekg(streamoff offset,seek_dir origin);
&&&&&&&&&&&&&&& ostream &seekp(streamoff offset,seek_dir origin);
&&&&&&&&&&&&&&& streamoff定义于 iostream.h 中,定义有偏移量 offset 所能取得的最大值,seek_dir 表示移动的基准位置,是一个有以下值的枚举:
&&&&&&&&&&&&&&&&ios::beg: 文件开头
&&&&&&&&&&&&&&& ios::cur: 文件当前位置
&&&&&&&&&&&&&&& ios::end: 文件结尾
&&&&&&&&&&&&&&& 这两个函数一般用于二进制文件,因为文本文件会因为系统对字符的解释而可能与预想的值不同。
&&&&&&&&&&&&&&& 例:
&&&&&&&&&&&&&&& file1.seekg(1234,ios::cur) ;& //把文件的读指针从当前位置向后移1234个字节
&&&&&&&&&&&&&&&&file2.seekp(1234,ios::beg) ; //把文件的写指针从文件开头向后移1234个字节
阅读(...) 评论()}

我要回帖

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信