程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++之文件IO操作流

C++之文件IO操作流

編輯:C++入門知識

前兩節介紹了C++的IO流類庫,標准設備IO操作流中部分預定義流對象的成員函數以及IO格式控制。那今天我將繼續介紹關於C++中的流操作內容——文件IO操作流fstream。並會著重講解C++是如何對文件進行操作的。
  文件指存放在外部介質上的數據的集合。大家都知道操作系統是以文件為單位來對數據進行管理的。因此如果你要查找外部介質的數據,則先要按文件名找到指定文件,然後再從文件中讀取數據,如果要把數據存入外部介質中,如果沒有該文件,則先要建立文件,再向它輸入數據。由於文件的內容千變萬化,大小各不相同,為了統一處理,在C++中用文件流的形式來處理,文件流是以外存文件為輸入輸出對象的數據流。輸出文件流表示從內存流向外存文件的數據,輸入文件流則相反。根據文件中數據的組織形式,文件可分為兩類:文本文件和二進制文件。文本文件又稱為ASCII文件,它的每個字節存放一個ASCII碼,代表一個字符。二進制文件則是把內存中的數據,按照其在內存中的存儲形式原樣寫在磁盤上存放。比如一個整數20000,在內存中在兩個字節,而按文本形式輸出則占5個字節。因此在以文本形式輸出時,一個字節對應一個字符,因而便於字符的輸出,缺點則是占用存儲空間較多。用二進制形式輸出數據,節省了轉化時間和存儲空間,但不能直接以字符的形式輸出。
  1.在C++中對文件進行操作分為以下幾個步驟:(1)建立文件流對象;(2)打開或建立文件;(3)進行讀寫操作;(4)關閉文件;用於文件IO操作的流類主要有三個fstream(輸入輸出文件流),ifstream(輸入文件流)和ofstream(輸出文件流);而這三個類都包含在頭文件fstream中,所以程序中對文件進行操作必須包含該頭文件。首先建立流對象,然後使用文件流類的成員函數open打開文件,即把文件流對象和指定的磁盤文件建立關聯。成員函數open的一般形式為:
  文件流對象.open(文件名,使用方式);
其中文件名可以包括路徑(如:e:\c++\file.txt),如果缺少路徑,則默認為當前目錄。使用方式則是指文件將被如何打開。以下就是文件的部分使用方式,都是ios基類中的枚舉類型的值:

\
 
此外打開方式有幾個注意點:
(1)因為nocreate和noreplace,與系統平台相關密切,所以在C++標准中去掉了對它的支持。
(2)每一個打開的文件都有一個文件指針,指針的開始位置由打開方式指定,每次讀寫都從文件指針的當前位置開始。每讀一個字節,指針就後移一個字節。當文件指針移到最後,會遇到文件結束符EOF,此時流對象的成員函數eof的值為非0值,表示文件結束。
(3)用in方式打開文件只能用於輸入數據,而且該文件必須已經存在。
(4)用app方式打開文件,此時文件必須存在,打開時文件指針處於末尾,且該方式只能用於輸出。
(5)用ate方式打開一個已存在的文件,文件指針自動移到文件末尾,數據可以寫入到其中。
如果文件需要用兩種或多種方式打開,則用"|"來分隔組合在一起。除了用open成員函數打開文件,還可以用文件流類的構造函數來打開文件,其參數和默認值與open函數完全相同。比如:文件流類 stream(文件名,使用方法);如果文件打開操作失敗,open函數的返回值為0,用構造函數打開的話,流對象的值為0。所以無論用哪一種方式打開文件,都需要在程序中測試文件是否成功打開。
   在每次對文件IO操作結束後,都需要把文件關閉,那麼就需要用到文件流類的成員函數close,一般調用形式:流對象.close();關閉實際上就是文件流對象和磁盤文件失去關聯。
  2.介紹完文件的打開和關閉,接下來說說文件的讀寫。我將分別從文本文件讀寫和二進制文件的讀寫來介紹。其實文件的讀寫是十分容易的。流類庫中的IO操作<<、>>、put、get、getline、read和write都可以用於文件的輸入輸出。
  (1)文本文件的讀寫:
寫文件:
 1 #include "stdafx.h"
 2 #include <iostream>
 3 #include <fstream>
 4
 5 int main()
 6 {
 7     //打開文件
 8     std::ofstream file("file.txt",std::ios::out|std::ios::ate);
 9     if(!file)
10     {
11         std::cout<<"不可以打開文件"<<std::endl;
12         exit(1);
13     }
14
15     //寫文件
16     file<<"hello c++!\n";
17
18     char ch;
19     while(std::cin.get(ch))
20     {
21         if(ch=='\n')
22             break;
23         file.put(ch);
24     }
25
26     //關閉文件
27     file.close();
28
29     return 0;
30 }
鍵盤輸入字符:
 \
讀文件file.txt:
 1 #include "stdafx.h"
 2 #include <iostream>
 3 #include <fstream>
 4
 5 int main()
 6 {
 7     //打開文件
 8     std::ifstream rfile("file.txt",std::ios::in);
 9     if(!rfile)
10     {
11         std::cout<<"不可以打開文件"<<std::endl;
12         exit(1);
13     }
14
15     //讀文件
16     char str[100];
17     rfile.getline(str,100);//讀到'\n'終止
18     std::cout<<str<<std::endl;
19
20     char rch;
21     while(rfile.get(rch))//文件指針指向字符‘\n’的下一個
22     {
23         std::cout.put(rch);
24     }
25
26     std::cout<<std::endl;
27
28     //關閉文件
29     rfile.close();
30
31     return 0;
32 }
讀出顯示字符:
 \
其實建立ifstream類和ofstream類的對象時,ios:in和ios:out可以省略,因為ifstream類默認為ios:in,ofstream類默認為ios:out;
  (2)最初設計流的目的是用於文本,因此在默認情況下,文件用文本方式打開。在以文本模式輸出時,若遇到換行符"\n"(十進制為10)則自動擴充為回車換行符(十進制為13和10)。所以,如果我們輸入的整數10,那麼在文件輸出時會轉化為13和10,然而這並不是我們所需要的。為了解決這樣的問題,就要采用而二進制模式,使其所寫的字符不轉換。在對二進制文件進行IO操作時,打開文件時要指定方式ios::binary,即以二進制形式傳送和存儲。接下來我用read函數和write函數來對二進制文件進行讀寫。在示例描述之前先簡單介紹一下這兩個函數:
  read函數常用格式為:文件流對象.read(char *buf,int len);
  write函數常用格式為:文件流對象.write(const char *buf,int len);
兩者格式上差不多,第一個參數是一個字符指針,用於指向讀入讀出數據所放的內存空間的其實地址。第二個參數是一個整數,表示要讀入讀出的數據的字節數。以下是二進制文件的讀寫的示例:
定義一個精靈類(用於文件數據處理):
 1 class Sprite
 2 {
 3     private:
 4         std::string profession;//職業
 5         std::string weapon;//武器
 6         static int count;//個數
 7     public:
 8         Sprite(){}
 9         Sprite(std::string profession,std::string weapon):profession(profession),weapon(weapon)
10         {
11         }
12         void showSprite();//顯示精靈信息
13 };
14
15 int Sprite::count=0;
16
17 void Sprite::showSprite()
18 {
19     ++count;
20     std::cout<<"精靈"<<count<<" 職業:"<<profession<<" 武器:"<<weapon<<std::endl;
21 }
寫文件:
 1 #include "stdafx.h"
 2 #include <iostream>
 3 #include <fstream>
 4 #include <string>
 5
 6 int main()
 7 {
 8     //建立對象數組
 9     Sprite sprites[3]={
10         Sprite("法師","魔杖"),
11         Sprite("戰士","屠龍寶刀"),
12         Sprite("道士","倚天劍")
13     };
14
15     //打開文件
16     std::ofstream file("file.dat",std::ios::ate|std::ios::binary);
17     if(!file)
18     {
19         std::cout<<"文件打開失敗!";
20         abort();//等同於exit
21     }
22
23     //寫文件
24     for(int i=0;i<3;i++)
25         file.write((char*) &sprites[i],sizeof(sprites[i]));
26
27     //關閉文件
28     file.close();
29
30     return 0;
31 }
讀文件file.dat:
 1 #include "stdafx.h"
 2 #include <iostream>
 3 #include <fstream>
 4 #include <string>
 5
 6 int main()
 7 {
 8     //建立對象數組
 9     Sprite rsprites[3];
10
11     //打開文件
12     std::ifstream rfile("file.dat",std::ios::binary);
13     if(!rfile)
14     {
15         std::cout<<"文件打開失敗!";
16         return 1;//等同於exit
17     }
18
19     //讀文件
20     for(int i=0;i<3;i++)
21     {
22         rfile.read((char*) &rsprites[i],sizeof(rsprites[i]));
23         rsprites[i].showSprite();
24     }
25
26     //關閉文件
27     rfile.close();
28
29     return 0;
30 }
讀出顯示字符:

\
 
在read函數還是write函數裡都要把數據轉化為char*類型,代碼中sizeof函數是用於確定要讀入讀出的字節數。
  在文件結束處有個標志位EOF,在用文件流讀取文件時,使用成員函數eof()(函數原型:int eof())可以檢測到結束符。如果該函數返回值為非零,則表示到達文件末尾。返回零則表示未達到文件末尾。
  (3)前面所介紹的文件都是按順序來讀取的的,C++中又提供了針對於文件讀寫指針的相關成員函數,使得我們可以在IO流中隨意移動文件指針,從而對文件的進行隨機地讀寫。類istream針對讀指針提供3個成員函數:
  tellg()//返回輸入文件讀指針的當前位置;
  seekg(文件中的位置)//將輸入文件中的讀指針移動到指定位置
  seekg(位移量,參照位置)//以參照位置為基准移動若干字節
其中參照位置是枚舉值:
beg//從文件開頭計算要移動的字節數
cur//從文件指針的當前位置計算要移動的字節數
end//從文件的末尾計算要移動的字節數
如果參照位置省略,則默認為beg。而類ostream針對寫指針提供的3個成員函數:
  tellp()//返回輸出文件寫指針的當前位置;
  seekp(文件中的位置)//將輸出文件中的寫指針移動到指定位置
  seekp(位移量,參照位置)//以參照位置為基准移動若干字節
現在我對上一示例中讀取二進制文件代碼稍作更改:
 1 #include "stdafx.h"
 2 #include <iostream>
 3 #include <fstream>
 4 #include <string>
 5
 6 int main()
 7 {
 8     //建立對象數組
 9     Sprite rsprites[3];
10
11     //打開文件
12     std::ifstream rfile("file.dat",std::ios::binary);
13     if(!rfile)
14     {
15         std::cout<<"文件打開失敗!";
16         return 1;//等同於exit
17     }
18
19     //讀文件
20     for(int i=0;i<3;i++)
21     {
22         rfile.read((char*) &rsprites[i],sizeof(rsprites[i]));
23         rsprites[i].showSprite();
24     }
25
26     Sprite rsprite;//建立對象
27
28     std::cout<<"改變讀取順序:"<<std::endl;
29     rfile.seekg(sizeof(Sprite)*2,std::ios::beg);//讀取精靈道士信息
30     rfile.read((char*) &rsprite,sizeof(Sprite));
31     rsprite.showSprite();
32    
33     rfile.seekg(-int(sizeof(Sprite)*2),std::ios::end);//讀取精靈戰士信息
34     rfile.read((char*) &rsprite,sizeof(Sprite));
35     rsprite.showSprite();
36
37     rfile.seekg(-int(sizeof(Sprite)*2),std::ios::cur);//讀取精靈法師信息
38     rfile.read((char*) &rsprite,sizeof(Sprite));
39     rsprite.showSprite();
40
41     //關閉文件
42     rfile.close();
43
44     return 0;
45 }
結果:

 \

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved