-
C++
中的文件输入
/
输出
收藏
简介
本教
程将以
C++
最基本的文件
I/O
p>
(输出
/
输出)开始。此后,我将从更深入
的方面,为你展
示一些技巧,并分析给出一些有用的函数。
<
/p>
你需要对
C++
有一个较好的理解,否则
这个教程于你而言将是陌生而毫无用处。
你的第一个程序
首先我将给出一段代码,
接着再逐行进行解释。
我们的第一个程序将建立一个文件,
并写入
一些字符:
#include
void main()
//
程序从这里开始运行
{
ofstream SaveFile(“cpp
-home<
/p>
.txt”);
SaveFile
<< “Hello World, from
-
and
Loobian!”;
();
}
仅仅如此吗?没错!
这个程序将在当前运行目录下建立一个名为
的文件,
并向
它写入“
Hello World, from and
Loobian!
”
。
下面给出各行的含义:
#include
——
你需要包含此文件以使用
C++
的文件输入<
/p>
/
输出函数。注意:一旦
包含了这个文件
,你不再需要(为了使用
cout/cin
)包含
iostream.h
,因为
fstream.h<
/p>
已经自
动包含了它。
< br>在这个头文件中声明了若干个类,包括
ifstream
,
ofstream
及
fstream
,它们都继承自
istream
和
p>
ostream
类。
ofstream
SaveFile(“cpp
-
”);
1
)
ofstream
即“
output file stream
(输出文
件流)
”
。它将建立一个句柄(
han
dle
)
,以便我们
以后能以一个文件
流的形式写入文件。
2
)
SaveFile
——
这是文件句柄的名字,当然,你还可以换用任何一个你想要的名称。
< br>3
)
(
“
”
);
——
打开名为
的文件。如果程序运行的当前目录已经
存在这样一个文件,<
/p>
则它将被替换掉;万一不存在,程序也会为你创建一个为文件,你不必
为此而担心。
现在,让我们稍微深入一点点。首先,我要
指出的是:
ofstream
是一个类。因此
< br>ofstream
SaveFile(
“
”
);
这一语句将创建一个
该类的对象;而我们在括号中所传递的参数
实际上将传给构造函数:
在这里我们将我们要建立的文件的名称作为实际参数传递给了该类
的构造函数。当然
,我们还可以传递其它的一些信息,不过我以后再对其进行讲解。
SaveFile <<
“
Hello World, from and Loob
ian!
”
;
——
“
<<
”看起来是不
是很亲切?不错,想必你已经在
cout <<
中见到过。这是一个预定义好的运算符。不管怎么
说,这行语句所做的,是将上面的那段文本写入文件。正如前
面所提到的,
SaveFile
是一个
文件句柄,它关联一个打开的流式文件。所以,我们只须输入句柄名,再跟着输入“
<<
”
,
然后接着写下一串用引号括起来的
文本,
就可以实现对文件的写入。
如果我们想写入的是某
个变量的值而不是带引号的文本,也只须像通常使用
cout <<
一样将变量传递给句柄对象,
像这样:
SaveFile << variablename;
就可以了!
();
——
既然我们打开了一个流文件,那么当我们用完它之后,就必须关闭它。
< br>SaveFile
是
ofstream
< br>类的一个对象,而该类
(ofstream)
有一个用于
关闭文件的成员函数,即
close()
函数。因此,我们只
要依次输入文件句柄名,点号和
close()
,就可以关闭该
文件!
注意:一旦你关闭文件,在你重新打开它以前,就再不
能对它进行访问。
以上就是一个可以写文件的最简单程序。
的确很容易!
不过,
正如你即将在以后部分的教程
中所看到的,还有更多的
东西要学呢!
读取文件
你已经看到了应该如何写文件。现
在,当我们已经得到
文件时,我们将要读取
它,并且将内容打印在屏幕上。
首先,
< br>我要指出的是,有很多种方法可以读取文件。以后我会向你们介绍所有的方法(就我
所知的)
。此刻,我先向你展示最佳的方法(我认为的)
。<
/p>
正如你已经熟悉的——我将首先给出一段程序代码,然后,我会
详细地对它进行解释说明:
#include
void main()
//
程序从这里开始
{
ifstream OpenFile(
char ch;
while(!())
{
(ch);
cout << ch;
}
();
}
你想
必已经了解首行的意义所在,而剩下的部分将由我为你解释。
ifstream OpenFile(
“
”
)
——
我猜它对现在的你而言多少会熟
悉些!
ifstream
表
示“
input file stream(
输入文件流
< br>)
”
。在前一节的程序中,出现的则是
< br>ofstream
,它的意义是
“
output
file
stream(
< br>输出文件流
)
”
。前一节的程序
是进行文件的写操作,这就是它用
“
output(
输出
)
”
来表示的原因。
而本节的程序则是读取一个文件,
这就是它用
< br>“
input(
输入
)
”
来表示的原因。这一行剩下的代码于你而言应当是熟悉的了:
OpenFile
是
ifstream
类的一个
对象,它
将关联一个输入文件流;而用引号括住的内容,就是将要打开的文件的名称。
请注意:这里没有对要打开的文件是否存在进行测试!以后我将向你指出如何进行检测。
char
ch;
——
声明一个字符数组(
array
of
type
char
)
。只是有一点要提醒你:这样的数组
(
arrays<
/p>
)只能存储一个
ASCII
字符。
while(!())
——
如果已经到达文件末尾,
eof(
)
函数将返回一个非零值。因此我
们所设计的这个循环将一直持续
,
直至我们的文件操作到达文件末尾。
这样我们就可以遍历
p>
整个文件,以便对它进行读取。
(ch);
——
OpenFile
是类
ifstream
的一个对象。该类声明了一个
名为
get(
)
的成
员函数。只要我们拥有该对象,我们自然就可以调用这个函数。
get(
p>
)
函数从相应的流文件
中读出一个字符,并
将其返回给变量。在本例中,
get(
)
函数只带一个参数——用于存储所
读取的字符的变量。所以,调用
< br>(ch)
后程序将会从
OpenFile
流中读取一个字符
并存入变量
ch
中。
注意:
如果你再次调用该函
数,
它将读取下一个字符,而不是原来的那一个!你过后将理解
为什么会这样。
这就是我们要不断反复循环直至读操作到达文
件尾的原因。
每循环一次,
我们将读出一个字
< br>符并将它保存在
ch
中。
cout << ch;
——
显示
c
h
变量值,它保存了读取得到的字符。
();
——
我们打开了一个流式文件,
p>
就需要关闭它。
使用
close()
函数即可将它关闭,
这和前一节的一样!
<
/p>
注意:一旦你关闭了一个文件,在你重新打开它之前,你不能再对它进行访问。
大功告成了!我希
望你能明白我的解释。当你编译并运行这个程序的时候,它应当会输出:
“
Hello World, from
-
and Loobian!”
掌握输入
/
输出流
在这一章里,
我会提及一些有用的函数。
我将为你演示如何打开
一个可以同时进行读、
写操
作的文件;此外,我还将为你介绍其
它打开文件的方法,以及如何判断打开操作是否成功。
因此,请接着往下读!
到目前为止,
我已为你所展示的只是单一的打开
文件的途径:
要么为读取而打开,
要么为写
入而打开。但文件还可以以其它方式打开。迄今,你应当已经认识了下面的方法:
ifstream
OpenFile(“cpp
-
”);
噢,这可不是唯一的方法!正如以
前所提到的,以上的代码创建一个类
ifstream
的对象,
并
将文件的名字传递给它的构造函数。
但实际上,
还存在有不少的重载的构造函数,
它们可以
接受不止
一个的参数。同时,还有一个
open()
函数可以做同样的事
情。下面是一个以上代码
的示例,但它使用了
open()
p>
函数:
ifstream
OpenFile;
(“cpp
-
”
);
你
会问:它们之间有什么区别吗?哦,我曾做了不少测试,结论是没有区别!只不过如果你
要创建一个文件句柄但不想立刻给它指定一个文件名,
那么你可以使用
< br>open()
函数过后进行
指定。顺便再给出一个要使用
open()
函数的例子:如果你打开一个文件,然后关闭了它
,又
打算用同一个文件句柄打开另一个文件,这样一来,你将需要使用
< br>open()
函数。
考虑以下的代码示例:
#include
void
read(ifstream &T) //pass the file stream to the
function
{
//the method to read a
file, that I showed you before
char ch;
while(!())
{
(ch);
cout << ch;
}
cout << endl <<
}
void main()
{
ifstream T(
read(T);
();
(
read(T);
();
}
据此,只要
和
并存储了文本内容,你将看到这些内容。
现在,
该向你演示的是,
文件名
并不是你唯一可以向
open()
函数或者构造函数
(其实都一样)
传递的参数。下面是一个函数原型:
ifstream
OpenFile(char *filename, int open_mode);
你应当知道
filename
表示文件的名称(一个字符串)
,而新出现
的则是
open_mode
(打开模
式)
。
op
en_mode
的值用来定义以怎样的方式打开文件。下面是打开模式的列表:
名称
描述
ios::in
打开一个可读取文件
ios::out
打开一个可写入文件
ios::app
你写入的所有数据将被追加到文件的末尾,
此方式使用
ios::out
ios::ate
你写入的所有数据将被追加到文件的末尾,
此方式不使用
ios::out
ios::trunk
删除文件原来已存在的内容(清空文件)
ios::nocreate
如果
要打开的文件并不存在,那么以此参数调用
open()
函数将
无法进行。
ios::noreplace
如果要打开的文件已存在,试
图用
open()
函数打开时将返回一个错误。
ios::binary
以二进制的形式打开一个文件。
实际上
,以上的值都属于一个枚举类型的
int
常量。但为了让你的编
程生涯不至于太痛苦,
你可以像上表所见的那样使用那些名称。
下面是一个关于如何使用打开模式的例子:
#include
void main()
{
ofstream SaveFile(
SaveFile <<
();
}
正如你在表中所看到的:
使用
ios::ate
将会从文件的末尾开始执
行写入。
如果我没有使用它,
原来的文件内容将会被重新写入的
内容覆盖掉。
不过既然我已经使用了它,
那么我只会在原
文件的末尾进行添加。所以,如果
原有的内容
是这样:
Hi! This is test from !
那么执行上面的代码后,程序将会为它添上“
That
’
s new!
”
,因
此它看起来将变成这样:
Hi! This is test
from -
!That’s n
ew!
假如你打算设置不止一个的打开模式标志,只须使用
OR
操
作符或者是
|
,像这样:
ios::ate | ios::binary
我希望现在你已经明白“打开模式”是什么意思了!
现在,
是时候向你展示一些真正有用的东西了!
我敢打赌你现在还不知道应当怎样打开一个
可以同时进行读取和写入操作的文件!下面就
是实现的方法:
fstream
File(“cpp
-
”,ios::in |
ios::out);
实际上,
这只是一个声明语句。
我将在下面数行
之后给你一个代码示例。
但此时我首先想提
及一些你应当知道的内容。
上面的代码创建了一个名为“
File
”的流
式文件的句柄。如你所知,它是
fstream
类的一个
对象。
当使用
fstream
时,你应当指定
ios::in
和
ios::out
作为文件的打开模式。
这样,
你就可
以同时对文件进行读、写,而无须创建新的文件句柄。噢,当然,你
也可以只进行读或者写
的操作。那样的话,相应地你应当只使用
ios::in
或者只使用
ios::out
——
要思考的问题是:
如果你打算这么做,为什么你不分别用
ifstream
及
ofstream
来实现呢?
下面就先给出示例代码:
#include
void main()
{
fstream File(
File <<
将“
Hi!
”写入文件
static char str[10]; //
当使用
p>
static
时,数组会自动被初始化
//
即是被清空为零
(ios::beg); //
回到文件首部
//
此函数将在后面解释
File >> str;
cout << str <<
endl;
();
}
OK
,这儿又有一些新东西,所以我将逐行进行解释:
fstream File(
p>
“
”
,
ios::in | ios::out);
——
此行创建一个
fstream
对象,执行时将会
以读
/
写方式打开
文件。这意味着你可以同时读取文件并写入数据。
File <<
“
Hi!
”
;
——
我打赌你已经知道它的意思了。
static char str[10];
——
这将创建一个容量为
10
的字符数组。我猜
static
对你而言或者有些
陌生,如果这样就忽略它。这只不过会在创建数组的同时对
其进行初始化。
(ios::beg);
——
p>
OK
,我要让你明白它究竟会做些什么,因此我将以一些有点儿离<
/p>
题、但挺重要的内容开始我的解释。
还记得它么:
while(!())
{
(ch);
cout << ch;
}
你是
不是曾经很想知道那背后真正执行了什么操作?不管是或不是,
我都将为你解释。
这是
一个
while
型循环,它会一直反复,直至程序的操作到达文件的尾端。但这个循环如何知道
是
否
已
经
到
了
文
件
末
尾
?
嗯
,
当
你
读
文
件
的
时
候
,<
/p>
会
有
一
个
类
似
于
“
内
置
指
针
(
inside-pointer
)
< br>”的东西,它表明你读取(写入也一样)已经到了文件的哪个位置,就像
记事本中
的光标。而每当你调用
(ch)
的时候,它会返回当前位置的字
符,存储
在
ch
变量中,并将这一内置
指针向前移动一个字符。因此下次该函数再被调用时,它将会
返回下一个字符。而这一过
程将不断反复,直到读取到达文件尾。
所以,让我们回到那行代
码:函数
seekg()
将把内置指针定位到指定的位置(依你
决定)
。你可以使用:
ios::beg
——
可将它移动到文件首端
ios::end
——
可将它移动到文件末端
或者,
你可以设定向前或向后跳转的
字符数。
例如,
如果你要向定位到当前位置的
< br>5
个字符
以前,你应当写:
(-5);
如果你想向后跳过
40<
/p>
个字符,则应当写:
(40);
p>
同时,我必须指出,函数
seekg()
是
被重载的,它也可以带两个参数。另一个版本是这样子
的:
(-5,ios::end);
在这个例子中,你将能够读到
文件文本的最后
4
个字符,因为:
<
/p>
1
)你先到达了末尾(
ios::end
)
2
)你接
着到达了末尾的前五个字符的位置(
-5
)
为什么你会读到
4
个字符而不是
5
个?噢,只须把最后一个看成是“丢掉了”
< br>,因为文件最
末端的“东西”既不是字符也不是空白符,那只是一个位置(译注:
或许
ios::end
所“指”
的根本
已经超出了文件本身的范围,
确切的说它是指向文件最后一个字符的下一个位置,
有
点类似
STL
中的
各个容器的
end
迭代点是指向最后一个元素的下一位置。这样
设计可能是
便于在循环中实现遍历)
。
你现在可能想知道为什么我要使用到这个函数。呃,当我把“
H
i
”写进文件之后,内置指针
将被设为指向其后面??也就是文
件的末尾。
因此我必须将内置指针设回文件起始处。
这就
是这个函数在此处的确切用途。
File
>> str;
——
这也是新鲜的玩意儿!噢,我确信这行代码让你想起了
cin
>> .
实际上,它
们之间有着相当的关联。此行会从文件中读
取一个单词,然后将它存入指定的数组变量中。
例如,如果文件中有这样的文本片断:
Hi! Do you know me?
使用
File
>>
str
,则只会将“
Hi!
”
输出到
str
数组中。你应当已经注意到了,它实际上是将
p>
空格作为单词的分隔符进行读取的。
由于
我存入文件中的只是单独一个“
Hi!
”
,我不需要写一个
while
循环,那会花费更多的时
间来写代码。这就是我使用此方法的原因。顺便说一下,
到目前为止,
我所使用的读取文件
的
while
p>
循环中,程序读文件的方式是一个字符一个字符进行读取的。然而你也可以一个单
词一个单词地进行读取,像这样:
char str[30]; //
每个单词的长度不能超过
30
个字符
while(!())
{
OpenFile >>
str;
cout << str;
}
你也可以一行一行地进行读取,像这样:
char line[100];
//
每个整行将会陆续被存储在这里
while(!())
{
e(line,100); //
100
是数组的大小
cout <<
line << endl;
}
你现在可能想知道应当使用哪种方法。
嗯,
我建议你使用逐行读取的方式,
或者是最初我提
及的逐字符
读取的方式。
而逐词读取的方式并非一个好的方案,
因为它不会
读出新起一行这
样的信息,
所以如果你的文件中新起一行时,<
/p>
它将不会将那些内容新起一行进行显示,
而是
加在已经打印的文本后面
(
即是不换行
)
。而使用
getline()
或
者
get()
都将会向你展现出文件的
本来面目!
现在,
我将向你介绍如何
检测文件打开操作是否成功。实现上,好的方法少之又少,我将都
会涉及它们。需要注意
的是,出现“
X
”的时候,它实际可以以“
o
”
、
“
i
”来代替,或者
也可以什么都不是
(那将是一个
fstream
对象)
。
例
1
:最通
常的作法
Xfstr
eam
File(“cpp
-
”);
if (!File)
{
cout
<< “Error opening the file!
Aborting…
n”;
exit(1);
}
例
p>
2
:如果文件已经被创建,返回一个错误
ofstream
File(
if(!File)
{
cout << “Error opening the
file!
Aborting…
n”;
exit(1);
}
例<
/p>
3
:使用
fail()
< br>函数
ofstream File(
if(())
{
cout << “Error opening the file!
Aborting…
n”;
exit(1);
}
例
3
中的新
出现的东西,
是
fail()
函数。如
果有任何输入
/
输出错误
(不是在文件
末尾)
发生,
它将返回非零值。
p>
我也要讲一些我认为非常重要的内容!
例如,
如果你已经创建一个流文件对象,
但你没有进
行打开文件操作
,像这样:
ifstream File;
//
也可以是一个
ofstream
这样,
我们就拥有一个文件句柄,但
我们仍然没有打开文件。
如果你打算迟些打开它,
那么
可以用
open()
函数来实现,我已经在本教
程中将它介绍了。但如果在你的程序的某处,你可
能需要知道当前的句柄是否关联了一个
已经打开的文件,那么你可以用
is_open()
来进行检<
/p>
测。如果文件没有打开,它将返回
0
(false)
;如果文件已经打开,它将返回
1
(true)
。例如:
ofstream File1;
(
cout << _open() << endl;
上面的代码将会返回
1
(译注:指
_open()
函数,下句同)
,因为我们已经打开了一个
文件(在第二行)
。而下面的代码则会返回
0
,这是由于
我们没有打开文件,而只是创建了
一个流文件句柄:
ofstream File1;
cout <<
_open() << endl;
检测输入
/
输出的状态标志
在此我不打算解释“标志(
flags
)
”一词的含义,不过假如你真的完全不理解关于这方面的
概念,
那么将本章读过一遍之后也许你对此会得到一些认识,
我也相信你同样能理解
这部分
的理论。
尽管如此,
如果你还是
不明白标志在
C++
中的含义,
我推荐
你阅读一些关于这个主
题的资料。
好,让我们开始吧。
C++
中负责的输入
/
输出的系统包括了关于每一个输
入
/
输出操作的结果的记录信息。这些当
前的状态信息被包含在
io_state
类型的对象中。
p>
io_state
是一个枚举类型(就像
o
pen_mode
一样)
,以下便是它包含的值(译注:表中第
一列为枚举值的名称,第二列为该值相应含义