c++中的流


streambuf類為緩沖區提供內存,並提供了用於填充緩沖區,訪問緩沖區,刷新新緩沖區和管理緩沖區內存的類方法。

ios_base類表示流的一般特征,如是否可讀,是二進制還是文本流等。

ios類基於ios_base,其中包括了一個之指向streambuf對象的的指針成員。

緩沖區介紹

輸入:從磁盤上讀取大量信息,將這些信息存儲在緩沖區中,然后每次從緩沖區讀取一個字節,到達緩沖區尾部,在從磁盤上讀取另一塊數據。

   但是鍵盤每次輸入一個字符時可以用不緩沖區,但是用緩沖區輸入可以讓程序沒有傳給程序之前返回並改正,c++通常是用戶按下回車鍵刷新緩沖區,

輸出:程序首先填滿緩沖區,然后把整塊數據給磁盤,並清空緩沖區,已備下一批輸入。

   對於輸出屏幕,輸出換行符時刷新緩沖區(也可能是其他:當輸入語句到達時,他刷新緩沖區中當前所有輸出)

刷新緩沖:用cout將字節發送給標准輸出時由於ostram類對cout對象輸出進行緩沖,輸出並不會直接發送到目標地址,而是在緩沖區中,直到緩沖區填滿,然后flush緩沖區,把內容發送出去並且清空緩沖,緩沖區通常為512byte或其整數倍。

  對於屏幕來說,程序不必等到緩沖區填滿,1.將換行符發送到緩沖區時刷新緩沖區;2.輸入即將發生時刷新緩沖區。

  由於程序運行時並不總是知道外部輸入的進度,很難控制是不是全部清除輸入緩沖區的內容。通常我們有可能只是希望放棄輸入緩沖區中的一部分,而不是全部。比如清除掉當前行、或者清除掉行尾的換行符等等。但要是緩沖區中已經有了下一行的內容,這部分可能是我們想保留的。這個時候最好不要用sync()。可以考慮用ignore函數代替

cin.ignore(numeric_limits<std::streamsize>::max(),'/n');//清除當前行
cin.ignore(numeric_limits<std::streamsize>::max());     //清除cin里所有內容

numeric_limits<std::streamsize>::max()不過是climits頭文件定義的流使用的最大值,你也可以用一個足夠大的整數代替它。使用ignore顯然能比sync()更精確控制緩沖區

cin.ignore(1024,'\n'),通常把第一個參數設置得足夠大,這樣實際上總是只有第二個參數'\n'起作用,所以這一句就是把回車(包括回車)之前的所以字符從輸入緩沖(流)中清除出去。

flush(cout);
cout<<"hello word"<<flush;
cout<<"hello word"<<endl;//刷新緩沖區並插入換行符 

以上三種方式均可以刷新緩沖區

但cout<<'\n';只是輸出換行符,不刷新緩沖區;\n只代表換行的轉義字符;\n是C中間的格式輸出換行,C++保留了下來;輸出'\n'是實際輸出了的'\10',往輸出流里添加了信息,所有的字符都是'\xx'的形式,換行符也是,你用其它任何字符輸出一下,前面都會有四個'*'填充的

程序員常常在調試時添加打印語句。這類語句應該保證‘一直’刷新流。否則,如果程序崩潰,輸出可能還停留在緩沖區中,從而導致關於程序崩潰位置的錯誤推斷

緩沖區的操作https://liam.page/2017/12/31/buffer-of-stream-in-Cpp/   https://izualzhy.cn/stream-buffer

#include <iostream> 
#include <sstream>
#include <cstdio>
using namespace std;

int main()
{
    stringstream ss;
    streambuf *buf=cout.rdbuf();
    //使用了新的緩沖區后,字符串不會輸出到屏幕,而是由stringstream管理
    cout.rdbuf(ss.rdbuf());
    cout<<"hello word"<<endl;
    
    printf("%s",ss.str().c_str());
    
    cout.rdbuf(buf);
    cout<<"hello word"<<endl;
    return 0;
}

基於控制台

 

繼承關系

c++將輸出看做字節流,將數值類型轉換為文本形式表示的字符流(將數據內部表示(二進制形式)轉換為字符字節組成的輸出流)

cout:對於void*類型,打印其地址數值(一端關聯顯示器另一端關聯程序

cin:從非空白字符開始,到與目標類型不匹配的第一個字符之間的全部內容(一端關聯鍵盤另一端關聯程序

cerr:關聯輸出設備(通常為顯示器)沒有緩沖區,信息直接輸出到屏幕,不會等到新的換行或緩沖區滿

clog:關聯輸出設備(通常為顯示器)有緩沖區

ostream:ostream &<<使用該操作符返回一個指向該對象的引用,插入操作符的返回值是調用該操作符的對象

        int a=0,sum=0;
    while(cin>>a)
        sum+=a;
    //輸入1 2 3 4 5 6c 60     

由於輸入緩沖,在用戶鍵入回車之前不會發送給程序,但是循環在了對字符c出停止了處理,導致cin>>a返回alse,終止循環

狀態流

  cin或cout對象包含一個描述狀態流的數據成員(繼承於ios_base)被定義為iostate,是bitmask類型,由eofbit,badbit,failbit,每一位可以是1(設置)0(清除)

clear:將狀態設置為他的參數,默認0清楚全部狀態;clear(eofbit),eofbit被設置,其他清除

setstate:setstate(eofbit)只設置eofbit,不影響其他位

get()與getline():get將換行符留在流中,getline抽取並丟棄輸入流中的換行符

char tem[60];
while(cin.get(tem,60))//空行終止 
{
    //...
}

//空行不終止,因為空行不導致getline設置failbit,getline抽取換行符並丟棄 
while(cin.getline(tem,60)) 
{
    //...
}
//空行終止 
while(cin.getline(tem,60)&&tem[0]!='\0') 
{
    //...
}

I/O異常

  exceps()返回一個為字段,包含三位:eofbit,failbit,badbit。修改流狀態后,clear()函數將當前流狀態與exceps()的返回值比較,如果返回值中某一位被設置,當前流轉態位對應位也被設置,則clear()引發ios_base::failure異常,exceptions()返回good不會引發異常,ios_base::failure從exception類派生,因此包含一個what()函數。

#include <iostream> 
#include <exception>
using namespace std;

int main()
{
    cin.exceptions(ios_base::failbit);
    
    int a=0,sum=0;
    try
    {    
        while(cin>>a)
            sum+=a;
    }
    catch(ios_base::failure &bf)
    {
        cout<<bf.what()<<endl;
    }
    
    cout<<"last input:"<<a<<endl;
    cout<<"sum:"<<sum<<endl;
    return 0;
}
#include <iostream>
#include <exception>
#include <fstream>
using namespace std;

//filebuf in;
//istream i(&in);
int main()
{
    int a=0,sum=0;
    while(cin>>a)//只有狀態流良好(所有為都被清楚)的情況下,while或if才返回true 
        sum+=a;

    cout<<"last input:"<<a<<endl;
    cout<<"sum:"<<sum<<endl;
    
    if(cin.fail()&&!cin.eof())
    {
        cin.clear();
        while(!isspace(cin.get()))//get提供不跳過空白字符讀的功能 
            continue;
    }
    else
    {
        cout<<" i cannot go on"<<endl;
        exit(1);
    }
    cout<<"now input a new number:";
    cin>>a;
    
    return 0;
}

cin.get(char &):到達文件末尾(真正的文件末尾,或鍵盤仿真文件末尾---win下ctrl+z---UNIX下ctrl+d,都不會給參數賦值,返回eof

cin.get():返回int類型的值,后不能跟抽取操作符,如:cin.get().get()>>c;

基於文件

繼承關系

程序寫入文件

  1. 創建一個ofstream對象管理輸出流
  2. 將對象與特定的文件關聯
  3. 用cout的方式使用該對象
  4. 使用colse()顯示關閉文件的鏈接;(對象過期時(程序中止))文件的鏈接會自動關閉
    #include <iostream>
    #include <fstream>
    using namespace std;
    
    int main()
    {
        ofstream of;
        of.open("1.txt");
        //ofstream of("1.txt");//也可以用構造函數 
        
        of<<"ds";
        of.close();//關閉文件將刷新緩沖區,確保文件被更新 
        return 0;
    }

讀取文件

  1. 創建一個ifstream對象管理輸出流
  2. 將對象與特定的文件關聯
  3. 用cin的方式使用該對象
  4. 使用colse()顯示關閉文件的鏈接;(對象過期時(程序中止))文件的鏈接會自動關閉
    #include <iostream>
    #include <fstream>
    using namespace std;
    
    int main()
    {
        ifstream fin;
        fin.open("1.txt");
        //ifstream fin("1.txt");//也可以用構造函數,可以用is_open()檢查是否打開成功,若成功流狀態為0
        
        string s;
        fin>>s;
        cout<<s<<endl;
        
        fin.close();//只是斷開文件的鏈接,對象與他的緩沖區仍然存在 
        return 0;
    }

文件流作為類成員時,其初始化只能是初始化列表方式,即構造對象時文件,不能在構造函數體中進行操作,否則文件打開失敗。

文本文件與二進制文件

  文本文件:

    所有內容都儲存為文本,如:以文本格式存儲123456,將存儲6個字符,計算機內部將浮點數轉為字符格式,這是<<插入操作符的工作;寫文本文件時,linux,UNIX。dos c++自動將換行符轉為回車和換行;讀文本文件時,將本地的換行符轉為c++格式;macintosh c++將換行轉為回車。

  二進制文件:

    計算機不存儲字符,而存儲64位double;字符的二進制和文本表示一樣(字符的ASCII二進制表示);較為精確,二進制保存數據更快,不需要轉換,占用空間小,可大量存儲;但二進制對於換行符,有歧義。二進制與文本文件的末未檢測方式也不同。

 

  二進制和文本模式的區別:
  在windows系統中,文本模式下,文件以"\r\n"代表換行。若以文本模式打開文件,並用fputs等函數寫入換行符"\n"時,函數會自動在"\n"前面加上"\r"。即實際寫入文件的是"\r\n" 。
  在類Unix/Linux系統中文本模式下,文件以"\n"代表換行。所以Linux系統中在文本模式和二進制模式下並無區別

 

#include <iostream>
#include <fstream>
using namespace std;

struct Stu
{
    int age;
    double weight;    
}stu;

int main()
{
    ofstream of;
    of.open("1.txt"); 
    
    //到達stu的結構地址,並且將6個字節復制到與of相關的文件中,用write函數可以以二進制形式存儲數據
    of.write((char *)&stu,sizeof(stu)); 
    of.close();//關閉文件將刷新緩沖區,確保文件被更新 
    
    //將文件中的sizeof(stu)個字節復制到結構體中 
    ifstream fin("1.txt");
    fin.read((char *)&stu,sizeof(stu));
    return 0;
}

基於字符串的I/O

繼承關系

#include <iostream>
#include <sstream>
using namespace std;

int main()
{
    ostringstream os;
    string s("123");
    os<<s;
      cout<<os.str()<<endl;
      
      ostringstream _os;
      _os<<os.str();
      cout<<_os.str()<<endl;//讓_os從os中提取數據,str()成員函數的使用可以讓istringstream對象返回一個string字符串
          
    return 0;
}

 清空stringstream對象中的內容

    vector<string> s(v.size(),"");//確定字符串數組的大小 
    stringstream ss;
    for(int i=0;i<v.size();++i)
    {
        ss<<v[i];
        s[i]=ss.str();
        ss.str("");    //清空stringstream對象中的內容,clear()只是清空了流的狀態    
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM