c++進階---IO類詳解(二)--文件流的詳解

前言

在上一篇博客,我們已經主要介紹了IO類是什麼和對標準輸入流cin做了比較詳細的介紹,這篇博客我們就來開始學習另外一類IO類:ifstream 、oftream、fstream。我們稱之爲文件流,主要是對系統的文件進行一些讀寫之類的操作。下面我們就對文件流進行詳細的講述。

1、流的分類總結

ofstream :文件的寫操作,主要是從內存寫入存儲設備(如磁盤),它是繼承istream類

ifstream :文件的讀操作,主要是從存儲設備中讀取數據到內存,它是繼承了ostream類

fstream :讀寫操作,對打開的文件可進行讀寫操作,它是繼承了iostream類。

2、打開文件

打開文件作爲我們對文件操作一個最基本操作,它主要就是把我們的類對象和一個文件相關聯起來,這樣這個被打開的文件就可以用那個類對象表示,之後我們對類對象所做的輸入和輸出操作其實都是對那個文件所做的操作。

在每個文件流的類中都定義了一個打開文件的成員函數:open,函數原型爲:

void open(const char* filename,ios_base::openmode mode);

參數的含義:
   filename:  要打開的文件名
  mode:    要打開文件的方式

其中mode定義在之前說的所有IO的基類:ios類中,它包括如下幾種方式:

    ios::app:   以追加的方式打開文件
  ios::ate:   文件打開後定位到文件尾,ios:app就包含有此屬性
  ios::binary: 以二進制方式打開文件,缺省的方式是文本方式。兩種方式的區別見前文
  ios::in:    文件以輸入方式打開(文件數據輸入到內存)(ifstream對象默認方式就是這個)
  ios::out:   文件以輸出方式打開(內存數據輸出到文件)(ofstream對象默認的打開方式)
  ios::nocreate: 不建立文件,所以文件不存在時打開失敗
  ios::noreplace:不覆蓋文件,所以打開文件時如果文件存在失敗
  ios::trunc:  如果文件存在,把文件長度設爲0

其中,mode的參數可以不止一個,但是兩個參數之間必須要用操作符:“|”隔開,例如:

//下面是聲明一個ofstream對象,往text.txt文件中輸入內容,並且是在文件的末尾追加內容,不清空原有的內容,
//具體聲明如下
ofstream outout.open("text.txt",ios::out|ios::app)

或者

//這個聲明方式是調用了ofstream有參構造函數,該構造函數會自動調用open函數。
ofstream out("text.txt",ios::out|ios::app);

例外,我們之前說的三種文件流類都有自己默認的打開方式,具體的內容如下:

ofstream默認打開方式:ios::out | ios::trunc 
ifstream默認打開方式: ios::in 
fstream默認打開方式: ios::in | ios::out

我們要對文件進行操作,那麼第一個操作當然是要把我們需要操作的文件打開,所以每次我們調用完open之後,都要去判斷一下我們的文件是否打開,你可以直接使用如下兩種方式判斷:

ofstream out;
out.open("text.txt",ios::out|ios::app);
if(!out){
    cout<<"文件打開失敗"<<endl;
    }
//這個方式和我們之前使用:while(cin>>a)的原理一樣,都是因爲流的條件狀態:badbit、failbit、eofbit
//三者中只要有一個被置位,那麼我們的條件都是返回false的,而文件打開失敗,failbit會被置位。

或者

ofstream out;
out.open("text.txt",ios::out|ios::app);
if(!out.is_open()){
    cout<<"文件打開失敗"<<endl;
    }
    //你可以通過調用成員函數is_open()來檢查一個文件是否已經被順利的打開了:bool is_open();
   //它返回一個布爾(bool)值,爲真(true)代表文件已經被順利打開,假( false )則相反。

3、關閉文件

當我們完成了對文件的操作後,我們就需要調用函數close來關閉我們的文件流,close函數的作用其實就是清空該類對象的在緩存中的內容並且關閉該類對象和那個文件的關聯關係。爲了防止一個類對象被銷燬後,還和某個文件保留着關聯關係,所以文件流類的析構函數都會自動調用close函數。

4、文件讀寫操作

對於文件讀寫操作要分兩種:

  1. 文本文件的讀取
  2. 二進制文件的讀取

(1)文本文件的讀寫

使用操作符:“<<”往文本文件中寫入數據,使用操作符:“>>”從文本文件中讀取數據,具體的代碼示例如下:

/************************************************************/
/*                程序作者:Willam                          */
/*                程序完成時間:2017/3/5                    */
/*                有任何問題請聯繫:[email protected]       */
/************************************************************/
//@儘量寫出完美的程序
#include<iostream>
#include<fstream>
#include<string>
using namespace std;

//往文本文件中寫入數據。
void write_content_file() {
    //如果text.txt文件不存在,系統會自動新建一個text.txt文件
    //如果text.txt文件存在了,系統會先把文件清空,除非我們設置了
    //打開文件的屬性:ios::app,那麼系統就會在文件的末尾追加內容
    ofstream fout("text.txt"); 
    if (!fout) { cerr << "文件打開失敗" << endl; return; }
    //插入一個字符串
    fout << "hello , i am fine." << endl;
    //fout其實就是一個指針,此時,fout它指向了字符串“hello , i am fine.”的末尾。
    //所以當我們希望往文件中插入新的數據時,它就會直接在最後追加。
    int num = 1;
    fout << num << endl;
    fout.close();
}
void read_content_file() {
    //把剛纔的內容讀取出來
    ifstream fin("text.txt");
    //以讀文件爲目的打開文件,最容易出現問題
    if (!fin) { cerr << "文件打開失敗" << endl; return; }
    //讀取一行的方法。
    string str;
    getline(fin, str);
    cout << str << endl;
    //其實,fin也是一個指針,現在它指向了文件中存放上一個函數變量num的內存空間
    //的首地址。
    int num;
    fin >> num;
    cout << num << endl;
    fin.close();
}
int main() {
    write_content_file();
    read_content_file();
    system("pause");
    return 0;
}

輸出結果:
這裏寫圖片描述 這裏寫圖片描述

(2)二進制文件的讀寫

對於二進制文件的讀寫,我們必須強調的一點就是打開文件屬性必須添加:ios::binary,其實就是告訴系統,我要按照二進制格式進行讀寫文件了。

具體代碼示例如下:

/************************************************************/
/*                程序作者:Willam                          */
/*                程序完成時間:2017/3/5                    */
/*                有任何問題請聯繫:[email protected]       */
/************************************************************/
//@儘量寫出完美的程序
#include<iostream>
#include<fstream>
#include<string>
using namespace std;

//往二進制文件中寫入數據。
void write_content_file() {
    ofstream fout("text.bat", ios::binary);
    if (!fout) { cerr << "文件打開失敗" << endl; return; }
    int num = 1;
    string str = "Hello word";
    //記住,write有兩個參數,第一個爲字符數組參數,第二個需要輸入到文件的數據的
    //大小
    fout.write((char*)&num, sizeof(int));
    fout.write(str.c_str(),sizeof(char)*(str.size()));

    fout.close();

}
//讀取二進制文件中的數據。
void read_content_file() {
    ifstream fin("text.bat", ios::binary);
    if (!fin) { cerr << "文件打開失敗" << endl; return; }
    int num;
    char buf[256] = {0};
    //記住,read有兩個參數,第一個爲字符數組參數,第二個需要輸入到文件的數據的
    //大小
    fin.read((char*)&num, sizeof(int));
    fin.read(buf, sizeof(char)*256);
    cout << num << endl;
    cout << buf << endl;
    fin.close();


}
int main() {
    write_content_file();
    read_content_file();
    system("pause");
    return 0;
}

輸出結果爲:
這裏寫圖片描述這裏寫圖片描述

首先,我們要記住我們是以二進制流方式打開和讀取文件的,所以我們的在text.bat中看到hello word前面會有四個空白符,因爲1的是int型,它佔用了四個字節,而且我們是以ASCII編碼的,所以0是空白符,1是辯題的開始。

由於文本文件本質上也是磁盤上的一個個二進制編碼,因此,讀寫二進制文件的代碼同樣可以讀寫文本文件,在文件類型不是很明確的讀寫操作中,直接使用二進制讀寫比較可取。

當然,我們有時候需要遍歷整個文件,然後,對文件內容進行操作,我們通過函數eof()來判斷文件是否到達文件末尾了,如果到達文件尾返回非0值,否則返回0。原型是int eof();下面一個例子來演示如果遍歷整個文件:

代碼如下:

/************************************************************/
/*                程序作者:Willam                          */
/*                程序完成時間:2017/3/5                    */
/*                有任何問題請聯繫:[email protected]       */
/************************************************************/
//@儘量寫出完美的程序
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
//將一個文件的內容複製到另外一個文件
void copy_file_content() {
    ifstream fin("text.txt");
    ofstream fout("text1.txt");
    if(!fin || !fout) { cerr << "文件打開失敗" << endl; return; }
    while (!fin.eof()) {
        char buf[256] = { 0 };
        fin.read(buf, sizeof(char) * 256);
        fout.write(buf, sizeof(char) * 256);
    }
}
int main() {
    copy_file_content();
    system("pause");
    return 0;
}

結果爲:
這裏寫圖片描述

5、文件定位

c++文件系統管理着兩個與文件相關聯的指針

  1. 讀指針:說明下一次讀在文件中的位置
  2. 寫指針:說明下一次寫在文件的位置,

每次執行了讀或寫操作後,這兩個指針的值都會自動變化,另外,c++還提供了修改這兩個指針值的函數分別是:

  • seekg(),設置下次讀的位置
  • seekp(),設置下一次寫的位置

這兩個函數的原型如下:

istream &seekg(streamoff offset,seek_dir origin);
ostream &seekp(streamoff offset,seek_dir origin);

其中,offset代表的是我們希望把指針移到的距離的大小,origin代表起點是哪裏,它的取值有三種如下:

ios::beg:  文件開頭
ios::cur:  文件當前位置
ios::end:  文件結尾

下面,我們舉兩個例子來說明上面兩個函數的使用方式:

file1.seekg(1234,ios::cur); //把文件的讀指針從當前位置向後移1234個字節file2.seekp(1234,ios::beg); //把文件的寫指針從文件開頭向後移1234個字節

其中,我們可以通過文件的定位來計算文件的大小,但是在介紹如何計算文件大小前,我們需要在介紹兩個函數:

對讀操作:seekg()與tellg()  //函數tellg()返回讀指針當前的值
對寫操作:seekp()與tellp()  //函數tellp()返回寫指針當前的值

所以下面我們就介紹如何計算文件的大小:

/************************************************************/
/*                程序作者:Willam                          */
/*                程序完成時間:2017/3/5                    */
/*                有任何問題請聯繫:[email protected]       */
/************************************************************/
//@儘量寫出完美的程序
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
void filesize() {
    ifstream fin("text.txt");
    if (!fin) { cerr << "文件打開失敗" << endl; return; }
    fin.seekg(0, ios::beg);
    streampos sp = fin.tellg();
    cout << "tellg起點爲:" << sp 
        << ",所以當其定位到文件末尾的指針時,其值就是文件的大小,如下列程序所示" 
        << endl;
    fin.seekg(0, ios::end);       //基地址爲文件結束處,偏移地址爲0,於是指針定位在文件結束處
    sp = fin.tellg(); //sp爲定位指針,因爲它在文件結束處,所以也就是文件的大小
    cout << "file size:"  << sp <<"字節"<< endl;

}
int main() {
    filesize();
    system("pause");
    return 0;
}

輸出:
這裏寫圖片描述這裏寫圖片描述

發佈了70 篇原創文章 · 獲贊 1096 · 訪問量 101萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章