1. 程式人生 > >C++PrimerPlus學習之輸入,輸出和檔案

C++PrimerPlus學習之輸入,輸出和檔案

流和緩衝區

  • C++程式把輸入和輸出看作位元組流。輸入時,程式從輸入流中抽取位元組;輸出時,程式將位元組插入到輸出流中。
  • 流充當了程式和流源或流目標之間的橋樑。C++程式只是檢查位元組流,而不需要知道位元組來自何方和去向何處。
  • 使用緩衝區可以更高效地處理輸入和輸出。
    • 處理輸入時,緩衝區通常從磁碟讀取大量資訊,然後每次從緩衝區裡讀取一個位元組。
    • 處理輸出時,程式首先先填滿緩衝區,然後將整塊資料傳輸給硬碟,並清空緩衝區,以備下一批輸出使用。這被稱為重新整理緩衝區。但如果是通過鍵盤來輸入的話,在這種情況下,C++程式通常在使用者按下回車鍵時重新整理輸入緩衝區。
  • 流檔案的關係圖 在這裡插入圖片描述
    • streambuf
      類為緩衝區提供了記憶體,並提供了用於填充緩衝區,訪問緩衝區內容,重新整理緩衝區和管理緩衝區記憶體的類方法
    • ios_base類表示流的一般特徵,如是否可讀取,是二進位制還是文字流等
    • ios類基於ios_base,其中包括了一個指向streambuf物件的指標成員
    • ostream類是從ios類派生而來的,提供了輸出方法
    • istream類是從ios類派生而來的,提供了輸入方法
    • iostream類是基於istreamostream類的,因此繼承了輸入方法和輸出方法
    • fstream類是從iostream類派生而來的,而後者基於istreamostream類,因此它繼承了它們的方法
  • iostream
    檔案中的8個流物件(4個用於窄字元流,4個用於寬字元流)
    • cin物件對應於標準輸入流。在預設情況下,這個流被關聯到標準輸入裝置(通常是鍵盤)。wcin物件與此類似,但處理的是wchar_t型別。
    • cout物件與標準輸出流對應。在預設情況下,這個流被關聯到標準輸出裝置(通常為顯示器)。wcout物件與此類似。
    • cerr物件與標準錯誤流相對應,可用於顯示錯誤訊息。在預設情況下,這個流被關聯到標準輸出裝置(通常為顯示器)。這個流沒有被緩衝,這意味著資訊將被直接傳送到螢幕,而不會等到緩衝區填滿或新的換行符。wcerr物件與此類似。
    • clog物件也對應著標準錯誤流。在預設情況下,這個流被關聯到標準輸出裝置(通常為顯示器)。這個流被緩衝。wclog
      物件與此類似。

使用cout進行輸出

  • write()

    • 模板原型
      basic_ostream<charT,traits>& wirte(const char_type *s,streamsize n);
      
    • write()的第一個引數提供了要顯示的字串的地址,第二個引數指出要顯示多少個字元。使用cout呼叫write()時,將呼叫char具體化,因此返回型別為ostream &
    • 一個例子
      #include<bits/stdc++.h>
      using namespace std;
      int main()
      {
          const char* one="helloworld";
          const char* two="guoshen";
          int len=strlen(one);
          for(int i=1;i<=len;i++)
          {
              cout.write(one,i);
              cout<<endl;
          }
          cout.write(one,len+8);
      }
      
    • 注意,write()方法並不會在遇到空字元時自動停止列印字元。
  • 進位制轉換(hex(),dec(),oct())

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
        int a=1234;
        cout<<hex<<a<<endl;//16進位制
        cout<<oct<<a<<endl;//8進位制
        cout<<dec<<a<<endl;//10進位制
    }
    /*
    output:
    4d2
    2322
    1234
    */
    
  • int width()int width(int i)

    • 前者返回欄位寬度的當前設定,後者設定字元寬度為i個空格,並返回以前的欄位寬度

    • 調整欄位寬度

    • 只會影響下一個專案,然後欄位寬度將恢復為預設值

    • 預設是右對齊

    • 一個例子

      #include<bits/stdc++.h>
      using namespace std;
      int main()
      {
          cout<<'#';
          cout.width(12);
          cout<<12<<'#'<<24<<"#\n"<<endl;
      }
      /*
      output:
      #          12#24#
      */
      
  • fill(char a)

    • 填充未被使用的欄位

    • 一直有效,直到被更改

    • 一個例子

      #include<bits/stdc++.h>
      using namespace std;
      int main()
      {
          cout<<'#';
          cout.width(12);
          cout.fill('*');
          cout<<12<<'#'<<24<<"#\n"<<endl;
      }
      /*
      output:
      #**********12#24#
      */
      
  • precision(int i)

    • 設定浮點數的顯示精度為i

    • 預設精度為6位(但末尾的0將不顯示)

    • 一直有效,直到被更改

    • 一個例子

      #include<bits/stdc++.h>
      using namespace std;
      int main()
      {
          double pi=acos(-1.0);
          cout<<pi<<endl;
          cout.precision(2);
          cout<<pi<<endl;
          cout.precision(10);
          cout<<pi<<endl;
      }
      /*
      output:
      3.14159
      3.1
      3.141592654
      */
      
  • setf()

    • 第一個原型
      fmtflags setf(fmtflags);
      
      • fmtflagsbitmask型別的typdef名,用於儲存格式標記。
      • ios_base類中定義。
      • 引數是一個fmtflags值,指出要設定哪一位。返回值是型別為fmtflags的數字,指出所有標記以前的設定。如果打算以後恢復原始設定,則可以儲存這個值。
      • 格式常量表
    常量
    os_base::boolalpha 輸入和輸出bool值,可以為true或false
    os_base::showbase 對於輸出,使用C++基數字首(0,0x)
    os_base::showpoint 顯示末尾的小數點
    os_base::uppercase 對於16進位制輸出,使用大寫字母,E表示法
    os_base::showpos 在正數前面加+
    • 第二個原型

      fmtflags setf(fmtflags,fmtflags);
      
    • 第一個引數和以前一樣,也是一個包含了所需的fmtflags值。第二個引數指出要清除第一個引數中的哪一位。

    • 呼叫setf()的效果可以通過unsetf()消除

    • 引數表

