Chinaunix首页 | 论坛 | 博客
  • 博客访问: 33468
  • 博文数量: 9
  • 博客积分: 172
  • 博客等级: 入伍新兵
  • 技术积分: 105
  • 用 户 组: 普通用户
  • 注册时间: 2012-09-08 21:15
文章分类

全部博文(9)

文章存档

2012年(9)

最近访客

分类:

2012-09-08 22:51:31

原文地址:c++文件io完全手册 作者:wwm

大多数 C++ 程序员都熟悉不止一个文件 I/O 库。首先是传统的 Unix 风格的库,它由一些低级函数如 read() 和 open()组成。其次是 ANSI C 的 库,它包含 fopen() 和 fread()等函数。其它的还有一些具备所有权的库或框架,比如 MFC,它有很多自己的文件处理类。
  这些库一般都很难跨平台使用。更糟的是,上述提到的 C 库由于其程序接口的原因,在很大程度上强制程序员进行某些处理,而且缺乏类型安全支持。
   标准 C++ 提供提供了一个增强的、面向对象的、具有国际化意识的  库。这个库包含一系列派生于标准 ios_base 和 ios 类的类模板。因此, 提供了高级的自动控制机制和健壮性。本文下面将示范如何使用  类实现文件的输入/输出处理:

1。打开文件

void open(const char *_Filename, ios_base::open_mode _Mode)

void open(const char *_Filename, ios_base::openmode _Mode = ios_base::out, int _Prot = (int)ios_base::_Openprot)

打开模式:

ios::app // 从后面添加
ios::ate // 打开并找到文件尾
ios::binary // 二进制模式 I/O (与文本模式相对)
ios::in // 只读打开
ios::out // 写打开
ios::trunc // 将文件截为 0 长度

保护方式:

filebuf::openprot // 兼容共享方式

filebuf::sh_none  // 独占,不共享

filebuf::sh_read

filebuf::sh_write

2。检测是否打开:

ofstream File1;

cout << File1.is_open() << endl;//false

2.。检测打开成功与否:

ofstream File("unexisting.txt", ios::nocreate);

 

if(!File)

{

cout << “Error opening the file! Aborting…\n”;

exit(1);

}

==============================================

ofstream File("filer.txt", ios::nocreate);

 

if(File.fail())

{

cout << “Error opening the file! Aborting…\n”;

exit(1);

}

3:设置文件的位置

ofstream fout("parts.txt");fout.seekp(10); // 从0偏移开始前进 10 个字节cout<<"new position: "<

你可以用下面的常量重新定位文ian指针:

ios::beg // 文件开始位置ios::cur // 当前位置,例如: ios::cur+5ios::end // 文件尾

File.seekg(-5);

File.seekg(40);

File.seekg(-5,ios::end);//读到文件文本的最后4个字符

4.

检测输入/输出的状态标志

C++中负责的输入/输出的系统包括了关于每一个输入/输出操作的结果的记录信息。这些当前的状态信息被包含在io_state类型的对象中。io_state是一个枚举类型(就像open_mode一样),以下便是它包含的值(译注:表中第一列为枚举值的名称,第二列为该值相应含义的描述)

 

godbit

无错误

Eofbit

已到达文件尾

failbit

非致命的输入/输出错误

badbit

致使的输入/输出错误

 

有两种方法可以获得输入/输出的状态信息。一种方法是通过调用rdstate()函数,它将返回当前状态的错误标记(上表中提到的)。例如,假如没有任何错误,则rdstate()会返回goodbit.

另一种方法则是使用下面任何一个函数来检测相应的输入/输出状态:

 

bool bad();

bool eof(); //还记得它么?“不断读取文件内容直到到达文件末尾!”

bool fail(); //噢,这也是老朋友……检测一个打开操作是否成功

bool good();

 

假如badbit标志被标设(译注:原文为“If the badbit flag is up”,这里将“is up”译为“标设”,意即出现了badbit对应的错误,badbit状态被置为当前的错误状态,下同),则bad()函数返回true;假如failbit标志被标设,则fail()函数返回true;假如没有错误发生(goodbit标志被标设),则good()函数返回true;假如操作已经到达了文件末尾(eofbit被标设),则eof()函数返回true.

如果错误发生,你必须清除这些错误状态,以使你的程序能正确适当地继续运行——如果你这么打算的话。要清除错误状态,需使用clear()函数。此函数带一个参数,它是你将要设为当前状态的标志值。假使你想让你的程序“清清爽爽”地运行下去,只要将ios::goodbit作为实参。你将在以下内容中看到示例代码。

我将向你展示示例代码,以巩固你所学到的理论知识。

 

示例1:简单的状态检测

 

// 实际应用中可将 FileStream替换成你相应在使用的文件流句柄

    if(FileStream.rdstate() == ios::eofbit)

       cout << "End of file!\n";

    if(FileStream.rdstate() == ios::badbit)

       cout << "Fatal I/O error!\n";

    if(FileStream.rdstate() == ios::failbit)

       cout << "Non-fatal I/O error!\n";

    if(FileStream.rdstate() == ios::goodbit)

cout << "No errors!\n";

 

示例2clear()函数

 

#include

 

void main()

{

    ofstream File1("file2.txt"); //建立file2.txt

    File1.close();

 

    // 下面的检测代码将会返回错误,这是因为我使用了ios::noreplace打开模式

    // 它模式在试图打开一个已存在的文件时会返回错误

    ofstream Test("file2.txt",ios::noreplace);

 

    // 上一行将导致ios::failbit错误,我们这就将其演示出来

    if(Test.rdstate() == ios::failbit)

       cout << "Error...!\n";

 

    Test.clear(ios::goodbit); // 将当前状态重置为ios::goodbit

 

    if(Test.rdstate() == ios::goodbit) // 检测程序是否已经正确地施行了设置

       cout << "Fine!\n";

 

    Test.clear(ios::eofbit); // 将状态标志设为ios::eofbit. 无实际用途.

 

    if(Test.rdstate() == ios::eofbit) // 检测是否已经正确地施行了设置       cout << "EOF!\n";

 

    Test.close();

   

}

 

除了使用标记值判断,你也可以使用函数(译注:指bad()、eof()、fail()、good()这些函数)的形式进行判断,两者实际上是一样的——都是检测某个标记是否被标设。这些函数前面已经介绍过,我就不再重复了。如果你对如何使用它们还不是十分确定,那就重新回顾一下本教程中我曾经为你演示的应该如何检测一个文件打开操作是否成功的那部分内容。在那里我就使用了fail()函数。

5.二进制文件的处理

虽然有规则格式(formatted)的文本(到目前为止我所讨论的所有文件形式)非常有用,但有时候你需要用到无格式(unformatted)的文件——二进制文件。它们和你的可执行程序看起来一样,而与使用<<>>操作符创建的文件则大不相同。

首先设置ios::binary

然后使用函数:

读取一个字节:get()  put()

/写一整块的数据:

istream &read(char *buf, streamsize num);

ostream &write(const char *buf, streamsize num);

示例1:使用get( )put( )

 

#include

 

void main()

{

    fstream File("test_file.txt",ios::out | ios::in | ios::binary);

 

    char ch;

    ch='o';

 

    File.put(ch); // ch的内容写入文件

 

    File.seekg(ios::beg); // 定位至文件首部

 

    File.get(ch); // 读出一个字符

 

    cout << ch << endl; // 将其显示在屏幕上

 

    File.close();

}

 

示例2:使用read( )write( )

 

#include

#include

 

void main()

{

    fstream File("test_file.txt",ios::out | ios::in | ios::binary);

 

    char arr[13];

    strcpy(arr,"Hello World!"); //Hello World!存入数组

 

    File.write(arr,5); // 将前5个字符——"Hello"写入文件

 

    File.seekg(ios::beg); // 定位至文件首部

 

    static char read_array[10]; // 在此我将打算读出些数据

 

    File.read(read_array,3); // 读出前三个字符——"Hel"

 

    cout << read_array << endl; // 将它们输出  

 

    File.close();

}

6.常用函数:

 tellg() ——返回一个int型数值,它表示“内置指针”的当前位置。此函数仅当你在读取一个文件时有效。例如:
   
#include
     
    void main()
    {
       
// 假如我们已经在test_file.txt中存有了“Hello”的内容
   
    ifstream File("test_file.txt");
     
        char arr[10];
     
        File.read(arr,10);
       
       
// 由于Hello5个字符,因此这里将返回5
   
    cout << File.tellg() << endl;
     
        File.close();
    }
   
   
tellp() —— tellg()有同样的功能,但它用于写文件时。总而言之:当我们读取一个文件,并要知道内置指针的当前位置时,应该使用tellg();当我们写入一个文件,并要知道内置指针的当前位置时,应该使用tellp(). 由于此函数的用法与tellg()完全一样,我就不给出示例代码了。
   
    seekp() ——
还记得seekg()么?当我在读取一个文件,并想到达文件中某个特定位置时,就曾使用过它。seekp()亦如此,只不过它用于写入一个文件的时候。例如,假如我在进行文件读写,而要定位到当前位置的三个字符之前,则需调用FileHandle.seekg(-3). 但如果我是在写入一个文件,并且比如我要重写后5个字符的内容,我就必须往回跳转5个字符,因而,我应该使用FileHandle.seekp(-5) .
   
    ignore() ——
使用于读取文件之时。如果你想略过一定数量的字符,只需使用此函数。实际上,你也可以使用seekg()来代替,然而使用ignore()有一个优点——你可以指定一个特定“界限规则(delimiter rule)”,同样使得ignore()在指定的位置停下。函数原型如下:
   
   
istream& ignore( int nCount, delimiter );
   
   
nCount表示要略过的字符数量,而delimiter —— 与它的名称有着同样的含义:假如你想在文件末尾停下,则可使用EOF值传入,这样一来此函数就等同于seekg();但该参数还可以使用其他值,例如‘\n’这样可以在换行的同时定位在新行处。下面是示例:
   
#include
     
    void main()
    {
       
// 假设test_file.txt中已经存有"Hello World"这一内容
   
    ifstream File("test_file.txt");
     
        static char arr[10];
     
       
// 假如一直没有遇到字符"l",则向前定位直到跳过6个字符
   
    // 而如果期间遇到"l",则停止向前,定位在该处
   
    File.ignore(6,'l');
     
        File.read(arr,10);
     
        cout << arr << endl;
// 它将显示"lo World!"
     
   
    File.close();
     
    }
   

    getline() ——
虽然前面的章节中我曾提到过这个函数,但还有一些内容我们未曾涉及:此函数不但可用于逐行读取,而且它还可以设为遇到某个特定字符后停止读取。下面给出传递这一参数的方法:
   
   
getline(array,array_size,delim);
   
   
以下为示例代码:
   
   
#include
     
    void main()
    {
       
// 假设test_file.txt中已经存有"Hello World"这一内容
   
    ifstream File("test_file.txt");
     
        static char arr[10];
     
       
/* 读取,直到满足下面的条件之一:
    1
)已经读取10个字符
    2
)遇到字母"o"
    3
)出现新一行
   
    */
        File.getline(arr,10,'o');
     
        cout << arr << endl;
// 将显示"Hell"
   
    File.close();
    }
   

    peek() ——
此函数将返回输入流文件的下一个字符,但它不移动内置指针。我想你该记得,像get()这样的函数也返回输入流文件的下一个字符,而与此同时它将移动内置指针。所以当你再次调用get()函数的时候,它会返回再下一个字符,而非前面那个。哦,使用peek()也会返回字符,但它不会移动“光标”。所以,假如你连续两次调用peek()函数,它会返回同一个字符。考虑以下代码:
   
   
#include
     
    void main()
    {
       
// 假设test_file.txt中已经存有"Hello World"这一内容
   
    ifstream File("test_file.txt");
     
        char ch;
     
        File.get(ch);
        cout << ch << endl;
// 将显示"H"
     
        cout <<    char(File.peek()) << endl;
//将显示"e"
        cout <<    char(File.peek()) << endl;
//将再次显示"e"
     
        File.get(ch);
        cout << ch << endl;
// 还是显示"e"
   

    File.close();
       
    }
   

   
顺便说一下,我忘了讲——peek()函数实质上返回的是字符的ASCII码,而非字符本身。因此,假如你想看到字符本身,你得像我在示例中做的那样进行调用(译注:即要转为char类型)
   
    _unlink() ——
删除一个文件。假如你要使用此函数,需要在你的程序中包含io.h头文件。下面是示例代码:
   
   
#include
    #include
     
    void main()
    {
        ofstream File;
     
        File.open("delete_test.txt");
//创建一个文件
   
    File.close();
     
        _unlink("delete_test.txt");
// 删除这个文件
   
 
       
// 试图打开此文件,但假如它已不存在
   
    // 函数将返回一个ios::failbit错误值
   
    File.open("delete_test.txt",ios::nocreate);
     
       
// 验证它是否返回该值
   
    if(File.rdstate() == ios::failbit)
            cout << "Error...!\n";
// 耶,成功了
   
   
File.close();
     
    }
   

    putback() ——
此函数将返回最后一个所读取字符,同时将内置指针移动-1个字符。换言之,如果你使用get()来读取一个字符后再使用putback(),它将为你返回同一个字符,然而同时会将内置指针移动-1个字符,所以你再次使用get()时,它还是会为你返回同样的字符。下面是示例代码:
   
   
#include
     
    void main()
    {
       
// test_file.txt应包含内容"Hello World"
   
    ifstream File("test_file.txt");
       
        char ch;
     
        File.get(ch);
     
        cout << ch << endl;
// 将显示"H"
     
        File.putback(ch);
        cout << ch << endl;
// 仍将显示"H"
       
        File.get(ch);
        cout << ch << endl;
// 再一次显示"H"
     
        File.close();
    }
   
   
flush() —— 在处理输出流文件的时候,你所存入的数据实际上并非立刻写入文件,而是先放入一个缓冲区中,直到该缓冲区放满数据之后,这些数据才被存入真正的文件中(在你的磁盘上)。旋即缓冲区会被清空,再重新进行下一轮写入。
   
但假如你想在缓冲区写满之前就将其中的数据写入磁盘,则使用flush()函数。只须像这样进行调用:FileHandle.flush(),这样缓冲区内的数据将会写入实际的物理文件,而后缓冲区被清空。
   
再补充一点(高阶的)内容:flush()函数会调用与相应流缓冲(streambuf)相联系的sync()函数(出自MSDN)。

阅读(843) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~