C++ 中的文件 IO 流

文件流类与文件流对象

  • C++ 中对文件的操作是由文件流类完成的
  • 通过文件流类可以将文件和流联系起来
  • 文件流类分为输入流,输出流和输入/输出流
  • 再对文件进行操作之前,需要将文件流说明为 ifstream,ofstream 或者 fstream 的对象

文件操作过程

在 C 语言中,对文件的操作步骤为:

  • 打开文件
  • 进行读写
  • 关闭文件

而在 C++ 中,对文件的操作步骤为:

  • 定义文件流对象
  • 打开文件
  • 进行读写
  • 关闭文件

定义流对象

ifstream ifile;
ofstream ofile;
fstream f;

只是在定义流对象之前之前需要包含对应的头文件。

打开文件

void open(const std::string& __s, ios_base::openmode __mode = ios_base::in)
void open(const std::string& __s, ios_base::openmode __mode = ios_base::out | ios_base::trunc)
void open(const std::string& __s, ios_base::openmode __mode = ios_base::in | ios_base::out)

打开文件的函数为 open(),第一个参数表示要打开的文件名,第二个参数表示文件的打开方式,可以看出三种不同的文件流对象,具有不同的默认打开方式。

主要的文件打开方式为:

文件打开方式描述
ios::in0x01以读方式打开文件,若文件不存在则报错
ios::out0x02以写方式打开文件,若文件不存在则创建
ios::app0x08在文件末尾添加内容,如果文件不存在,则报错
ios::trunc0x10若文件存在,则清除文件所有内容,若文件不存在,则创建新文件
ios::binary0x80以二进制方式打开文件,缺省时以文本方式打开文件
ios::nocreate0x20打开一个已有文件,如果文件不存在,则打开失败
ios::noreplace0x40如果要打开的文件已经存在,则打开失败
  • 通常很少单独使用某一种打开方式,一般会将几种打开方式进行组合:
ios::in | ios::out以读写方式打开文件,ifstream 对象默认的打开方式
ios::in | ios::binary以二进制读方式打开文件
ios::out | ios::binary以二进制写方式打开文件
ios::in | ios::out | ios::binary以二进制读写方式打开文件
ios_base::out | ios_base::truncofstream 对象默认的打开方式
  • C++ 中文件都是借助文件流类打开的,而类中存在构造函数,因此也可以直接利用构造函数打开
  • C 中是通过判断指针是否为空来判断是否正确地打开了文件,而 C++ 中则是通过类对象来进行判断的

关闭文件

文件操作之后,应使用 close() 来关闭文件。

#include <iostream>
#include <fstream>

using namespace std;

int main()
{
    ifstream ifile("test.txt");
    ofstream ofile("out.txt");
    char buf[100];

    if(!ifile && !ofile)
        cout<<"error"<<endl;

    while(ifile)
    {
        ifile>>buf;
        ofile<<" "<<buf;
    }

    ifile.close();
    ofile.close();

    return 0;
}

使用上边的程序可以将 test.txt 中的内容对应的拷贝到 out.txt 文件中,只是 out.txt 中没有换行。

流文件状态与判断

标识位

在 ios_base.h 中,给出了一个判断流文件状态的标志位:

enum _Ios_Iostate
  { 
    _S_goodbit 		= 0,
    _S_badbit 		= 1L << 0,
    _S_eofbit 		= 1L << 1,
    _S_failbit		= 1L << 2,
    _S_ios_iostate_end = 1L << 16,
    _S_ios_iostate_max = __INT_MAX__,
    _S_ios_iostate_min = ~__INT_MAX__
  };

/// Indicates a loss of integrity in an input or output sequence (such
/// as an irrecoverable read error from a file).
static const iostate badbit =	_S_badbit;

/// Indicates that an input operation reached the end of an input sequence.
static const iostate eofbit =	_S_eofbit;

/// Indicates that an input operation failed to read the expected
/// characters, or that an output operation failed to generate the
/// desired characters.
static const iostate failbit =	_S_failbit;

/// Indicates all is well.
static const iostate goodbit =	_S_goodbit;

上边的头文件中以枚举的形式给出了几个标志位,这几个标志位各自代表有各自的含义。

函数

eof()

如果读取文件到达文件末尾,返回 true

bad()

如果读写过程中出错,返回 true

fail()

如果读写过程中出错或者格式错误,返回 true

good()

如果发生了之前的任何情况,该函数都返回 false

clear()

标志位被置位后,可以使用 clear() 来重置标志位

#include <iostream>
#include <fstream>

using namespace std;

int main()
{
    cout<<cin.eof()<<" "<<cin.bad()<<" "<<cin.fail()<<" "<<cin.good()<<endl;
    cin.clear();

    int i;
    cin>>i;

    cout<<cin.eof()<<" "<<cin.bad()<<" "<<cin.fail()<<" "<<cin.good()<<endl;
    cin.clear();

    return 0;
}

结果为:

0 0 0 1
abc
0 0 1 0

!cin

在打开文件的时候,我们使用了 !cin 的形式来判断文件是否打开。我们知道 cin 其实是一个流对象,那么我们又为什么能够使用流对象进行逻辑判断呢?

这只能说明在 C++ 中为流对象提供了某种转换函数,使之可以将一个流对象转换成为可以进行逻辑操作的类型。

在 basic_ios.h 文件中存在两个重载函数,它们为这种行为做出了解释:

operator void*() const
{ return this->fail() ? 0 : const_cast<basic_ios*>(this); }
// 函数会在 while(cin) 或者 if(cin) 时被调用,会将流对象转化为 void* 类型
// 重载 void * 的可以实现连等式
// 看着像是运算符重载,但是 C++ 内部并没有 void * 这个运算符

bool operator!() const
{ return this->fail(); }
// 函数会在 while(!cin) 或者 if(!cin) 时被调用,会将流对象转化为 bool 类型
// 这个是对 ! 的重载

可以对自定义类进行上述两个函数的重载,测试结果:

#include <iostream>
#include <fstream>

using namespace std;

class TEST
{
public:
    operator void *() const
    {
        cout<<"operator void *() const"<<endl;
        return (void *)(this);
    }

    bool operator !() const
    {
        cout<<"bool operator !() const"<<endl;
        return true;
    }
};

int main()
{
    TEST t;

    if(t)
        cout<<"yes"<<endl;
    else
        cout<<"no"<<endl;

    if(!t)
        cout<<"yes"<<endl;
    else
        cout<<"no"<<endl;

    return 0;
}

结果为:

operator void *() const
yes
bool operator !() const
yes

不得不说,C++ 真的很神奇。

文件读写

就像 cout 和 cin 一样,如果要读取数据,可以使用文件流类的 get,getline 和 >> 进行操作;如果要写入数据,可以使用 put,write 和 << 进行操作。

流状态的查询和控制

#include <iostream>
#include <fstream>

using namespace std;

int main()
{
    ifstream ifile("test.txt");
    ofstream ofile("out.txt");
    char buf[100];

    if(!ifile && !ofile)
        cout<<"error"<<endl;

    while(ifile >> buf,!ifile.eof())
    {
        ofile<<" "<<buf;
        if(ifile.bad())
            throw runtime_error("IO stream corrupted");
        if(ifile.fail())
        {
            cerr<<"bad data";
            ifile.clear(istream::failbit);
            continue;
        }
    }

    ifile.close();
    ofile.close();

    return 0;
}
  • 上面的循环会不断迭代,直到到达文件结束符或者发生不可恢复的读取错误为止。
  • 循环条件使用了逗号操作符,因此会返回最右边操作数作为整个表达式的结果。
  • 如果 ifile 到达文件结束符,条件则为假,退出循环,如果 ifile 没有到达文件结束符,则都会进入循环
  • 在循环中,首先检查流是否已破坏,如果流已经被破坏,则抛出异常,退出循环
  • 如果输入无效,则输出警告并清除 failbit 状态,continue 下一次循环

文件读写实例

#include <iostream>
#include <fstream>

using namespace std;

int main()
{
    ifstream ifile("test.txt");
    ofstream ofile("out.txt");
    char buf[1024];

    if(!ifile && !ofile)
        cout<<"error"<<endl;

    while(ifile.getline(buf,1024,'\n'))
    {
        ofile<<buf<<" "<<endl;
        if(ifile.eof())
            break;
    }

    ifile.close();
    ofile.close();

    return 0;
}

随机读写函数

成员函数描述
tellg()返回当前指针位置(输入流操作)
seekg()绝对移动
seekg()相对操作
seekp()绝对移动(输出流操作)
seekp()相对操作
tellp()返回当前指针位置

g 表示 get,为输入,p 表示 put,为输出。参照位置为:

成员意义
ios::beg = 0相对于文件头
ios::cur = 1相对于当前位置
ios::end = 2相对于文件尾
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值