23IO流

IO流

1.流類庫結構

程序的輸入指的是從輸入文件將數據傳送給程序,程序的輸出指的是從程序將數據傳送給輸出文件。


C++輸入輸出包含以下三個方面的內容:

  • 對系統指定的標準設備的輸入和輸出。即從鍵盤輸入數據,輸出到顯示器屏幕。這種輸入輸出稱爲標準的輸入輸出,簡稱標準I/O。
  • 以外存磁盤文件爲對象進行輸入和輸出,即從磁盤文件輸入數據,數據輸出到磁盤文件。以外存文件爲對象的輸入輸出稱爲文件的輸入輸出,簡稱文件I/O。
  • 對內存中指定的空間進行輸入和輸出。通常指定一個字符數組作爲存儲空間(實際上可以利用該空間存儲任何信息)。這種輸入和輸出稱爲字符串輸入輸出,簡稱串I/O。

C++的輸入輸出對C語言的擴展和加強

在C++的輸入輸出中,編譯系統對數據類型進行嚴格的檢查,凡是類型不正確的數據都不可能通過編譯。因此C++的I/O操作是類型安全(type safe)的。C++的I/O操作是可擴展的,不僅可以用來輸入輸出標準類型的數據,也可以用於用戶自定義類型的數據。

C++通過I/O類庫來實現豐富的I/O功能。這樣使C++的輸人輸出明顯地優於C 語言中的printf和scanf,但是也爲之付出了代價,C++的I/O系統變得比較複雜,要掌握許多細節。

流類庫結構

ios是抽象基類,由它派生出istream類和ostream類,兩個類名中第1個字母i和o分別代表輸入(input)和輸出(output)。 istream類支持輸入操作,ostream類支持輸出操作, iostream類支持輸入輸出操作。iostream類是從istream類和ostream類通過多繼承而派生的類。其繼承層次見上圖表示。
C++對文件的輸入輸出需要用ifstreamofstream類,兩個類名中第1個字母i和o分別代表輸入和輸出,第2個字母f代表文件 (file)。ifstream支持對文件的輸入操作, ofstream支持對文件的輸出操作。類ifstream繼承了類istream,類ofstream繼承了類ostream,類fstream繼承了 類iostream。

有關頭文件

iostream類庫中不同的類的聲明被放在不同的頭文件中,用戶在自己的程序中用#include命令包含了有關的頭文件就相當於在本程序中聲明瞭所需 要用到的類。可以換 —種說法:頭文件是程序與類庫的接口,iostream類庫的接口分別由不同的頭文件來實現。常用的有

頭文件 含義
iostream 包含了對輸入輸出流進行操作所需的基本信息。
fstream 用於用戶管理的文件的I/O操作。
strstream 用於字符串流I/O。
stdiostream 用於混合使用C和C ++的I/O機制時,例如想將C程序轉變爲C++程序。
iomanip 在使用格式化I/O時應包含此頭文件。

常見流對象

對象 含義 對應設備 對應的類 c語言中相應的標準文件
cin 標準輸入流 鍵盤 istream_withassign stdin
cout 標準輸出流 屏幕 ostream_withassign stdout
cerr 標準錯誤流 屏幕 ostream_withassign stderr
clog 標準錯誤流 屏幕 ostream_withassign stderr

在iostream頭文件中定義以上4個流對象用以下的形式(以cout爲例):
ostream cout ( stdout);在定義coutostream流類對象時,把標準輸出設備stdout作爲參數,這樣它就與標準輸出設備(顯示器)聯繫起來,如果有cout <<3;就會在顯示器的屏幕上輸出3。

運算符重載

“<<”和“>>”本來在C++中是被定義爲左位移運算符和右位移運算符的,由於在iostream頭文件中對它們進行了重載, 使它們能用作標準類型數據的輸入和輸出運算符。所以,在用它們的程序中必須用#include命令把iostream包含到程序中。
1)>>a表示將數據放入a對象中。
2)<<a表示將a對象中存儲的數據拿出。

2.標準IO

cout流對象
cout是console output的縮寫,意爲在控制檯(終端顯示器)的輸出。強調幾點。
1) cout不是C++預定義的關鍵字,它是ostream流類的對象,在iostream中定義。 顧名思義,流是流動的數據,cout流是流向顯示器的數據。cout流中的數據是用流插入運算符“<<”順序加入的。cout流是容納數據的載體,它並不是一個運算符。人們關心的是cout流中的內容,也就是向顯示器輸出什麼。
2)用“cout<<”輸出基本類型的數據時,可以不必考慮數據是什麼類型,系統會判斷數據的類型,並根據其類型選擇調用與之匹配的運算符重 載函數。這個過程都是自動的,用戶不必干預。如果在C語言中用prinf函數輸出不同類型的數據,必須分別指定相應的輸出格式符,十分麻煩,而且容易出錯。C++的I/O機制對用戶來說,顯然是方便而安全的。
3) cout流在內存中對應開闢了一個緩衝區,用來存放流中的數據,當向cout流插人一個endl時,不論緩衝區是否已滿,都立即輸出流中所有數據,然後插入一個換行符並刷新流(清空緩衝區)。注意如果插人一個換行符”\n“(如cout<<a<<"\n"),則只輸出和換行,而不刷新cout流(但並不是所有編譯系統都體現出這一區別)。
4) 在iostream中只對”<<”和”>>”運算符用於標準類型數據的輸入輸出進行了重載,但未對用戶聲明的類型數據的輸入輸出 進行重載。如果用戶聲明瞭新的類型,並希望用”<<”和”>>”運算符對其進行輸入輸出,按照重運算符重載來做。

