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++對文件的輸入輸出需要用ifstream和ofstream類,兩個類名中第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);
在定義cout
爲ostream
流類對象時,把標準輸出設備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; 輸出: 123456,123456
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)文件是一種形象的說法,如同打開房門就可以進入房間活動一樣。 打開文件是指在文件讀寫之前做必要的準備工作,包括:
- 爲文件流對象和指定的磁盤文件建立關聯,以便使文件流流向指定的磁盤文件。
- 指定文件的工作方式,如,該文件是作爲輸入文件還是輸出文件,是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;
}