c++语言循环读写文件夹,【C++探索之旅】第一部分第十课:文件读写,海阔凭鱼跃...

cfd927bddf9130b21c1af2e668bf2b7a.png

内容简介

1、第一部分第十课:文件读写,海阔凭鱼跃

2、第一部分第十一课预告:小练习,猜单词

文件读写,海阔凭鱼跃

上一课《【C++探索之旅】第一部分第九课:数组威武,动静合一》中,我们学习了动态数组和静态数组,也看到其实字符串很类似字符数组(到了之后的第二部分,学习面向对象,我们会知道其实string是一个类)。

到目前为止,我们写的程序还比较简单,当然了,因为我们刚开始学习C++嘛。但只要加以训练,我们就慢慢地能够写一些真正的应用了。我们也开始逐渐了解C++的基础知识了,不过缺了很重要的一环:与文件交互。

我们已经学会如何将信息输出到控制台(console)以及如何提取用户在控制台中输入的数据(使用cin和cout)。但是,我们岂能就此罢休。想想我们

之前介绍过的一些程序,例如:记事本,一些IDE(VS, CodeBlocks, xCode, Eclipse,

etc),绘图软件,等等,都能够读写文件。

在游戏领域就更是如此啦(我知道一帮宅男已经激动了):游戏里的数据要保存,游戏的图片,音乐,道具,等等。都需要存档。

总之,如果一个软件不会与文件交互,那么它的功能是比较有限的。

因此,一起来学习如何读写文件吧。你会发现,如果你掌握了cin和cout的用法,那其实你已经知道大半啦。

写入文件

我们要读写文件,首先需要打开文件。就好像平时我们要记笔记一样,你总得先打开笔记本吧,才能阅读内容,或者往里面写东西。

一旦文件被打开之后,接下来的操作就很类似之前用cin和cout来进行标准输入和输出了。我们又会与老朋友<>见面。

用术语来说,我们会将一个程序和外界的通信方式用"流"来描述。流,英语是stream。记得吗?我们要使用cin和cout,需要用

#include 

因为cin和cout定义在iostream这个C++的标准库中。而这里的iostream就是input output stream的缩写,表示"输入输出流"。所以,其实我们早就在不知不觉地接触流的概念了。

在这一章中,我们要和文件交互,那么就需要文件流来帮忙了。聪明如你一定想到了,是的,文件的英语是file,那么文件流就是file stream。是不是很简单呢?

因此,我们需要用到fstream这个标准库,fstream就是file stream的缩写。

当然了,如果你不是参加一个程序员的派对,也不需要显得很专业,那么说"读写文件"就可以了。

fstream头文件

在C++中,我们要使用一个功能,需要引入合适的头文件。因此,我们在程序一开始须要这样做:

#include 

接下来,我们就学习如何创建一个文件流,以便我们能读写文件。

以写模式打开文件

流其实是对象,还记得我们说C++是一门面向对象的语言吗?当然我们现在还不深究,要到第二部分讲面向对象编程时才会畅聊类和对象。暂时只需要知道这些流其实都是C++的对象(不是找对象的对象,少年你想多了)。

也完全无需害怕,因为我们之后还会不断提到流。暂时,只需把其看作比较高级的变量就可以了。这些文件流包含了文件的很多信息,提供给我们很多功能,例如可以关闭文件,在文件中移动,等等。

会看到,声明一个流的对象,其实就和我们声明变量一样简单。首先,我们来看看如何创建用于写文件的流,须要用到ofstream,也就是output

file stream,因为是从程序向文件输入数据,因此对于程序来说是"出去"的流,因此是output(输出),而不是input(输入)。

话休絮烦。翠花,上"栗子":

#include 

#include 

using namespace std;

int main()

{

ofstream myStream("C:/Cpp/Files/scores.txt");

//声明用于写入文件的流,

//此文件是在C盘下的Cpp文件夹的子文件夹Files中的scores.txt文件

return 0;

}

在上面程序中,我在myStream后面的括号中指定了文件的路径.这个路径可以有两种形式:

绝对路径:就是文件的所在,不过是从根文件夹开始的路径。例如:C:/Cpp/Files/scores.txt

相对路径:也是文件的所在,不过是相对于你的程序的路径。例如,你的程序位于C:/Cpp/,那么如果你的文件是在C:/Cpp/Files/scores.txt,你在程序里指定文件的相对路径时就要写 Files/scores.txt

自此,我们就可以使用这个文件流来写文件啦。

如果文件不存在,那么会被自动创建。不过,至少指定的目录要存在,不然会出现"目录不存在"的错误。在我们上面的例子中,至少目录C:/Cpp/Files必须事先存在。

在打开文件的时候,也会有其他问题。例如文件不属于你,或者磁盘已满,等等,总之,打开失败。因此,我们为了保险起见,总要测试文件是否顺利被打开。我们使用 if (myStream) 的方法来测试。

ofstream myStream("C:/Cpp/Files/scores.txt");  //试着打开这个文件

if(myStream)  //测试打开文件是否成功

{

//一切顺利,我们可以使用此文件了

}

else

{

cout <

}

至此,我们已经做好了写文件的准备工作。你会看到,接下来的操作还是有点眼熟的。

向流中写入数据

前面我们说过写入文件的操作就和以前我们使用cout类似。因此当我对你说要使用<

#include 

#include 

#include 

using namespace std;

int main()

{

string const fileName("C:/Cpp/Files/scores.txt");

ofstream myStream(fileName.c_str());

if(myStream)

{

myStream <

myStream <

int age(23);

myStream <

}

else

{

cout <

}

return 0;

}

上面的程序中,可以看到我们首先声明了一个string的变量,里面存放了C:/Cpp/Files/scores.txt这个字符串,不过之后在将其赋给ofstream的对象myStream时,我们却用了c_str()这个函数,这是为什么呢?

其实,ofstream接受的参数是char *(暂时不需要知道是什么,马上我们会学习指针的知识,到时就清楚了),c_str()函数就是用于将string转换成char *

运行此程序,不出意外的话,你的电脑的C:/Cpp/Files/目录下就会多出一个文件 scores.txt, 里面的内容如下所示:

b3861088fec94db7bfc02296f4f5694e.png

你也来试试

你也可以写一个程序,请求用户输入自己的名字和年龄,然后你的程序将这些信息写入文件。

文件的不同打开模式

我们只需要再处理一个小问题:

假如文件已经存在,那怎么办呢?

如果运行上面的已有程序,那么文件的内容会被删除,然后替换为你写入的内容。但是假如我们想要保留文件本来的内容,只是想在文件末尾追加我们的新内容呢?

不用怕,肯定有办法的。只需要在打开文件的时候添加第二个参数,用于指明文件的打开模式,如下所示:

ofstream myStream("C:/Cpp/Files/scores.txt", ios::app);

app是英语append的缩写,表示"追加",也就是说写入的内容不会覆盖原本文件里的内容,而是追加到文件末尾。

读取文件

我们学习了如何写文件,现在来学习如何读取文件内容吧。你会看到,两种操作是很类似的。

以读的形式打开文件

之前我们用了ofstream的对象,那么这次就要用到ifstream的对象了,ifstream是input file stream的缩写。当然也需要测试文件是否顺利被打开。

ifstream myStream("C:/Cpp/Files/scores.txt");  //试着打开文件

if(myStream)

{

//可以读取文件

}

else

{

cout <

}

没有什么新的难点不是吗?

接下来我们就可以读取文件内容了。

要读取文件内容,有三种不同的方式:一行一行地读取,用getline()函数

一个词一个词地读取,用>>

一个字符一个字符地读取,用get()函数

我们分别来学习这三种方式:

一行一行地读取

第一种方式可以一次读取整一行的内容,将其存储在一个字符串里。举例如下:

string line; // 储存整行内容的字符串变量

getline(myStream, line); //读取整一行,存储到line中

此函数的原理和cin是类似的。

一个词一个词地读取

第二种方式,其实你也早就知道了,毕竟聪慧如你嘛。举例如下:

double number;

myStream >> number; //从文件中读取一个浮点数

string word;

myStream >> word;    //从文件中读取一个单词

个方法会读取当前所在的文件位置处的内容和之后的一个空格("词"并不是我们平时说的一个单词,而是以空格来分隔的,假如中间没有空格,那么就是一个词,

例如heusyg3这是一个词,但是heu

syg3却被认为是两个词,因为中间存在空格)。读取的内容根据变量的类型会被转换成double,int,string,等等。

一个字符一个字符地读取

第三种方式,我们之前没学过,不过也很简单就是了。举例如下:

char a;

myStream.get(a);

上面的代码读取一个字符,将其存储在char型变量a中。

这个方法可以读取所有字符,不管是字母,空格,回车符,制表符,等等。

还记得在【C++探索之旅】第一部分第五课:简易计算器中,

我们学习过cin的用法吗?还记得我们说过在cin>>和getline之间需要使用cin.ignore()吗?因此,这里我们从一个词一

个词地读取(用cin>>)转换到一行一行地读取(用getline()),也需要在之间加入ignore()。不过,因为我们这里是在读取

文件,所以不能用cin.ignore(),而要使用ifstream的ignore方法,如下所示:

ifstream myStream("C:/Cpp/Files/scores.txt");

string word;

myStream >> word;          //读取一个词

myStream.ignore();        //改变读取方式

string line;

getline(myStream, line); //读取一整行

一次读取整个文件

很多时候,我们会希望读取整个文件。我们已经学习了如何读取文件,但是还没学习当到达文件结尾时,如何停止。

了获知我们是否还可以继续读取,可以用getline函数的返回值。getline函数的返回值是一个bool(布尔值),如果等于true,还可以继续

读,说明还没到文件末尾;如果等于false,那么说明已经读取了文件的最后一行或者出错了。在false的情况下,就不能再继续读取了。

还记得我们学过的循环吗?只要还没到达文件末尾(getline函数返回是true),我们就继续读取文件。while循环就是最好的选择啦。看如下例子:

#include 

#include 

#include 

using namespace std;

int main()

{

ifstream file("C:/Cpp/Files/scores.txt"); // 尝试打开文件

if(file)

{

//文件顺利打开,可以读取了

string line;  //存储读取的一整行的变量

while(getline(file, line))  //只要没到达文件末尾,我们就一直一行一行地读取

{

cout <

//在控制台显示读取的行

//或者随便你拿这一行干什么,由你决定

}

}

else

{

cout <

}

return 0;

}

一旦我们读取了这些行,我们就可以非常方便地操作它们了。在上面的例子中,我们只是把读取的每一行显示在控制台中,但是你可以随便怎么用。

一些小技巧

这一课的最后,我们来学习几个小技巧,这样文件读写我们就学习得差不多了。

提前关闭文件

我们已经知道怎么打开一个文件,但还没演示如何关闭文件。倒不是因为我忘记了,而是之前关闭文件显得没有那么必要。一旦我们跳出了文件流声明的区块,打开的文件就会被自动关闭。例如:

void f()

{

ofstream myStream("C:/Cpp/Files/scores.txt"); //打开文件

// 操作文件

}  //当我们跳出这个函数,文件就自动被关闭了

因此,并不需要做任何操作来显式地关闭文件。

但是,有时候我们想要提前关闭文件,在它被自动关闭前。为了达到这个目的,我们必须"不择手段"... 哦,不是,是使用close函数。例如:

void f()

{

ofstream myStream("C:/Cpp/Files/scores.txt");

//打开文件C:/Cpp/Files/scores.txt

//使用文件

myStream.close();  //关闭文件

//自此,我们将不能再往文件里写东西了

}

同样地,我们也可以推迟打开文件。用open函数。例如:

void f(){

ofstream myStream;  //声明文件流,但没有绑定文件

myStream.open("C:/Cpp/Files/scores.txt");

//打开文件C:/Cpp/Files/scores.txt

//使用文件

myStream.close();  //关闭文件

//自此,我们将不能再往文件里写东西了

}

正如你所见,以上的操作都很简单。然而,在大部分时候,没必要使用open和close函数来显示地打开和关闭文件。

文件里的游标

我们再来深入一些技术细节,"研究"一下文件的读取是怎么运作的。

你还记得平时用文本编辑器的时候,我们在编辑文本时总会有一个一闪一闪的光标(cursor),指示了我们当前编辑的位置吗?如下图所示:

ee32b84a315441bbb48012ca3a10fcf5.png

可以看到,目前光标位于Oscar的后面。

在C++中操作文件时,也是同样的原理。有一个游标(cursor)一直指示当前在文件中的位置。

例如,当我们运行这一行的时候:

ifstream file("C:/Cpp/Files/scores.txt");

文件C:/Cpp/Files/scores.txt会被打开,游标会定位于文件最开始处。

如果之后我们读取第一个词,就会读取到Oscar这个词。读取完之后,我们的游标就会位于下一个单词的开始处了,如下图所示:

4046b917947c6dfaec950dbbccedb788.png

可以看到,现在游标位于is这第二个词的开始处了。然后我们可以接着读取第二个词,第三个,... 一直到文件结束。

但如果这样的话,我们只能按顺序读取文件,这可太束缚了。我们需要自由,需要飞翔,"在你的心上,自由地飞翔~" (小编,你的药已经准备好了...)

幸好,我们能够在文件中移动,说到移动,那就是移动那个cursor(游标)了。例如,我们可以说"我要移动到距离文件开始处20个字符的地方",或者"我要从当前位置前进32个字符"。这样,我们就可以很方便地读取我们真正想要的内容了。

首先,我们要了解游标目前位于哪里。然后才能正确地移动。

获得在文件中的位置

有一个方法可以获知当前我们的游标位于文件的第几个字符处(从文件开始处算起)。不过,对于输入文件流(ifstream)和输出文件流(ofstream),所用的函数不一样,而且名字也有点古怪,我们列在下面:

针对ifstream针对ofstreamtellg()tellp()

然而,这两个函数的使用方法完全一样。因此只介绍其中一个就可以了。举例如下:

ofstream file("C:/Cpp/Files/scores.txt");

int position = file.tellp(); //获取当前位置

cout <

在文件中移动

用于在文件中移动的函数也有两个,成对的,每一个对应一种流的形式:

针对ifstream针对ofstreamseekg()seekp()

用法和之前的两个函数类似。

这两个函数接受两个参数:一个是在文件中的位置,另一个是相对文件中的位置的距离数(字符数/字节数)。

myStream.seekp(numberOfCharacters, position);

对于此函数的position参数,有三种可能的位置:

文件开始处 : ios::beg ;

文件末尾处 : ios::end ;

当前位置 : ios::cur.

例如,我想要移动到距离文件开始处10个字符的地方,我会这么做:

myStream.seekp(10, ios::beg);

假如我想要移动到距离当前游标所在位置的20个字符处,我会这么做:

myStream.seekp(20, ios::cur);

相信你已经理解啦。

获知文件大小(所包含字节数)

这第三个小技巧需要用到前两个。为了获知文件的大小,我们首先移动到文件末尾,然后询问我们所在的位置。你知道怎么做了吗?一起来看看吧:

#include 

#include 

using namespace std;

int main()

{

ifstream file("C:/Cpp/Files/scores.txt");  //打开文件

file.seekg(0, ios::end);  //移动到文件末尾

int size;

size = file.tellg();

//在文件结尾处调用tellg这个函数,以获得目前位于第几个字符处,因此也就知道了文件的大小

cout <

return 0;

}

好了,我们学完了文件读写的大致概念。不过肯定不只于此,还有很多知识点需要慢慢在实践中去探索。

总结

在C++中,为了能读写文件,需要引入fstream头文件。

为了写入文件,我们需要创建一个ofstream对象;为了读取文件,我们需要创建一个ifstream对象。

写入文件的操作其实很类似 cout :  myStream << "文本";  读取文件的操作其实很类似 cout :  myStream >> variable;

可以用getline()函数一行一行地读取文件。

游标(cursor)指示了写入操作或读取操作时,在文件中的位置。如果需要,可以移动这个游标。

第一部分第十一课预告

今天的课就到这里,一起加油吧!

下一课我们学习:小练习,猜单词

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值