官术网_书友最值得收藏!

  • Visual C++實用教程
  • 鄭阿奇編著
  • 2047字
  • 2018-12-30 12:04:35

2.6 輸入/輸出流

在C++中,沒有專門的內部輸入/輸出語句。但為了方便用戶靈活使用輸入/輸出功能,C++提供了兩套輸入/輸出方法:一套是與C語言相兼容的輸入/輸出函數,如printf和scanf函數等;另一套是使用功能強大的輸入/輸出流庫ios。

盡管printf和scanf函數可以使用格式字符串來輸入/輸出各種不同數據,但卻不能操作類對象數據。而C++的輸入/輸出流庫ios不僅可以實現printf和scanf函數的功能,而且可以通過對提取運算符“>>”和插入運算符“<<”進行重載,從而實現類對象數據流的操作,擴展流的輸入/輸出功能。

2.6.1 流類和流對象

在C++中,輸入/輸出是由“流”來處理的。所謂流,它是C++的一個核心概念,數據從一個位置到另一個位置的流動抽象為流。當數據從鍵盤或磁盤文件流入到程序中時,這樣的流稱為輸入流,而當數據從程序中流向屏幕或磁盤文件時,這樣的流稱為輸出流。當流被建立后就可以使用一些特定的操作從流中獲取數據或者向流中添加數據。從流中獲取數據的操作稱為提取操作,向流中添加數據的操作稱為插入操作。

C++針對流的特點,構造了功能強大的輸入/輸出流庫,它具有面向對象的特性,其繼承結構如圖2.2所示。

圖2.2中,ios類用來提供一些關于對流狀態進行設置的功能,它是一個虛基類,其他類都是從這個類派生而來的,但streambuf不是ios類的派生類,在類ios中只有一個指針成員,指向streambuf類的一個對象。streambuf類是用來為ios類及其派生類提供對數據的緩沖支持。所謂緩沖,是指系統在主存中開辟一個專用的區域來臨時存放輸入/輸出信息,這個區域稱為緩沖區。有了緩沖區以后,輸入/輸出時所占用的CPU時間就大大減少了,提高了系統的效率。這是因為只有當緩沖區滿時,或當前送入的數據為新的一行時,系統才對流中的數據進行處理,稱為刷新。

圖2.2 C++的輸入/輸出流庫

itream和ostream類均是ios的公有派生類,前者提供了向流中插入數據的有關操作,后者則提供了從流中提取數據的有關操作。iostream類是itream和ostream類公有派生的,該類并沒有提供新的操作,只是將itream和ostream類綜合在一起,提供一種方便。

為了方便用戶對基本輸入/輸出流進行操作,C++提供了4個預定義的標準流對象:cin、cout、cerr和clog。當在程序中包含了頭文件“iostream.h”,編譯器調用相應的構造函數,產生這4個標準流對象,在程序中就可以直接使用它們了。其中,cin是istream類的對象,用處理標準輸入,即鍵盤輸入。cout是ostream類的對象,用處理標準輸出,即屏幕輸出。cerr和clog都是ostream類的對象,用來處理標準出錯信息,并將信息顯示在屏幕上。在這4個標準流對象中,除了cerr不支持緩沖外,其余3個都帶有緩沖區。

標準流通常使用插入運算符“<<”和提取運算符“>>”來進行輸入/輸出操作,而且系統還會自動地完成數據類型的轉換。由于以前已討論過cin和cout的基本用法,對于cerr和clog也可同樣使用,因此這里不再重復。

2.6.2 流的格式控制和錯誤處理

C++標準的輸入/輸出流提供了兩種格式的控制方式,一種是使用ios類中的相關成員函數,如width、precision和fill等,另一種是可以直接使用的格式操作算子,如oct、hex和dec等。下面分別來討論它們的使用方法及流的錯誤處理。

1.使用格式控制成員函數

在ios類中控制輸入/輸出的成員函數有:

int ios::width();                    // 返回當前的寬度設置
int ios::width(int);                 // 設置寬度并返回上一次的設置
int ios::precision();                // 返回當前的精度設置
int ios::precision(int);             // 設置精度并返回上一次的設置
char ios::fill();                    // 返回當前空位填充的字符
char ios::fill(char);                // 設置空位填充的字符并返回上一次的設置
long ios::setf(long);                // 設置狀態標志并返回上一次的狀態標志
long ios::unsetf(long);              // 消除狀態標志并返回上一次的狀態標志
long ios::flags();                   // 返回當前的狀態標志
long ios::flags(long);              // 設置狀態標志并返回上一次的狀態標志

在這些成員函數中,前面6個成員函數在以前曾經討論過,這里不再重復。后面的4個成員函數都跟狀態標志有關,各種狀態值之間都是通過“|”(或運算)組合而成的,在ios類中是一個公共的枚舉類型,各個標志代表的含義如下:

ios::skipws           跳過輸入中的空白符
ios::left             輸出數據按輸出域左對齊
ios::right            輸出數據按輸出域右對齊(默認)
ios::internal         數據的符號左對齊,數據本身右對齊,符號和數據之間為填充字符
ios::dec              轉換為十進制形式
ios::oct              轉換為八進制形式
ios::hex              轉換為十六進制形式
ios::showbase         輸出的數值前面帶有基數符號(0或0x)
ios::showpoint        顯示浮點數的小數點和后面的0
ios::uppercase        用大寫字母(A~F)輸出十六進制數
ios::showpos          顯示正數前面的“+”號
ios::scientific       按科學記數法顯示浮點數
ios::fixed            用定點格式顯示浮點數
ios::unitbuf          輸入操作完成后立即刷新緩沖區
ios::stdio            每次輸入操作完成后刷新stdout和stderr

下面舉例說明標志位的用法。

【例Ex_FormatFunc】 使用格式控制成員函數

#include <iostream.h>
int  main()
{
    int nNum = 12345;
    double dNum = 12345.6789;
    char *str[] = {"This", "is", "a Test!"};
    cout.setf(ios::oct|ios::showbase|ios::showpos);
    // 設置標志:八進制,顯示基和正號
    cout<<nNum<<"\t"<<dNum<<endl;
    cout.setf(ios::hex|ios::scientific|ios::uppercase);
    // 設置十六進制,科學記數法和大寫標志
    cout<<nNum<<"\t"<<dNum<<endl;
    cout.fill('*');                    // 設置填充符號為*
    for (int i=0; i<3; i++)
    {
        cout.width(12);     cout<<str[i]<<"  ";
    }
    cout<<endl;
    cout.setf(ios::left);               // 設置標志:左對齊
    for (i=0; i<3; i++)
    {
        cout.width(12);     cout<<str[i]<<"  ";
    }
    cout<<endl;
    return 0;
}

程序運行結果如下:

030071 +12345.7

0X3039 +1.234568E+004

********This **********is *****a Test!

This******** is********** a Test!*****

2.使用格式算子

前面介紹的使用成員函數進行格式控制的方法中,每次都要使用一條語句,這樣操作起來比較煩瑣。為此,C++提供了一些格式算子來簡化上述操作。格式算子是一個對象,可以直接用插入符或提取符來操作。C++提供的預定義格式算子如表2.2所示。

表2.2 C++預定義的格式算子

需要說明的是,若使用從resetiosflags一直到后面的格式算子,則還需在程序中包含頭文件iomanip.h。下面的例子與【例Ex_FormatFunc】的運行結果完全相同,只不過是使用了格式算子。

【例Ex_Formator】 使用格式算子

#include <iostream.h>
#include <iomanip.h>
int  main()
{
     int nNum=12345;
    double dNum = 12345.6789;
    char *str[] = {"This", "is", "a Test!"};
    cout<<setiosflags(ios::oct|ios::showbase|ios::showpos);
     // 設置八進制,顯示基和正號
    cout<<nNum<<"\t"<<dNum<<endl;
    cout<<setiosflags(ios::hex|ios::scientific|ios::uppercase);
     // 設置十六進制,科學記數法和大寫標志
    cout<<nNum<<"\t"<<dNum<<endl;
    cout<<setfill('*');               // 設置填充符號為*
    for (int i=0; i<3; i++)
        cout<<setw(12)<<str[i]<<"  ";
    cout<<endl;
    cout<<setiosflags(ios::left);     // 設置標志:左對齊
    for (i=0; i<3; i++)
        cout<<setw(12)<<str[i]<<"  ";
    cout<<endl;
    return 0;
}

3.流的錯誤處理

在輸入/輸出過程中,一旦發現操作錯誤,C++流就會將發生的錯誤記錄下來。用戶可以使用C++提供的錯誤檢測功能,檢測和查明錯誤發生的原因和性質,然后調用clear函數清除錯誤狀態,使流能夠恢復處理。

在ios類中,定義了一個公有枚舉成員io_state來記錄各種錯誤的性質:

enum io_state
{
    goodbit    =0x00,               // 正常
    eofbit     =0x01,               // 已達到文件尾
    failbit    =0x02,               // 操作失敗
    badbit     =0x04                // 非法操作
};

在ios類中又定義了檢測上述流狀態的下列成員函數:

int  ios::rdstate();                    // 返回當前的流狀態,它等于io_state中的枚舉值
int  ios::bad();                        // 如果badbit位被置1,返回非0
void ios::clear(int);                   // 清除錯誤狀態
int  ios::eof();                        // 返回非0表示提取操作已到文件尾
int  ios::fail();                       // 如果failbit位被置1,返回非0
int  ios::good();                       // 操作正常時,返回非0

可以利用上述函數來檢測流是否錯誤,然后進行相關處理。

【例Ex_ManipError】 檢測流的錯誤

#include <iostream.h>
int  main()
{
    int i, s;
    char buf[80];
    cout<<"輸入一個整數:";
    cin>>i;
    s = cin.rdstate();
    cout<<"流狀態為:"<<hex<<s<<endl;
    while (s)
    {
        cin.clear();
        cin.getline(buf, 80);
        cout<<"非法輸入,重新輸入一個整數:";
        cin>>i;
        s = cin.rdstate();
    }
    return 0;
}

程序運行結果如下:

輸入一個整數:a?

流狀態為:2

非法輸入,重新輸入一個整數:abcd?

非法輸入,重新輸入一個整數:12?

該程序檢測輸入的數據是否為整數,若不是,則要求重新輸入。需要說明的是,若輸入一個浮點數,C++會自動進行類型轉換,不會發生錯誤。只有輸入字符或字符串時,才會產生輸入錯誤,但由于cin有緩沖區,是一個緩沖流,輸入的字符或字符串會暫時保存到它的緩沖區中,因此為了能繼續提取用戶的輸入,必須先將緩沖區清空,語句“cin.getline(buf, 80);”就是起到這樣的作用。如果沒有這條語句,就必然會導致輸入流不能正常工作,而產生死循環。

2.6.3 使用輸入/輸出成員函數

不同數據類型的多次輸入/輸出可以通過插入符“<<”和提取符“>>”來進行,但是如果想要更為細致地控制,例如希望把輸入的空格作為一個字符,就需要使用istream和ostream類中的相關成員函數。

1.輸入操作的成員函數

數據的輸入/輸出可以分為三大類:字符類、字符串和數據。

(1)使用get和getline函數

用于輸入字符或字符串的成員函數get原型如下:

int get();
istream& get( char& rch );
istream& get( char* pch, int nCount, char delim = '\n' );

第1種形式是從輸入流中提取一個字符,并轉換成整型數值。第2種形式是從輸入流中提取字符到rch中。第3種形式是從輸入流中提取一個字符串并由pch返回,nCount用來指定提取字符的最多個數,delim用來指定結束字符,默認時是'\n'。

函數getline原型如下:

istream& getline( char* pch, int nCount, char delim = '\n' );

它是用來從輸入流中提取一個輸入行,并把提取的字符串由pch返回,nCount和delim的含義同上。這些函數可以從輸入流中提取任何字符,包括空格等。

【例Ex_GetAndGetLine】 get和getline的使用

#include <iostream.h>
int  main()
{
    char s1[80], s2[80], s3[80];
    cout<<"請輸入一個字符:";
    cout<<cin.get()<<endl;
    cin.get();                      // 提取換行符
    cout<<"請輸入一行字符串:";
    for (int i=0; i<80; i++)
    {
        cin.get(s1[i]);
        if (s1[i] == '\n')
        {
            s1[i] = '\0';
            break;                  // 退出for循環
        }
    }
    cout<<s1<<endl;
    cout<<"請輸入一行字符串:";
    cin.get(s2,80);
    cout<<s2<<endl;
    cin.get();                      // 提取換行符
    cout<<"請輸入一行字符串:";
    cin.getline(s3,80);
    cout<<s3<<endl;
    return 0;
}

程序運行結果如下:

請輸入一個字符:A?

65

請輸入一行字符串:This is a test!?

This is a test!

請輸入一行字符串:Computer?

Computer

請輸入一行字符串:你今天過得好嗎??

你今天過得好嗎?

需要說明的是,在用get函數提取字符串時,如果遇到換行符就會結束提取,此時換行符仍保留在緩沖區中,當下次提取字符串時就會不正常,而getline在提取字符串時,換行符也會被提取,但不保存它。因此,當提取一行字符串時,最好能使用函數getline。

(2)使用read函數

read函數不僅可以讀取字符或字符串(稱為文本流),而且可以讀取字節流。其原型如下:

istream& read( char* pch, int nCount );
istream& read( unsigned char* puch, int nCount );
istream& read( signed char* psch, int nCount );

read函數的這幾種形式都是從輸入流中讀取由nCount指定數目的字節并將它們放在由pch或puch或psch指定的數組中。

【例Ex_Read】 read函數的使用

#include <iostream.h>
int  main()
{
    char data[80];
    cout<<"請輸入:"<<endl;
    cin.read(data, 80);
    data[cin.gcount()] = '\0';
    cout<<endl<<data<<endl;
    return 0;
}

程序運行結果如下:

請輸入:

12345?

ABCDE?

This is a test!?

^Z?

12345

ABCDE

This is a test!

其中,^Z表示用戶按下【Ctrl+Z】組合鍵,“^Z+回車鍵”表示數據輸入提前結束。gcount是istream類的另一個成員函數,用來返回上一次提取的字符個數。從這個例子可以看出,當用read函數讀取數據時,不會因為換行符而結束讀取,因此它可以讀取多個行的字符串,這在許多場合下是很有用處的。

2.輸出操作的成員函數

ostream類中用于輸出單個字符或字節的成員函數是put和write,它們的原型如下:

ostream& put( char ch );
ostream& write( const char* pch, int nCount );
ostream& write( const unsigned char* puch, int nCount );
ostream& write( const signed char* psch, int nCount );

例如:

char data[80];
cout<<"請輸入:"<<endl;
cin.read(data, 80);
cout.write(data,80);  cout<<endl;

2.6.4 提取和插入運算符重載

C++中的一個最引人注目的特性是允許用戶重載“>>”和“<<”運算符,以便用戶利用標準的輸入/輸出流來輸入/輸出自己定義的數據類型(包括類),實現對象的輸入/輸出。

重載這兩個運算符時,雖然可使用別的方法,但最好將重載聲明為類的友元函數,以便能訪問類中的私有成員。下面來看一個示例。

【例Ex_ExtractAndInsert】 提取和插入運算符的重載

#include <iostream.h>
class CStudent
{
public:
     friend ostream&operator<<(ostream&os,CStudent&stu);
     friend istream&operator>>(istream&is,CStudent&stu);
private:
     char strName[10];                 // 姓名
     char strID[10];                   // 學號
     float fScore[3];                  // 三門成績
};
ostream& operator<< ( ostream& os, CStudent& stu )
{
     os<<endl<<"學生信息如下:"<<endl;
     os<<"姓名:"<<stu.strName<<endl;
     os<<"學號:"<<stu.strID<<endl;
     os<<"成績:"<<stu.fScore[0]<<",\t"<<stu.fScore[1]<<",\t"<<stu.fScore[2]<<endl;
     return os;
}
istream& operator>> ( istream& is, CStudent& stu )
{
     cout<<"請輸入學生信息"<<endl;
     cout<<"姓名:";         is>>stu.strName;
     cout<<"學號:";         is>>stu.strID;
     cout<<"三門成績:";     is>>stu.fScore[0]>>stu.fScore[1]>>stu.fScore[2];
     return is;
}
int  main()
{
     CStudent one;
     cin>>one;
     cout<<one;
     return 0;
}

程序運行結果如下:

請輸入學生信息

姓名:LiMing?

學號:20110212?

三門成績:80 90 75?

學生信息如下:

姓名:LiMing

學號:20110212

三門成績:80, 90, 75

經重載提取和插入運算符后,通過cin和cout實現了對象的直接輸入和輸出。

2.6.5 文件流及其處理

1.文件流概述

C++將文件看成由連續的字符(字節)的數據順序組成的。根據文件中數據的組織方式,可分為文本文件(ASCII文件)和二進制文件。文本文件中每一個字節用以存放一個字符的ASCII碼值,而二進制文件是將數據以二進制存放在文件中,它保持了數據在內存中存放的原有格式。

無論是文本文件還是二進制文件,都需要用“文件指針”來操縱。一個文件指針總是和一個文件所關聯的,當文件每一次打開時,文件指針指向文件的開始,隨著對文件的處理,文件指針不斷地在文件中移動,并一直指向最新處理的字符(字節)位置。

文件處理有兩種方式,一種稱為文件的順序處理,即從文件的第一個字符(字節)開始順序處理到文件的最后一個字符(字節),文件指針也相應地從文件的開始位置到文件的結尾。另一種稱為文件的隨機處理,即在文件中通過C++相關的函數移動文件指針,并指向所要處理的字符(字節)位置。按這兩種處理方式,可將文件相應地稱為順序文件隨機文件

為方便用戶對文件的操作,C++提供了文件操作的文件流庫,它的體系結構如圖2.3所示。其中,ifstream類是從istream類公有派生而來的,用來支持從輸入文件中提取數據的各種操作。ofstream類是從ostream類公有派生而來的,用來實現把數據寫入文件中的各種操作。fstream類是從iostream類公有派生而來的,提供從文件中提取數據或把數據寫入文件的各種操作。filebuf類是從streambuf類派生而來的,用來管理磁盤文件的緩沖區,應用程序中一般不涉及該類。

圖2.3 C++的文件流庫

在使用上述類的成員函數進行文件操作時,需要在程序中包含頭文件fstream.h。文件操作一般是按打開文件、讀寫文件、關閉文件這3個步驟進行的。

2.順序文件操作

文件的順序處理是文件操作中最簡單的一種方式。在C++中打開或創建一個指定的文件需要下列兩個步驟:

(1)聲明一個ifstream、ofstream或fstream類對象。例如:

ifstream infile;              // 聲明一個輸入(讀)文件流對象
ofstream outfile;             // 聲明一個輸出(寫)文件流對象
fstream iofile;               // 聲明一個可讀可寫的文件流對象

(2)使用文件流類的成員函數打開或創建一個指定的文件,使得該文件與聲明的文件流對象聯系起來,這樣對流對象的操作也就是對文件的操作。例如:

infile.open("file1.txt");
outfile.open("file2.txt");
iofile.open("file3.txt",ios::in | ios::out);

上述這兩步操作也可合為一步進行,即在聲明對象時指定文件名。例如:

ifstream infile("file1.txt");
ofstream outfile("file2.txt");
fstream iofile("file3.txt",ios::in | ios::out);

事實上,ifstream、ofstream或fstream類構造函數中總有一種原型和它的成員函數open功能相同。它們的函數原型如下:

ifstream( const char* szName, int nMode = ios::in, int nProt = filebuf::openprot );
void ifstream::open( const char* szName, int nMode = ios::in, int nProt = filebuf::openprot );
ofstream( const char* szName, int nMode = ios::out, int nProt = filebuf::openprot );
void ofstream::open( const char* szName, int nMode = ios::out, int nProt = filebuf::openprot );
fstream( const char* szName, int nMode, int nProt = filebuf::openprot );
void fstream::open( const char* szName, int nMode, int nProt = filebuf::openprot );

其中,參數szName用來指定要打開的文件名,包括路徑和擴展名,Mode指定文件的訪問方式,表2.3列出了open函數可以使用的訪問方式。參數Prot用來指定文件的共享方式,默認時是filebuf::openprot,表示DOS兼容的方式。

表2.3 文件訪問方式

需要說明的是,nMode指定文件的訪問方式通過“|”(或)運算組合而成。其中,ios::trunc方式將消除文件原有內容,在使用時要特別小心,它通常與ios::out、ios::ate、ios::app和ios:in進行“|”組合,如ios::out| ios::trunc。

ios::binary是二進制文件方式,通常可以有這樣的組合:

ios::in|ios::binary              表示打開一個只讀的二進制文件
ios::out|ios::binary             表示打開一個可寫的二進制文件
ios::in|ios::out|ios::binary     表示打開一個可讀可寫的二進制文件

在使用文件過程中,一定不要忘記:當文件使用結束后要及時調用close函數關閉,以防止文件再被“誤用”。

當文件打開后,就可以對文件進行讀寫操作。若從一個文件中讀出數據,可以使用get、getline、read函數及提取符“>>”;而向一個文件寫入數據,可以使用put、write函數及插入符“<<”。下面舉例來說明文件的操作過程和方法。

【例Ex_File】 將文件內容保存在另一個文件中,并將內容顯示在屏幕上

#include <iostream.h>
#include<fstream.h>                  // 文件操作所必需的頭文件
int  main()
{
    fstream  file1;                  // 定義一個fstream類的對象用于讀
    file1.open("Ex_DataFile.txt", ios::in);
    if (!file1)
    {
        cout<<"Ex_DataFile.txt不能打開!\n";    return;
    }
    fstream  file2;                  // 定義一個fstream類的對象用于寫
    file2.open("Ex_DataFileBak.txt", ios::out | ios::trunc);
    if (!file2)
    {
        cout<<"Ex_DataFileBak.txt不能創建!\n"; file1.close();   return;
    }
    char ch;
    while (!file1.eof())
    {
        file1.read(&ch,1);   cout<<ch;     file2.write(&ch,1);
    }
    file2.close();                    // 不要忘記文件使用結束后要及時關閉
    file1.close();
    return 0;
}

上述程序中,eof是ios類的成員函數,當到達文件的末尾時,它將返回true(真)。

3.隨機文件操作

隨機文件提供在文件中來回移動文件指針和非順序地讀寫文件的能力,這樣在讀寫磁盤文件某一數據以前無須讀寫其前面的數據,從而能快速地檢索、修改和刪除文件中的信息。

C++中順序文件和隨機文件間的差異不是物理的,這兩種文件都是以順序字符流的方式將信息寫在磁盤等存儲介質上,其區別僅在于文件的訪問和更新的方法。在以隨機的方式訪問文件時,文件中的信息在邏輯上組織成定長的記錄格式。所謂定長的記錄格式是指文件中的數據被解釋成C++的同一種類型的信息的集合,例如都是整型數或者都是用戶所定義的某一種結構的數據等。這樣就可以通過邏輯的方法,將文件指針直接移動到所讀寫數據的起始位置,來讀取數據或者將數據直接寫到文件的這個位置上。

在以隨機的方式讀寫文件時,同樣必須首先打開文件,且隨機方式和順序方式打開文件所用的函數也完全相同,但隨機方式的文件流的打開模式必須同時有ios::in|ios::out。

在文件打開時,文件指針指向文件的第一個字符(字節)。當然,可根據具體的讀寫操作使用C++提供的seekg和seekp函數將文件指針移動到指定的位置。它們的原型如下:

istream&seekg(long  pos);
istream&seekg(long  off,  ios::seek_dir  dir);
ostream&seekp(long  pos);
ostream&seekp(long  off,  ios::seek_dir  dir);

其中,pos用來指定文件指針的絕對位置。而off用來指定文件指針的相對偏移時,文件指針的最后位置還依靠dir的值。dir值可以是:

ios::beg       從文件流的頭部開始
ios::cur       從當前的文件指針位置開始
ios::end       從文件流的尾部開始

【例Ex_FileSeek】 使用seekp指定文件指針的位置

#include <iostream.h>
#include <iomanip.h>
#include <fstream.h>
#include <string.h>
class CStudent
{
public:
    CStudent(char* name, char* id, float score = 0);
    void print();
    friend ostream& operator<< ( ostream& os, CStudent& stu );
    friend istream& operator>> ( istream& is, CStudent& stu );
private:
    char strName[10];                // 姓名
    char strID[10];                  // 學號
    float fScore;                    // 成績
};
CStudent::CStudent(char* name, char* id, float score)
{
    strncpy(strName,name,10);      strncpy(strID,id,10);      fScore=score;
}
void CStudent::print()
{
    cout<<endl<<"學生信息如下:"<<endl;
    cout<<"姓名:"<<strName<<endl;
    cout<<"學號:"<<strID<<endl;
    cout<<"成績:"<<fScore<<endl;
}
ostream& operator<< ( ostream& os, CStudent& stu )
{
    os.write(stu.strName,10);      os.write(stu.strID,10);    os.write((char*)&stu.fScore,4);
    return os;
}
istream& operator>> ( istream& is, CStudent& stu )
{
    char name[10];
    char id[10];
    is.read(name,10);              is.read(id,10);            is.read((char*)&stu.fScore,4);
    strncpy(stu.strName, name, 10);
    strncpy(stu.strID, id, 10);
    return is;
}
int  main()
{
    CStudent stu1("MaWenTao","99001",88);
    CStudent stu2("LiMing","99002",92);
    CStudent stu3("WangFang","99003",89);
    CStudent stu4("YangYang","99004",90);
    CStudent stu5("DingNing","99005",80);
    fstream file1;
    file1.open("student.dat",ios::out|ios::in|ios::binary);
    file1<<stu1<<stu2<<stu3<<stu4<<stu5;
    CStudent* one = new CStudent("","");
    const int size = sizeof(CStudent);
    file1.seekp(size*4);       file1>>*one;       one->print();
    file1.seekp(size*1);       file1>>*one;       one->print();
    file1.seekp(size*2, ios::cur);
    file1>>*one;       one->print();
    file1.close();
    delete one;
    return 0;
}

程序運行結果如下:

學生信息如下:

姓名:DingNing

學號:99005

成績:80

學生信息如下:

姓名:LiMing

學號:99002

成績:92

學生信息如下:

姓名:DingNing

學號:99005

成績:80

程序先將五個學生記錄保存到文件中,然后移動文件指針,讀取相應的記錄,最后將數據輸出到屏幕上。需要說明的是,由于文件流file1既可以讀(ios::in)也可以寫(ios::out),因此用seekg代替程序中的seekp,其結果也是一樣的。

以上是C++的面向對象、輸入/輸出和模板的相關內容,但實際上由于Windows操作系統機制的引入,標準C++遠不能滿足Windows程序設計的需要。為此,Visual C++ 6.0針對其操作系統,提供了許多高效、實用的方法和技術,從下一章起將著重討論這方面的內容。

主站蜘蛛池模板: 淳化县| 钟山县| 丹巴县| 汕尾市| 临泽县| 历史| 满城县| 余姚市| 任丘市| 湘潭县| 即墨市| 漳州市| 永嘉县| 康保县| 临洮县| 信宜市| 黄冈市| 吉安县| 临城县| 达尔| 桐梓县| 黄浦区| 永昌县| 万源市| 祁东县| 武山县| 进贤县| 宁晋县| 东安县| 双辽市| 盐边县| 壶关县| 黄平县| 神木县| 桓仁| 阿克苏市| 龙游县| 喜德县| 高陵县| 华阴市| 城固县|