cerr流對象
cerr流對象是標準錯誤流,cerr流已被指定爲與顯示器關聯。cerr的 作用是向標準錯誤設備(standard error device)輸出有關出錯信息。cerr與標準輸出流cout的作用和用法差不多。但有一點不同:cout流通常是傳送到顯示器輸出,但也可以被重定向 輸出到磁盤文件,而cerr流中的信息只能在顯示器輸出。當調試程序時,往往不希望程序運行時的出錯信息被送到其他文件,而要求在顯示器上及時輸出,這時 應該用cerr。cerr流中的信息是用戶根據需要指定的。

注意:cerr不經過緩衝區

clog流對象

clog流對象也是標準錯誤流,它是console log的縮寫。它的作用和cerr相同,都是在終端顯示器上顯示出錯信息。區別:cerr是不經過緩衝區,直接向顯示器上輸出有關信息,而clog中的信息存放在緩衝區中,緩衝區滿後或遇endl時向顯示器輸出。

2.1標準輸入

標準輸入流對象cin,重點掌握的函數

cin.get() //一次只能讀取一個字符
cin.get(一個參數) //讀一個字符
cin.get(三個參數) //可以讀字符串
cin.getline()
cin.ignore()
cin.peek()
cin.putback()

輸入基本類型

#include <iostream>
using namespace std;

void main()
{
    char YourName[50];
    int myInt;
    long myLong;
    double myDouble;
    float myFloat;

    cout << "請輸入一個Int: ";
    cin >> myInt;
    cout << "請輸入一個Long: ";
    cin >> myLong;
    cout << "請輸入一個Float: ";
    cin >> myFloat;
    cout << "請輸入一個Double: ";
    cin >> myDouble;

    cout << "請輸入你的姓名: ";
    cin >> YourName;

    cout << "\n\n你輸入的數是:" << endl;
    cout << "Int: \t" << myInt << endl;
    cout << "Long: \t" << myLong << endl;
    cout << "Float: \t" << myFloat << endl;
    cout << "Double: \t" << myDouble << endl;
    cout << "姓名: \t" << YourName << endl;
    cout << endl << endl;
    system("pause");
    return;
}

ctr+z 會產生一個 EOF(-1)

#include <iostream>
using namespace std;

void main()
{
    char ch;
    while ((ch = cin.get()) != EOF)
    {
        std::cout << "字符: " << ch << std::endl;
    }
    std::cout << "\n結束.\n";

    return;
}

讀一個字符 鏈式編程

#include <iostream>
using namespace std;

void main()
{
    char a, b, c;
    cin.get(a);
    cin.get(b);
    cin.get(c);
    cout << a << b << c << endl;

    cout << "開始鏈式編程" << endl;
    cout.flush();

    cin.get(a).get(b).get(c);
    cout << a << b << c << endl;

    return;
}

cin.getline() 可以接受空格

#include <iostream>
using namespace std;

void main()
{
    char buf1[256];
    char buf2[256];
    cout << "\n請輸入你的字符串 不超過256";
    cin.getline(buf1, 256, '\n');
    cout << buf1 << endl;

    // 
    cout << "注意: cin.getline() 和 cin >> buf2 的區別, 能不能帶空格 " << endl;
    cin >> buf2; //流提取操作符 遇見空格 停止提取輸入流
    cout << buf2 << endl;

    system("pause");
    return;
}

緩衝區實驗

1 輸入 “aa bb cc dd” 字符串入緩衝區
2 通過 cin >> buf1; 提走了 aa
3 不需要輸入 可以再通過cin.getline() 把剩餘的緩衝區數據提走

#include <iostream>
using namespace std;

void main()
{
    char buf1[256];
    char buf2[256];

    cout << "請輸入帶有空格的字符串,測試緩衝區" << endl;
    cin >> buf1;
    cout << "buf1:" << buf1 << endl;

    cout << "請輸入數據..." << endl;

    //緩衝區沒有數據,就等待; 緩衝區如果有數據直接從緩衝區中拿走數據
    cin.getline(buf2, 256);
    cout << "buf2:" << buf2 << endl;

    system("pause");
    return;
}

ignore 和 peek

#include <iostream>
using namespace std;

void main()
{
    int  intchar;
    char buf1[256];
    char buf2[256];

    cout << "請輸入帶有空格的字符串,測試緩衝區 aa bb cc dd ee " << endl;
    cin >> buf1;
    cout << "buf1:" << buf1 << endl;

    cout << "請輸入數據..." << endl;
    cin.ignore(2);
    intchar = cin.peek();
    cout << "緩衝區若有數據,返回第一個數據的asc碼:" << intchar << endl;

    //緩衝區沒有數據,就等待; 緩衝區如果有數據直接從緩衝區中拿走數據
    cin.getline(buf2, 256);
    cout << "buf2:" << buf2 << endl;

    intchar = cin.peek(); //沒有緩衝區 默認是阻塞模式 
    cout << "緩衝區若有數據,返回第一個數據的asc碼:" << intchar << endl;

    return;
}

輸入的整數和字符串分開處理

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

void main()
{
    cout << "Please, enter a number or a word: ";
    char c = cin.get();

    if ((c >= '0') && (c <= '9')) //輸入的整數和字符串 分開處理
    {
        int n; //整數不可能 中間有空格 使用cin >>n
        cin.putback(c);
        cin >> n;
        cout << "You entered a number: " << n << '\n';
    }
    else
    {
        string str;
        cin.putback(c);
        getline(cin, str); // //字符串 中間可能有空格 使用 cin.getline();
        cout << "You entered a word: " << str << '\n';
    }   
    system("pause");


    return;
}

2.2標準輸出

標準輸出流對象cout

cout.flush()
cout.put()
cout.write()
cout.width()
cout.fill()
cout.setf(標記)

常見案例

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

void main()
{
    cout << "hello world" << endl;
    cout.put('h').put('e').put('l').put('\n');
    cout.write("hello world", 4); //輸出的長度

    char buf[] = "hello world";
    printf("\n");
    cout.write(buf, strlen(buf));

    printf("\n");
    cout.write(buf, strlen(buf) - 6);

    printf("\n");
    cout.write(buf, strlen(buf) + 6); //給的大於buf長度 不會幫我們檢查 提高速度

    printf("\n");
    system("pause");

    return;
}

使用cout.setf()控制符

#include <iostream>
#include <string>
#include <iomanip>
using namespace std;

void main()
{
    //使用類成員函數
    cout << "<start>";
    cout.width(30);
    cout.fill('*');
    cout.setf(ios::showbase); //#include <iomanip>
    cout.setf(ios::internal); //設置
    cout << hex << 123 << "<End>\n";

    cout << endl;
    cout << endl;
    //manipulator(操作符、控制符)

    //使用控制閥
    cout << "<Start>"
        << setw(30)
        << setfill('*')
        << setiosflags(ios::showbase) //基數
        << setiosflags(ios::internal)
        << hex
        << 123
        << "<End>\n"
        << endl;

    system("pause");

    return;
}
<start>0x**************************7b<End>


<Start>0x**************************7b<End>

在輸出數據時,爲簡便起見,往往不指定輸出的格式,由系統根據數據的類型採取默認的格式,但有時希望數據按指定的格式輸出,如要求以十六進制或八進制形式 輸出一個 整數,對輸出的小數只保留兩位小數等。有兩種方法可以達到此目的。
1)使用控制符的方法;
2)使用流對象的有關成員函數。分別敘述如下。

使用格式控制符

#include <iostream>
#include <string>
#include <iomanip>
using namespace std;

void main()
{
    int a;
    cout << "input a:";
    cin >> a;
    cout << "dec:" << dec << a << endl; //以十進制形式輸出整數
    cout << "hex:" << hex << a << endl; //以十六進制形式輸出整數a
    cout << "oct:" << setbase(8) << a << endl; //以八進制形式輸出整數a
    char *pt = "China"; //pt指向字符串"China"
    cout << setw(10) << pt << endl; //指定域寬爲,輸出字符串
    cout << setfill('*') << setw(10) << pt << endl; //指定域寬,輸出字符串,空白處以'*'填充
    double pi = 22.0 / 7.0; //計算pi值
    //按指數形式輸出,8位小數
    cout << setiosflags(ios::scientific) << setprecision(8);
    cout << "pi=" << pi << endl; //輸出pi值
    cout << "pi=" << setprecision(4) << pi << endl; //改爲位小數
    cout.unsetf(ios::scientific); //終止科學記數法狀態
    cout << "pi=" << setiosflags(ios::fixed)  << pi << endl; //改爲小數形式輸出


    system("pause");

    return;
}

人們在輸入輸出時有一些特殊的要求,如在輸出實數時規定字段寬度,只保留兩位小數,數據向左或向右對齊等。C++提供了在輸入輸出流中使用的控制符(有的書中稱爲操縱符)

輸出雙精度數

double a=123.456789012345;  // 對a賦初值

1) cout<<a;  輸出: 123.456
2) cout<<setprecision(9)<<a;  輸出: 123.456789
3) cout<<setprecision(6);  恢復默認格式(精度爲6)
4) cout<< setiosflags(ios∷fixed);  輸出: 123.456789
5) cout<<setiosflags(ios∷fixed)<<setprecision(8)<<a;  輸出: 123.45678901
6) cout<<setiosflags(ios∷scientific)<<a;  輸出: 1.234568e+02
7) cout<<setiosflags(ios∷scientific)<<setprecision(4)<<a;  輸出: 1.2346e02

整數輸出的例子

int b=123456;  // 對b賦初值
1) cout<<b;  輸出: 123456
2) cout<<hex<<b;   輸出: 1e240
3) cout<<setiosflags(ios∷uppercase)<<b;  輸出: 1E240
4) cout<<setw(10)<<b<<','<<b;   輸出:  123456123456
5) cout<<setfill('*')<<setw(10)<<b;  輸出: **** 123456
6) cout<<setiosflags(ios∷showpos)<<b;  輸出: +123456

如果在多個cout語句中使用相同的setw(n),並使用setiosflags(ios::right),可以實現各行數據右對齊,如果指定相同的精度,可以實現上下小數點對齊。

各行小數點對齊

int main( )
{
    double a=123.456,b=3.14159,c=-3214.67;
    cout<<setiosflags(ios::fixed)<<setiosflags(ios::right)<<setprecision(2);
    cout<<setw(10)<<a<<endl;
    cout<<setw(10)<<b<<endl;
    cout<<setw(10)<<c<<endl;
    system("pause");
    return 0;
}

輸出如下:

123.46 (字段寬度爲10,右對齊,取兩位小數)
3.14
-3214.67

先統一設置定點形式輸出、取兩位小數、右對齊。這些設置對其後的輸出均有效(除非重新設置),而setw只對其後一個輸出項有效,因此必須在輸出a,b,c之前都要寫setw(10)。

用流對象的成員函數控制輸出格式
除了可以用控制符來控制輸出格式外,還可以通過調用流對象cout中用於控制輸出格式的成員函數來控制輸出格式。用於控制輸出格式的常用的成員函數如下:

流成員函數setf和控制符setiosflags括號中的參數表示格式狀態,它是通過格式標誌來指定的。格式標誌在類ios中被定義爲枚舉值。因此在引用這些格式標誌時要在前面加上類名ios和域運算符“::”。格式標誌見表13.5。

簡單示例:

#include <iostream>
#include <string>
#include <iomanip>
using namespace std;

void main()
{ 
    int a = 21;
    cout.setf(ios::showbase);//顯示基數符號(0x或)
    cout << "dec:" << a << endl; //默認以十進制形式輸出a
    cout.unsetf(ios::dec); //終止十進制的格式設置
    cout.setf(ios::hex); //設置以十六進制輸出的狀態
    cout << "hex:" << a << endl; //以十六進制形式輸出a
    cout.unsetf(ios::hex); //終止十六進制的格式設置
    cout.setf(ios::oct); //設置以八進制輸出的狀態
    cout << "oct:" << a << endl; //以八進制形式輸出a
    cout.unsetf(ios::oct);
    char *pt = "China"; //pt指向字符串"China"
    cout.width(10); //指定域寬爲
    cout << pt << endl; //輸出字符串
    cout.width(10); //指定域寬爲
    cout.fill('*'); //指定空白處以'*'填充
    cout << pt << endl; //輸出字符串
    double pi = 22.0 / 7.0; //輸出pi值
    cout.setf(ios::scientific); //指定用科學記數法輸出
    cout << "pi="; //輸出"pi="
    cout.width(14); //指定域寬爲
    cout << pi << endl; //輸出pi值
    cout.unsetf(ios::scientific); //終止科學記數法狀態
    cout.setf(ios::fixed); //指定用定點形式輸出
    cout.width(12); //指定域寬爲
    cout.setf(ios::showpos); //正數輸出“+”號
    cout.setf(ios::internal); //數符出現在左側
    cout.precision(6); //保留位小數
    cout << pi << endl; //輸出pi,注意數符“+”的位置
    system("pause");
    return ;
}

1) 成員函數width(n)和控制符setw(n)只對其後的第一個輸出項有效。如:

cout. width(6);
cout <<20 <<3.14<<endl;

輸出結果爲 203.14.
在輸出第一個輸出項20時,域寬爲6,因此在20前面有4個空格,在輸出3.14時,width (6)已不起作用,此時按系統默認的域寬輸出(按數據實際長度輸出)。如果要求在輸出數據時都按指定的同一域寬n輸出,不能只調用一次width(n), 而必須在輸出每一項前都調用一次width(n>,上面的程序中就是這樣做的。


2) 在表13.5中的輸出格式狀態分爲5組,每一組中同時只能選用一種(例如dec、hex和oct中只能選一,它們是互相排斥的)。在用成員函數setf和 控制符setiosflags設置輸出格式狀態後,如果想改設置爲同組的另一狀態,應當調用成員函數unsetf(對應於成員函數setf)或 resetiosflags(對應於控制符setiosflags),先終止原來設置的狀態。然後再設置其他狀態,大家可以從本程序中看到這點。程序在開始雖然沒有用成員函數setf和控制符setiosflags設置用dec輸出格式狀態,但系統默認指定爲dec,因此要改變爲hex或oct,也應當先 用unsetf 函數終止原來設置。如果刪去程序中的第7行和第10行,雖然在第8行和第11行中用成員函數setf設置了hex和oct格式,由於未終止dec格式,因 此hex和oct的設置均不起作用,系統依然以十進制形式輸出。
同理,程序倒數第8行的unsetf 函數的調用也是不可缺少的。


3) 用setf 函數設置格式狀態時,可以包含兩個或多個格式標誌,由於這些格式標誌在ios類中被定義爲枚舉值,每一個格式標誌以一個二進位代表,因此可以用位或運算符“|”組合多個格式標誌。如倒數第5、第6行可以用下面一行代替:
cout.setf(ios::internal | ios::showpos); //包含兩個狀態標誌,用"|"組合

4) 可以看到:對輸出格式的控制,既可以用控制符(如例13.2),也可以用cout流的有關成員函數(如例13.3),二者的作用是相同的。控制符是在頭文件iomanip中定義的,因此用控制符時,必須包含iomanip頭文件。cout流的成員函數是在頭文件iostream 中定義的,因此只需包含頭文件iostream,不必包含iomanip。許多程序人員感到使用控制符方便簡單,可以在一個cout輸出語句中連續使用多種控制符。

3.文件IO

3.1文件流及其對象

輸入輸出是以系統指定的標準設備(輸入設備爲鍵盤,輸出設備爲顯示器)爲對象的。在實際應用中,常以磁盤文件作爲對象。即從磁盤文件讀取數據,將數據輸出到磁盤文件。
和文件有關係的輸入輸出類主要在fstream.h這個頭文件中被定義,在這個頭文件中主要被定義了三個類,由這三個類控制對文件的各種輸入輸出操 作,他們分別是ifstream、ofstream、fstream,其中fstream類是由iostream類派生而來,他們之間的繼承關係見下圖所 示。

由於文件設備並不像顯示器屏幕與鍵盤那樣是標準默認設備,所以它在fstream.h頭文件中是沒有像cout那樣預先定義的全局對象,所以我們必須自己定義一個該類的對象。
ifstream類,它是從istream類派生的,用來支持從磁盤文件的輸入。
ofstream類,它是從ostream類派生的,用來支持向磁盤文件的輸出。
fstream類,它是從iostream類派生的,用來支持對磁盤文件的輸入輸出。

3.2文件的基本操作

打開文件

所謂打開(open)文件是一種形象的說法,如同打開房門就可以進入房間活動一樣。 打開文件是指在文件讀寫之前做必要的準備工作,包括:

  1. 爲文件流對象和指定的磁盤文件建立關聯,以便使文件流流向指定的磁盤文件。
  2. 指定文件的工作方式,如,該文件是作爲輸入文件還是輸出文件,是ASCII文件還是二進制文件等。

以上工作可以通過兩種不同的方法實現。

1) 調用文件流的成員函數open。如

ofstream outfile;  //定義ofstream類(輸出文件流類)對象outfile
outfile.open("f1.dat",ios::out);  //使文件流與f1.dat文件建立關聯

第2行是調用輸出文件流的成員函數open打開磁盤文件f1.dat,並指定它爲輸出文件, 文件流對象outfile將向磁盤文件f1.dat輸出數據。ios::out是I/O模式的一種,表示以輸出方式打開一個文件。或者簡單地說,此時f1.dat是一個輸出文件,接收從內存輸出的數據。

調用成員函數open的一般形式爲:
文件流對象.open(磁盤文件名, 輸入輸出方式);
磁盤文件名可以包括路徑,如”c:/new/f1.dat”,如缺省路徑,則默認爲當前目錄下的文件。

2) 在定義文件流對象時指定參數
在聲明文件流類時定義了帶參數的構造函數,其中包含了打開磁盤文件的功能。因此,可以在定義文件流對象時指定參數,調用文件流類的構造函數來實現打開文件的功能。如
ostream outfile("f1.dat",ios::out); 一般多用此形式,比較方便。作用與open函數相同。
輸入輸出方式是在ios類中定義的,它們是枚舉常量,有多種選擇,見表13.6。

幾點說明:

1) 新版本的I/O類庫中不提供ios::nocreate和ios::noreplace。

2) 每一個打開的文件都有一個文件指針,該指針的初始位置由I/O方式指定,每次讀寫都從文件指針的當前位置開始。每讀入一個字節,指針就後移一個字節。當文 件指針移到最後,就會遇到文件結束EOF(文件結束符也佔一個字節,其值爲-1),此時流對象的成員函數eof的值爲非0值(一般設爲1),表示文件結束 了。

3) 可以用“位或”運算符“|”對輸入輸出方式進行組合,如表13.6中最後3行所示那樣。還可以舉出下面一些例子:
ios::in | ios:: noreplace //打開一個輸入文件,若文件不存在則返回打開失敗的信息
ios::app | ios::nocreate //打開一個輸出文件,在文件尾接着寫數據,若文件不存在,則返回打開失敗的信息
ios::out l ios::noreplace //打開一個新文件作爲輸出文件,如果文件已存在則返回打開失敗的信息
ios::in l ios::out I ios::binary //打開一個二進制文件,可讀可寫

但不能組合互相排斥的方式,如 ios::nocreate l ios::noreplace。

4) 如果打開操作失敗,open函數的返回值爲0(假),如果是用調用構造函數的方式打開文件的,則流對象的值爲0。可以據此測試打開是否成功。如

if(outfile.open("f1.bat", ios::app) ==0)
    cout <<"open error";

if( !outfile.open("f1.bat", ios::app) )
    cout <<"open error";

關閉文件

在對已打開的磁盤文件的讀寫操作完成後,應關閉該文件。關閉文件用成員函數close。如

outfile.close( );  //將輸出文件流所關聯的磁盤文件關閉

所謂關閉,實際上是解除該磁盤文件與文件流的關聯,原來設置的工作方式也失效,這樣,就不能再通過文件流對該文件進行輸入或輸出。此時可以將文件流與其他磁盤文件建立關聯,通過文件流對新的文件進行輸入或輸出。如
outfile.open(“f2.dat”,ios::app|ios::nocreate);
此時文件流outfile與f2.dat建立關聯,並指定了f2.dat的工作方式。

3.3對ASCII文件的操作

如果文件的每一個字節中均以ASCII代碼形式存放數據,即一個字節存放一個字符,這個文件就是ASCII文件(或稱字符文件)。程序可以從ASCII文件中讀入若干個字符,也可以向它輸出一些字符。
1) 用流插入運算符“<<”和流提取運算符“>>”輸入輸出標準類型的數據。“<<”和“ >>”都在iostream中被重載爲能用於ostream和istream類對象的標準類型的輸入輸出。由於ifstream和 ofstream分別是ostream和istream類的派生類;因此它們從ostream和istream類繼承了公用的重載函數,所以在對磁盤文件的操作中,可以通過文件流對象和流插入運算符“<<”及 流提取運算符“>>”實現對磁盤 文件的讀寫,如同用cin、cout和<<、>>對標準設備進行讀寫一樣。
2) 用文件流的put、get、geiline等成員函數進行字符的輸入輸出,:用C++流成員函數put輸出單個字符、C++ get()函數讀入一個字符和C++ getline()函數讀入一行字符。

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

void main()
{ 
    char fileName[80];
    char buffer[255];

    cout << "請輸入一個文件名: ";
    cin >> fileName;

    ofstream fout(fileName, ios::app);
    fout << "1111111111111111111\n";
    fout << "22222222222222222\n";
    cin.ignore(1,'\n');//在之前敲打文件名並輸入回車的時候回車字符留在了緩衝區
    cin.getline(buffer, 255); //從鍵盤輸入
    fout << buffer << "\n";
    fout.close();

    ifstream fin(fileName);
    cout << "Here's the the content of the file: \n";
    char ch;
    while (fin.get(ch))
        cout << ch;

    cout << "\n***End of file contents.***\n";
    fin.close();
    system("pause");
    return ;

}

由於fstream類可以對文件同時進行讀寫操作,所以對它的對象進行初始話的時候一定要顯式的指定mode和openprot參數。

3.4對二進制文件的操作

二進制文件不是以ASCII代碼存放數據的,它將內存中數據存儲形式不加轉換地傳送到磁盤文件,因此它又稱爲內存數據的映像文件。因爲文件中的信息不是字符數據,而是字節中的二進制形式的信息,因此它又稱爲字節文件
對二進制文件的操作也需要先打開文件,用完後要關閉文件。在打開時要用ios::binary指定爲以二進制形式傳送和存儲。二進制文件除了可以作爲輸入文件或輸出文件外,還可以是既能輸入又能輸出的文件。這是和ASCII文件不同的地方。

對二進制文件的讀寫主要用istream和ostream類的成員函數read和write來實現。這兩個成員函數的原型爲

istream& read(char *buffer,int len);
ostream& write(const char * buffer,int len);

字符指針buffer指向內存中一段存儲空間。len是讀寫的字節數。調用的方式爲:

a.write(p1,50);
b.read(p2,30);

上面第一行中的a是輸出文件流對象,write函數將字符指針p1所給出的地址開始的50個字節的內容不加轉換地寫到磁盤文件中。在第二行中,b是輸入文 件流對象,read 函數從b所關聯的磁盤文件中,讀入30個字節(或遇EOF結束),存放在字符指針p2所指的一段空間內。

#define _CRT_SECURE_NO_WARNINGS

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

class Teacher
{
public:

    Teacher(int age = 0,char *name = NULL)
    {
        int len = strlen(name);
        //m_name = new char[len+1];
        strncpy(m_name,name,len);
        m_name[len] = '\0';
        m_age = age;
    }
    Teacher(const Teacher & obj)
    {
        int len = strlen(obj.m_name);
        //m_name = new char[len + 1];
        strncpy(m_name, obj.m_name, len);
        m_name[len] = '\0';
        m_age = obj.m_age;
    }
    ~Teacher()
    {

    }
    void printT()
    {
        cout << "老師的年齡:" << m_age << endl << "老師的姓名:" << m_age << endl;
    }
protected:
private:
    int m_age;
    char m_name[64];
};

int main()
{ 
    char fileName[255] = "teacher123.dat";
    ofstream fout(fileName, ios::binary);
    if (!fout)
    {
        cout << "Unable to open " << fileName << " for writing.\n";
        fout.close();
        return(1);
    }

    Teacher t1(31, "31");
    Teacher t2(32, "32");
    fout.write((char *)&t1, sizeof Teacher);
    fout.write((char *)&t2, sizeof Teacher);
    fout.close();

    cout << "保存對象到二進制文件裏成功!" << endl;

    ifstream fin(fileName, ios::binary);
    if (!fin)
    {
        cout << "Unable to open " << fileName << " for reading.\n";
        return (1);
    }
    Teacher tmp(100, "100");

    fin.read((char *)&tmp, sizeof Teacher);
    tmp.printT();

    fin.read((char *)&tmp, sizeof Teacher);
    tmp.printT();

    fin.close();
    system("pause");
    return 1;
}

編程實現以下數據輸入/輸出:
(1)以左對齊方式輸出整數,域寬爲12。
(2)以八進制、十進制、十六進制輸入/輸出整數。
(3)實現浮點數的指數格式和定點格式的輸入/輸出,並指定精度。
(4)把字符串讀入字符型數組變量中,從鍵盤輸入,要求輸入串的空格也全部讀入,以回車符結束。
(5)將以上要求用流成員函數和操作符各做一遍。

成員函數方式

#define _CRT_SECURE_NO_WARNINGS

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

/*編程實現以下數據輸入/輸出:
    (1)以左對齊方式輸出整數,域寬爲12。
    (2)以八進制、十進制、十六進制輸入/輸出整數。
    (3)實現浮點數的指數格式和定點格式的輸入/輸出,並指定精度。
    (4)把字符串讀入字符型數組變量中,從鍵盤輸入,要求輸入串的空格也全部讀入,以回車符結束。
    (5)將以上要求用流成員函數和操作符各做一遍。
*/

int main()
{ 

    int a = 10;
    cout.fill('*');
    cout.flags(ios_base::left);
    cout.width(12);
    cout << a << endl;

    cout.fill('*');
    cout.flags(ios::right);
    cout.width(12);
    cout << a << endl;

    cout.flags(ios::hex);
    cout << "十六進制:"<<a << endl;

    cout.flags(ios::dec);
    cout << "十進制:"<<a << endl;

    cout.flags(ios::oct);
    cout << "八進制:"<<a << endl;


    double f = 123.45678901234;

    cout.flags(ios::scientific);
    cout.precision(4);
    cout << "指數形式:" << f << endl;

    cout.flags(ios::fixed);
    cout.precision(4);
    cout << "定點形式:" << f << endl;

    char str[128];
    cin.get(str, 99);
    cout << "輸入的數據是:"<<str << endl;


    system("pause");
    return 1;
}

格式控制符方式

#define _CRT_SECURE_NO_WARNINGS

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

/*編程實現以下數據輸入/輸出:
    (1)以左對齊方式輸出整數,域寬爲12。
    (2)以八進制、十進制、十六進制輸入/輸出整數。
    (3)實現浮點數的指數格式和定點格式的輸入/輸出,並指定精度。
    (4)把字符串讀入字符型數組變量中,從鍵盤輸入,要求輸入串的空格也全部讀入,以回車符結束。
    (5)將以上要求用流成員函數和操作符各做一遍。
*/

int main()
{ 

    int a = 10;
    /*cout.fill('*');
    cout.flags(ios_base::left);
    cout.width(12);
    cout << a << endl;*/
    cout << setfill('*') << setiosflags(ios::left) << setw(12) << a << endl;

    /*cout.fill('*');
    cout.flags(ios::right);
    cout.width(12);
    cout << a << endl;*/
    cout << setfill('*') << setiosflags(ios::right) << setw(12) << a << endl;

    /*cout.flags(ios::hex);
    cout << "十六進制:"<<a << endl;*/
    cout << "十六進制:" << hex << a << endl;

    /*cout.flags(ios::dec);
    cout << "十進制:"<<a << endl;*/
    cout << "十進制:" <<dec<< a << endl;

    /*cout.flags(ios::oct);
    cout << "八進制:"<<a << endl;*/
    cout << "八進制:" <<oct<< a << endl;


    double f = 123.45678901234;

    /*cout.flags(ios::scientific);
    cout.precision(4);
    cout << "指數形式:" << f << endl;*/
    cout << "指數形式:" << setiosflags(ios::scientific) << setprecision(4) << f << endl;


    /*cout.flags(ios::fixed);
    cout.precision(4);
    cout << "定點形式:" << f << endl;*/
    //resetiosflags(ios::scientific);必須寫在cout的鏈式編程流中,單獨寫成語句並沒有實際效果。
    cout << "定點形式:" << resetiosflags(ios::scientific)<< setiosflags(ios::fixed) << setprecision(4) << dec << f << endl;


    char str[128];
    cin.get(str, 99);
    cout << "輸入的數據是:"<<str << endl;

    system("pause");
    return 1;
}

2編寫一程序,將兩個文件合併成一個文件。

#define _CRT_SECURE_NO_WARNINGS

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


int main()
{ 
    char ifile1_name[16] = "1.cpp";
    char ifile2_name[16] = "2.cpp";
    char ofile_name[16] = "3.cpp";

    char tmp_buf[1024];

    ifstream ifile1(ifile1_name);
    ifstream ifile2(ifile2_name);

    ofstream ofile(ofile_name);

    while (!ifile1.eof())
    {
        ifile1.getline(tmp_buf, 1023);
        ofile << tmp_buf << endl;
    }

    while (!ifile2.eof())
    {
        ifile2.getline(tmp_buf, 1023);
        ofile << tmp_buf << endl;
    }

    ifile1.close();
    ifile2.close();
    ofile.close();

    system("pause");
    return 1;
}

3編寫一程序,統計一篇英文文章中單詞的個數與行數。

#define _CRT_SECURE_NO_WARNINGS

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

bool is_alph(char c)
{
    return ((c >= 'a'&&c <= 'z') || (c >= 'A'&&c <= 'Z'));
}

int main()
{ 
    char ifile_name[16] = "1.cpp";
    char tmp_buf[1024];
    int words_cnt = 0;
    int rows_cnt = 0;
    bool inword = false;

    ifstream ifile(ifile_name);

    int  i = 0;
    while (!ifile.eof())
    {
        ifile.getline(tmp_buf, 1023);
        rows_cnt++;
        i = 0;
        while (tmp_buf[i] != '\0')
        {
            if (!is_alph(tmp_buf[i]))//遇到非字母字符標記該字符不屬於單詞部分
            {
                inword = false;
            }
            else if (is_alph(tmp_buf[i])&&inword == false)//如果當前字符屬於字母,且前一個字符不屬於字母,表示有新單詞產生,單詞數加一,並把當前字母標記爲屬於單詞的部分
            {
                words_cnt++;
                inword = true;
            }
            i++;
        }
    }

    cout << "rows= " << rows_cnt << endl;
    cout << "words= " << words_cnt << endl;
    ifile.close();

    system("pause");
    return 1;
}

4編寫一程序,將C++源程序每行前加上行號與一個空格。

#define _CRT_SECURE_NO_WARNINGS

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

bool is_alph(char c)
{
    return ((c >= 'a'&&c <= 'z') || (c >= 'A'&&c <= 'Z'));
}

int main()
{ 
    char ifile_name[16] = "1.cpp";
    char ofile_name[16] = "4.cpp";
    char tmp_buf[1024];


    ifstream ifile(ifile_name);
    ofstream ofile(ofile_name);

    int  i = 0;
    while (!ifile.eof())
    {
        ofile << ++i << ":";
        ifile.getline(tmp_buf, 1023);
        ofile << tmp_buf << endl;   
    }
    ifile.close();
    ofile.close();

    system("pause");
    return 1;
}

5編寫一程序,輸出 ASCII碼值從20到127的ASCII碼字符表,格式爲每行10個。

#define _CRT_SECURE_NO_WARNINGS

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

bool is_alph(char c)
{
    return ((c >= 'a'&&c <= 'z') || (c >= 'A'&&c <= 'Z'));
}

int main()
{ 
    int i = 20;
    int cnt = 0;
    for (i = 20; i <= 127; i++)
    {
        cout << static_cast<char>(i) << "\t";
        cnt++;
        if (cnt % 10 == 0)
        {
            cout << endl;
        }
    }
    cout << endl;
    system("pause");
    return 1;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章