第二個引數 第一個引數 含義
ios_base::basefield ios_base::dec 使用基數10
ios_base::basefield ios_base::oct 使用基數8
ios_base::basefield ios_base::hex 使用基數16
ios_base::floatfield ios_base::fixed 使用定點計數法
ios_base::floatfield ios_base::scientific 使用科學計數法
ios_base::adjustfield ios_base::left 使用左對齊
ios_base::adjustfield ios_base::right 使用右對齊
ios_base::adjustfield ios_base::internal 符號或基數字首左對齊,值右對齊

使用cin進行輸入

  • 流狀態
成員 描述
eofbit 如果到達檔案尾,則設定為1
badbit 如果流被破壞,則設定為1;例如,檔案讀取錯誤
failbit 如果輸入操作未能讀取預期的字元或輸出操作沒有寫入預期的字元,則設定為1
goodbit 另一種表示0的方法
good() 如果流可以使用(所有的位都被清除),則返回true
eof() 如果eofbit被設定,則返回true
bad() 如果badbit被設定,則返回true
fail() 如果badbit或failbit被設定,則返回true
rdstate () 返回流狀態
exceptions () 返回一個位掩碼,指出哪些標記導致異常被引發
exceptions(isostate ex) 設定哪些狀態將導致clear()引發異常;例如,如果ex是eofbit,則如果eofbit被設定,clear()將引發異常
clear(iostate s) 將流狀態設定為s;s的預設值為0(goodbit);如果(restate()&exception())!=0,則引發異常basic_ios::failure
setstate(iostate s) 呼叫clear(rdstate() | s).這將設定與s中設定的位對應的流狀態位,其他流狀態位保持不變
  • I/O和異常
    • 修改流狀態涉及clear()setstate(),這都將會使用clear(),修改流狀態後,clear()方法將當前的流狀態與exceptions()返回的值進行比較。如果在返回值(exceptions())中某一位被設定,而在當前狀態中對應位也被設定,則clear()將引發ios_base::failure異常。

    • exceptions()的預設設定為goodbit,也就是沒有引發異常,但過載的exceptions(iostate)函式使得能夠控制其行為:

      cin.exceptions(badbit)
      
    • 一個例子

      #include<bits/stdc++.h>
      using namespace std;
      int main()
      {
          cin.exceptions(ios_base::failbit);
          int x;
          int sum=0;
          try
          {
              while(cin>>x)
              {
                  sum+=x;
              }
          }
          catch(ios_base::failure &bf)
          {
              cout<<bf.what()<<endl;
          }
          cout<<sum<<endl;
      }
      /*
      input:
      10
      20
      30
      pi
      output:
      basic_ios::clear
      60
      */
      
      
  • 一些函式
    • get()getline()ignore()
      • get()會將換行符留在輸入流中,接下來的輸入操作首先會是換行符,而getline()不會

      • ignore()的原型為

        istream & ignore(int =1,int =EOF)
        

        該函式接受兩個引數,一個是數字,指定要讀取的最大字元數;另一個是字元,用作輸入分界符。下面的函式呼叫讀取並丟棄接下來的255個字元或直到到達第一個換行符

        cin.ignore(255,'\n');
        

檔案的輸入和輸出

  • 二進位制檔案
    • 二進位制檔案比較精確

    • 要使用成員函式read()write()

    • 適用於結構體和不使用虛擬函式的類。

    • 一個例子

      #include<bits/stdc++.h>
      using namespace std;
      struct planet
      {
          char name[20];
          double populatition;
          double g;
      };
      const char *file="planets.dat";
      inline void eatline(){while(cin.get()!='\n')continue;}
      
      int main()
      {
          planet p1;
          cout<<fixed<<right;
          ifstream fin;
          fin.open(file,ios_base::in|ios_base::binary);
          if(fin.is_open())
          {
              while(fin.read((char*)&p1,sizeof p1))
              {
                  cout<<setw(20)<<p1.name<<": "
                      <<setprecision(0)<<setw(12)<<p1.populatition
                      <<setprecision(2)<<setw(6)<<p1.g<<endl;
              }
              fin.close();
          }
      
          //add new data
          ofstream fout(file,ios_base::out|ios_base::app|ios_base::binary);
          if(!fout.is_open())
          {
              cerr<<"Can't open"<<file<<endl;
              exit(EXIT_FAILURE);
          }
      
          cin.get(p1.name,20);
          while(p1.name[0]!='\0')
          {
              eatline();
              cin>>p1.populatition;
              cin>>p1.g;
              eatline();
              fout.write((char*)&p1,sizeof p1);
              cin.get(p1.name,20);
          }
          fout.close();
          //show data
          fin.clear();
          fin.open(file,ios_base::in|ios_base::binary);
          if(fin.is_open())
          {
              while(fin.read((char*)&p1,sizeof p1))
              {
                  cout<<setw(20)<<p1.name<<": "
                      <<setprecision(0)<<setw(12)<<p1.populatition
                      <<setprecision(2)<<setw(6)<<p1.g<<endl;
              }
              fin.close();
          }
          cout<<"Done\n";
          return 0;
      }
      /*
      輸入請參照書本
      */
      
    • 隨機存取

      • seekg()seekp()函式
        • seekg()的原型(seekp()與之類似)

          basic_istream<charT,traits>& seekg(streamoff,ios_base::seekdir);
          basic_istream<charT,traits>& seekg(streampos);
          

          第一個原型定位到離第二個引數指定的檔案位置特定距離(單位為位元組)的位置;第二個原型定位到離檔案開頭特定距離(單位為位元組)的位置

        • 示例(假設fin是一個iftream物件)

          fin.seekg(30,ios_base::beg);//30 bytes beyond the begining
          fin.seekg(-1,ios_base::cur);//back up one byte
          fin.seekg(0,ios_base::end);//go to the end of the file
          fin.seekg(112);//go to the 113 bytes
          
      • 建立臨時檔案
        • tmpnam()
          • 原型

            char * tmpnam(char * pszName);
            

            建立一個臨時檔名,將它放在pszName指向的C-風格字串中

          • 常量L_tmpnam為檔名包含的最大字數,TMP_MAX為該函式在不生成重複檔名的情況下最多可呼叫次數

          • 一個例子

            #include<bits/stdc++.h>
            using namespace std;
            int main()
            {
                char tmp[L_tmpnam]={'\0'};
                for(int i=0;i<10;i++)
                {
                    tmpnam(tmp);
                    cout<<tmp<<endl;
                }
            }
            
            

核心格式化

  • C++庫提供了sstream族,它們使用相同的介面提供程式和string物件之間的I/O。

  • 標頭檔案sstream定義了一個從ostream類派生而來的ostringstream類。如果建立了一個ostringstream物件,則可以將資訊寫入其中,它將儲存這些資訊。

  • ostringstream類有一個名為str()的成員函式,該函式返回一個被初始化為緩衝區內容的字串物件

  • 一個例子

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
        ostringstream outstr;
        string tmp;
        getline(cin,tmp);
        int cap;
        cin>>cap;
        outstr<<"hello world "<<tmp<<" has a capacity of "<<cap<<endl;
        string result=outstr.str();
        cout<<result<<endl;
    }
    
    
  • istringstream類允許使用istream方法族讀取istringstream物件的資料,istringstream物件可以使用string物件進行初始化

  • 一個例子

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
        istringstream in(string("guo shen is a cool boy!"));
        string tmp;
        while(in>>tmp)
        {
            cout<<tmp<<endl;
        }
    }
    
    
  • 總之,istringstreamostringstream類使得能夠使用istreamostream類的方法來管理儲存在字串中的字元資料。