
一、標準輸入輸出流
流的概念:若干個字節(jié)組成的一個字節(jié)序列,代表信息從源到目的的流動
頭文件 iostream
從標準輸入讀取流 cin >> //把空格、換行作為分隔符(不讀空格)
從標準輸出寫入流 cout <<
1.1提取符>>(賦值給)與插入符<<(輸出到)
首先,我們的這個分標題是插入符 << , 這樣一看大家是不是都懵了:cin>>a,為什么不是 >> 這個符號是插入符呢?這個不應該是寫是插入嗎?
請聽我娓娓道來:我們將cin理解成寫 是從用戶的角度理解 因為我們一寫 cin 我們就會調(diào)用控制臺console從黑框中寫入東西 所以我們覺得 >> 這個是寫 但是cin >> a 的本質(zhì)是:(注:cin在這里先當作ifstream )我們先從黑框中寫入,黑框的數(shù)據(jù)拷貝給了ifsream流文件:test.txt 接著我們把 test.txt 的文件內(nèi)容 >> 內(nèi)存變量,即流向了變量a。總的來說:對于用戶調(diào)用consolea來說:是寫;但是對于文件test.txt流向內(nèi)存變量:是讀(是test.txt賦值給變量a),我們考慮的正是后者?。▓D片里面的instream寫錯了,應該是ifstream)
理解cin >> a
粗暴點講:我們的cin就是調(diào)用鍵盤然后寫入,但是我們的操作符 >> 這個可是提取,提取我們鍵盤輸入的信息
cin >> a :1.調(diào)用鍵盤 2.賦值
理解ifstream(讀) >> a
把ifstream>>a理解成一個過程 而且這個過程正是cin>>a的第二個過程,我們探討的數(shù)據(jù)的流向和鍵盤與顯示器無關,而是探討數(shù)據(jù)內(nèi)存之間的流向
總的來說:
- 運算符<<常用 做輸入輸出流的插入符,表明“輸出到”,例如 cout<<“Hello”,是把字符串“Hello”輸出到屏 幕上
- 運算符>>常用做提取符,表明“賦值給”,例如:cin>>i,是把鍵盤輸入的信息賦值給i
例子
//應用
class Stu{
string name;
int score;
public:
Stu(string n="",int s=0){name=n,score=s;} //構造
//小羊謹記:寫這倆個運算符重載的時候 一定得在返回值和倆個參數(shù)都加上引用!
friend istream& operator>>(istream& in,Stu& s);
//聲名友元函數(shù) 要加關鍵字friend friend使得外部函數(shù)可以訪問
friend ostream& operator<<(ostream& out,Stu& s);
};
istream& operator>>(istream& in,Stu& s)
{
in >> s.name >> s.score;
return in;
}
ostream& operator<<(ostream& out,Stu& s)
{
out << s.name << " " << s.score;
return out;
}
void test3(){
Stu s;
cin>>s; //但是cin不理解怎么讀取Stu型,所以要對cin>>進行重載(詳情見上面)
cout<<s;
}
1.2get系列函數(shù)
-
get() 函數(shù):
istream& get (char& c);
-
get
是istream
類的成員函數(shù),用于從輸入流中獲取單個字符。 -
get
只獲取一個字符,并且不包括換行符(‘\n’)在內(nèi),它不會將換行符留在輸入流中。 -
get
函數(shù)通常用于從流中獲取字符,而不是整行文本。 - 語法示例:
cin.get(character);
-
-
getline() 函數(shù):
-
getline
也是istream
類的成員函數(shù),用于從輸入流中獲取一整行文本。 -
getline
獲取整行文本,包括換行符,然后將整行文本存儲在字符串中。 -
getline
可以指定一個定界符(默認為換行符’\n’),以指示何時停止讀取。 - 語法示例:
cin.getline(str, size);
-
聯(lián)系和建議使用情況:
- 如果你只需要獲取單個字符或者有特定需求,那么使用
get
是更合適的選擇。 - 如果你需要獲取整行文本,通常用于讀取用戶輸入或從文本文件中讀取一行數(shù)據(jù),那么使用
getline
更為方便,因為它會一次性獲取整行,包括換行符,不需要擔心換行符的處理。
get與getline函數(shù)細小但又重要的區(qū)別
當遇到輸人流中的界定符(delim, 即結束字符)時,get()停止執(zhí)行,但是并不從輸入流中提取界定符,直接在字符串緩沖區(qū)尾部加結束標志“\0”,從而把界定符放在緩沖區(qū);函數(shù) getline()則相反,它將從輸入流中提取界定符,但不會把它存儲到結果緩沖區(qū)中。
我們先驗證get函數(shù)最后是\0:
void test()
{
unsigned char buf1[5];
cout << "請輸入abcde" << endl;
cin.get((char*)buf1, 5);
cout << "get() read: " << buf1 << endl;
}
因為存放的是一個字符串,因此在4個字符之后要加入一個字符串結束標志,實際上存放到數(shù)組中的是5個字符(最后一個是斜杠0)
關于定界符緩沖區(qū)的問題,我們發(fā)現(xiàn):get寫在getline上面,那么回車會被getline接收,導致getline無法進行
但是如果getline寫在get上面,會發(fā)現(xiàn)回車這個定界符被getline接收了,而不會影響到get
getline在上面:
void test()
{
unsigned char buf1[10];
unsigned char buf2[10];
cin.getline((char*)buf2, 10);
cout << "getline() read: " << buf2 << endl;
cin.get((char*)buf1, 10);
cout << "get() read: " << buf1 << endl;
}
get在上面:
void test()
{
unsigned char buf1[10];
unsigned char buf2[10];
cin.get((char*)buf1, 10);
cout << "get() read: " << buf1 << endl;
cin.getline((char*)buf2, 10);
cout << "getline() read: " << buf2 << endl;
}
1.3獲取狀態(tài)信息函數(shù)(處理流錯誤)
獲取狀態(tài)信息的函數(shù)如下:
int rdstate(): 無參數(shù),返回值即是狀態(tài)信息特征值。
- 0:正確狀態(tài) good()
- 1:系統(tǒng)錯誤 bad()
- 2:非法數(shù)據(jù)讀入 fail()
- 4:到達文件結束,流已經(jīng)讀完 eof()
為什么沒有3咧,是因為:在狀態(tài)函數(shù)中,二進制里面只可以有一個1,所以就導致了數(shù)字3的不存在
000-0
001-1
010-2
100-4
使用下面函數(shù)來檢測相應輸入輸出狀態(tài):
- bool good(): 若返回值 true, 一切正常,沒有錯誤發(fā)生。
- bool bad(): 發(fā)生了(或許是物理上的)致命性錯誤,流將不能繼續(xù)使用。
- bool fail(): 若返回值 true,表明I/O 操作失敗,主要原因是非法數(shù)據(jù)(例如讀取數(shù)字時遇到字母)。但流可以繼續(xù)使用。
- bool eof(): 若返回值 true, 表明已到達流的末尾。
要想重置以上成員函數(shù)所檢查的狀態(tài)標志,你可以使用成員函數(shù)clear(),沒有參數(shù)。
課本上的例子,例4.4用的是cin.rdstate(),為了避免cin.good()等操作是cin專屬這個問題的爭議,我這里先不用標準輸出輸出流,我用istream的另一個文件流來重新命名一個參數(shù)進行操作(通過替換txt中文件的值,來達到不同的效果)
例子:檢測輸入輸出狀態(tài)
void test4_4()
{
int a;
ifstream file("D:\\vs code_code\\Teacher_DiXW\\a.txt");
file >> a;
cout<<"狀態(tài)值為:"<<file.rdstate()<<endl;
if(file.good())
{
cout<<" 輸入數(shù)據(jù)的類型正確,無錯誤!"<<endl;
}
if(file.fail())
{
cout<<" 輸入數(shù)據(jù)類型錯誤,非致命錯誤,可清除輸入緩沖區(qū)挽回!"<<endl;
}
}
接著我們將txt文件從10替換成a,我們繼續(xù)看看結果:
例子:確保一個整型數(shù)給變量a
void test4_5()
{
char BUFF[60];
int a;
while(1)
{
cin>>a;
if(cin.fail())
{
cout<<"輸入有錯!請重新輸入"<<endl;
cin.clear(); //清空狀態(tài)表示位
cin.get();
//cin.getline(BUFF,60); //清空流緩沖區(qū)
}
else
{
cout<<a<<endl;
break;
}
}
}
優(yōu)化:將get換成getline,就不用有那么多報錯信息了,只顯示一次提示信息
cin.get() --> cin.getline(BUFF,60);
二、文件輸入輸出流
2.1文件打開
#include <fstream>
fstream
ofstream ifstream
在fstream類中,成員函數(shù)open()實現(xiàn)打開文件的操作,從而將數(shù)據(jù)流和文件進行關聯(lián),通過ofstream,ifstream,fstream對象進行對文件的讀寫操作
函數(shù):open()
void open (const char* filename, ios_base::openmode mode = ios_base::in);
參數(shù): filename 操作文件名
? openmode 打開文件的方式
打開文件的方式在ios類(所以流式I/O的基類)中定義,有如下幾種方式:
ios::in | 為輸入(讀)而打開文件 |
ios::out | 為輸出(寫)而打開文件 |
ios::ate | 初始位置:文件尾 |
ios::app | 所有輸出附加在文件末尾 |
ios::trunc | 如果文件已存在則先清空該文件,若文件不存在則創(chuàng)建文件 |
ios::binary | 二進制方式打開,不做字符轉(zhuǎn)換(別用string 有大bug) |
These flags can be combined with the bitwise OR operator (|
)
這些方式是能夠進行組合使用的,以“或”運算(“|”)的方式:例如
ofstream out;
out.open("Hello.txt", ios::in|ios::out|ios::binary)
特別強調(diào)以下內(nèi)容:
很多程序中,可能會碰到ofstream out(“Hello.txt”), ifstream in(“…”),fstream file(“…”)這樣的的使用,并沒有顯式的去調(diào)用open()函數(shù)就進行文件的操作,直接調(diào)用了其默認的打開方式,因為在stream類的構造函數(shù)中調(diào)用了open()函數(shù),并擁有同樣的構造函數(shù),所以在這里可以直接使用流對象進行文件的操作,默認方式如下:
ofstream out("...", ios::out);ifstream in("...", ios::in);fstream file("...", ios::in|ios::out);
當使用默認方式進行對文件的操作時,你可以使用成員函數(shù)is_open()對文件是否打開進行驗證
2.2關閉文件
當文件讀寫操作完成之后,我們必須將文件關閉以使文件重新變?yōu)榭稍L問的。成員函數(shù)close(),它負責將緩存中的數(shù)據(jù)排放出來并關閉文件。這個函數(shù)一旦被調(diào)用,原先的流對象就可以被用來打開其它的文件了,這個文件也就可以重新被其它的進程所訪問了。為防止流對象被銷毀時還聯(lián)系著打開的文件,析構函數(shù)將會自動調(diào)用關閉函數(shù)close。
2.3文本文件的讀寫
類ofstream, ifstream 和fstream 是分別從ostream, istream 和iostream 中引申而來的。這就是為什么 fstream 的對象可以使用其父類的成員來訪問數(shù)據(jù)。
一般來說,我們將使用這些類與同控制臺(console)交互同樣的成員函數(shù)(cin 和 cout)來進行輸入輸出。如下面的例題所示,我們使用重載的插入操作符<<(下面這段代碼是寫文件):
#include<iostream>
#include<fstream>
using namespace std;
int main ()
{
ofstream out("D:\\vs code_code\\Teacher_DiXW\\lzy.txt");
if (out)
{
out << "This is a line.\n";
out << "This is another line.\n";
}
out.close();
return 0;
}
測試結果:
從文件中讀入數(shù)據(jù)也可以用與 cin>> 的使用同樣的方法(下面這段代碼是讀文件):
#include<iostream>
#include<fstream>
using namespace std;
int main ()
{
char buffer[256];
ifstream in("D:\\vs code_code\\Teacher_DiXW\\lzy.txt");
if (in)
{
while(!in.eof())//確保訪問到文件末尾
{
in.getline(buffer,100);//in >> buffer ;
cout << buffer << endl;//可以看出 想用ifstream讀文件 還是得借助 cout
}
}
return 0;
}
別看小小一段代碼,東西可多著呢:首先,我們文件內(nèi)部的內(nèi)容是倆行,這就表明如果我們不用eof判斷的話,我們讀完第一行就結束了;其次,如果我們不用getline,而是用 in >> buffer 那么我們遇到空格就夭折了;最后,我們想要真正的看到結果,還得需要利用cout,這是為了顯示在顯示器上,文件本身已經(jīng)有內(nèi)容了
上面的例子讀入一個文本文件的內(nèi)容,然后將它打印到屏幕上。注意我們使用了一個新的成員函數(shù)叫做 eof ,它是ifstream 從類 ios 中繼承過來的,當?shù)竭_文件末尾時返回true 。
例子:寫文本文件,把學生信息保存到文件當中
#include<fstream>
#include<iostream>
using namespace std;
struct STUDENT
{
char strName[20];
int nGrade;
};
int main()
{
ofstream out;
out.open("D:\\vs code_code\\Teacher_DiXW\\lzy.txt");
STUDENT st1={"張三",90};
STUDENT st2={"李四",80};
out << st1.strName << "\t" << st1.nGrade << endl;
out << st2.strName << "\t" << st2.nGrade << endl;
out.close();
return 0;
}
例子:讀文本文件并顯示在屏幕上
#include<fstream>
#include<iostream>
using namespace std;
int main()
{
char szBuf[80];
ifstream in("D:\\vs code_code\\Teacher_DiXW\\lzy.txt");
if(in)
{
while(in.getline(szBuf,80))
{
cout<<szBuf<<endl;
}
}
in.close();
return 0;
}
很明顯,這個利用getline當作while循環(huán),比上面的eof使用起來更加優(yōu)秀,而且為什么getline這么寫呢?
cin.getline(buff,size);這個的意思就是讀取我們鍵盤給cin的值
那么in.getline(buff,size);這個意思正是將txt文件當中的內(nèi)容利用getline流向buff
2.4二進制文件
在二進制文件中,使用<< 和>>,以及函數(shù)(如getline)來操作符輸入和輸出數(shù)據(jù),沒有什么實際意義,雖然它們是符合語法的。
文件流包括兩個為順序讀寫數(shù)據(jù)特殊設計的成員函數(shù):write 和 read。第一個函數(shù) (write) 是ostream 的一個成員函數(shù),都是被ofstream所繼承。而read 是istream 的一個成員函數(shù),被ifstream 所繼承。類 fstream 的對象同時擁有這兩個函數(shù)。它們的原型是:
write ( char* buffer, streamsize size );
read ( char* buffer, streamsize size );
這里 buffer 是一塊內(nèi)存的地址,用來存儲或讀出數(shù)據(jù)。參數(shù)size 是一個整數(shù)值,表示要從緩存(buffer)中讀出或?qū)懭氲淖址麛?shù)。
例子:寫二進制文件
void test_binary_write()
{
struct Worker w[3]={"zhang",2500,20,"lisi",4500,22,"wang",2500,23};
ofstream out("D:\\vs code_code\\Teacher_DiXW\\lzy.txt",ios::binary);//后綴表明是二進制文件
for(int i=0;i<3;i++)
{
//out << w[i]; 這么寫就成了標準輸入輸出流了
out.write((char *) &w[i],sizeof(Worker)); //(要寫的數(shù)據(jù)首地址,要寫數(shù)據(jù)的大小)
}
out.close();
}
out.write() 函數(shù)用于將指定的二進制數(shù)據(jù)寫入到文件流中。第一個參數(shù)是指向要寫入的數(shù)據(jù)的指針,該指針通常需要是 char* 類型,因為 write() 函數(shù)按字節(jié)處理數(shù)據(jù),而 char 類型正好是一個字節(jié)。在這里,將 &w[i] 強制轉(zhuǎn)換為 char* 類型,這樣就可以將 Worker 類型的數(shù)據(jù)按字節(jié)寫入到文件流中。把0X62fd60等變成一個字節(jié)的,才可以給write使用!!
例子:讀二進制文件
void test_binary_read()
{
//讀文件
ifstream in("D:\\vs code_code\\Teacher_DiXW\\lzy.txt",ios::binary);
Worker temp;
for(int i=0;i<3;i++)
{
in.read((char*)&temp,sizeof(Worker));
cout << temp.name <<" "<<temp.wage <<" "<<temp.age <<endl;//放在顯示器上
}
in.close();
}
將in中的數(shù)據(jù)內(nèi)存拷貝給temp,并且每拷貝一個輸出一次,循環(huán)三次就可以得到我們之前寫入文件的全部內(nèi)容!
二進制文件和普通文件的區(qū)別
文本文件與二進制文件的區(qū)別_二進制文件和文本文件的區(qū)別-CSDN博客
可以看看這篇博客,講的比較清楚
2.5尋找輸入輸出流緩沖(這樣就可以不用給緩沖區(qū)流向數(shù)據(jù)了)
C++ 標準庫封裝了一個緩沖區(qū)類 streambuf,用于暫存數(shù)據(jù)。當數(shù)據(jù)被寫入或讀取時,它們會先被存儲在緩沖區(qū)中,然后再被寫出或讀取到底層設備(如控制臺、磁盤文件等)或程序中的變量中。
每個標準 C++ 輸入輸出流對象都包含一個指向streambuf 的指針,用戶可以通過調(diào)用rdbuf() 成員函數(shù)獲得該指針,從而直接訪問底層streambuf 對象;可以直接對底層緩沖區(qū)進行數(shù)據(jù)讀寫,從而跳過上層的格式化輸入輸出操作。但由于類似的功能均可由上層緩沖區(qū)類實現(xiàn),因此就不再加以論述了。
streambuf 最精彩的部分在于它重載了 operator<< 及 operator>>。這個緩存(buffer)實際是一塊內(nèi)存空間,作為流(stream)和物理文件的媒介。例如,對于一個輸出流,每次成員函數(shù)put(寫一個單個字符)被調(diào)用,這個字符不是直接被寫入該輸出流所對應的物理文件中的,而是首先被插入到該流的緩存(buffer)中。
對 operator<<來說,它以 streambuf 指針為參數(shù),實現(xiàn)把 streambuf對象中的所有字符輸出到輸出流中;對 operator>>來說,可把輸入流對象中的所有字符輸入到 streambuf 對象中。
換句話說,對于輸出而言:我們可以不需要定義變量從而將內(nèi)容流向?qū)ο笤龠M行輸出,我們可以直接輸出文件內(nèi)容
void test_read()
{
ifstream in("D:\\vs code_code\\Teacher_DiXW\\lzy.txt");
if(in)
{
cout << in.rdbuf() < <endl;
}
in.close();
}
2.6定位輸入輸出流
倆個定位函數(shù):
istream&seekg(long relativepos,ios::seek_dir dir)
針對輸入流。第一個參數(shù)是要移動的字符數(shù)目,可正可負;第二個參數(shù)是移動方向,是 ios::begin、ios::cur、ios::end 中的一個值。含義是:字符指針相對于移動方向向前或向后 移動了多少個字符。(如果用beg和end的話,第一個參數(shù)一般給0就好了)
ostream&seekp(long relativepos,ios::seek_dir dir)
針對輸出流。含義同上
流的三個位置標識符:
ios::beg | 流開始位置 |
ios::cur | 流指針當前位置 |
ios::end | 流結束位置 |
例子:先寫文件再讀文件(用rdbuf讀)
#include<fstream>
#include<iostream>
using namespace std;
int main()
{
string a("hello");
fstream in_out;
in_out.open("D:\\vs code_code\\Teacher_DiXW\\lzy.txt",ios::in | ios::out | ios::trunc);//trunc保證了沒有該文件,則自動創(chuàng)建該文件
in_out << a;
//in_out.write("Hello",5);
in_out.seekg(0,ios::beg); //讀指針移到文件頭
cout<<in_out.rdbuf();
in_out.close();
return 0;
}
代碼分析:
- 正常來說,我們讀文件即使用rdbuf讀,也得起碼用ifstream創(chuàng)建對象,但是如果我們掌握了更改文件指針的定位方法,我們甚至可以寫完直接用seekg修改位置輸出
- 采用了 fstream 輸入輸出流類,既可讀又可寫
- 注意open 打開標志 ios::in | ios!:out | ios::trunc, 特別是ios::trunc, 它保證了若沒有該文件,則自動創(chuàng)建該文件。
- 文件打開或創(chuàng)建成功后,文件指針均指向文件頭,當寫完字符串“Hello” 后,文件指針已經(jīng)偏移了,若想完全顯示全部文件內(nèi)容,必須把指針移到文件頭。文中用到了 seekg 函 數(shù),其實用 seekp 函數(shù)也是等效的,這是因為fstream 是輸入輸出流。若是單獨的輸入流,則 只能用seekg 函數(shù);若是單獨的輸出流,則只能用 seekp 函數(shù)。
- 所以在這個代碼中一定不可以在寫完后就關閉文件,那么就無法修改文件指針了
- 同時我們也認識到為什么讀文件可以讀到全部內(nèi)容,正是因為文件打開或創(chuàng)建成功后,指向文件頭,這個知識點是我之前不懂的
我們還可以將seekg用cur重新寫:
in_out.seekg(-5,ios::cur); //讀指針移到文件頭
效果出來是如出一轍的~
三、字符串輸入輸出流(理解成動態(tài)字符串)
字符串輸入輸出流類直接對內(nèi)存而不是對文件和標準輸出進行操作,它使用與 cin 及 cout 相同的讀取和格式化函數(shù)來操縱內(nèi)存中的數(shù)據(jù),所有字符串流類的聲明都包含在標準頭文件 中
- istringstream: 字符串輸入流,提供讀 string 功能。
- ostringstream: 字符串輸出流,提供寫 string 功能。
- stringstream: 字符串輸入輸出流,提供讀寫 string 功能。
利用字符串輸入輸出流,可以方便地把多種基本數(shù)據(jù)類型組合成字符串,也可以反解字符串給各種變量賦值。
3.1例子:反解字符串給各變量賦值
void test()
{
int n;
float f;
string str;
string strText="13.14 hello";
istringstream s(strText);
s >> n;//n是int類型
s >> f;//f是float類型
s >> str;//str是字符串類型
cout << "n=" << n << endl;
cout << "f=" << f << endl;
cout << "str=" << str << endl;
}
代碼分析:
通過字符串輸入流 istringstream 讀取字符串“13.14 hello”給了s,接著依次賦給整型、浮點型、字符串變量
3.2例子:合并不同類型的數(shù)據(jù)到字符串
void test()
{
cout<<"輸入一個int一個float一個string:";
int i;
float f;
string stuff;
cin >> i;//輸入整形
cin >> f;//輸入float形
getline(cin,stuff);//輸入字符串形
ostringstream os;
os << "integer=" << i << endl;
os << "float=" << f << endl;
os << "string=" << stuff << endl;
string result=os.str();
cout << result << endl;
}
代碼分析:
首先分別輸入三個變量,并且定義一個 ostringstream 字符串流插入流 使得數(shù)據(jù)全部流向os 接著利用這倆段代碼:
string result=os.str();
cout << result << endl;
很明顯,字符串流的讀和文件是不同的
代碼測試結果:
3.3例子:字符切割(類3.1)
void test()
{
//2.用的多的情況 是用來做類型轉(zhuǎn)換 或者 字符切割
//to_string 數(shù)字轉(zhuǎn)換成字符串
//cout << to_string(1234) << endl;//此時1234是字符串
istringstream ip("192.168.1.1");//讀取
//要求:拆分ip地址每個數(shù)字
int ipNum[4];//由于這個是int類型的 所以我們在輸入的時候無法輸入.字符
for(int i=0;i<4;++i)
{
char get_char;
ip >> ipNum[i];//ip >> ipnum[1] >> ipnum[2] >> ipnum[3] >> ipnum[4];
if(i < 3)//一共三個點
{
ip >> get_char;//獲取字符
}
}
for(int i=0;i<4;++i)
{
cout << ipNum[i] << " ";
}cout << endl;
}
代碼分析:
int ipNum[4];//由于這個是int類型的 所以我們在輸入的時候無法輸入.字符
if(i < 3)//一共三個點
? {
? ip >> get_char;//獲取字符
? }
如果不接收的話,就會使得.無法接收,無法繼續(xù)進行讀取
3.4例子:使用stringstream進行類型轉(zhuǎn)換
void test()
{
stringstream data("");
int num=12345;
data << num;//把數(shù)據(jù)放到流里面 num 給了 data
char result[20]="";
data >> result;//data給了result
cout << result;//輸出result
}
代碼分析:
我們將int類型的 num 流向data,接著定義一個 result 字符數(shù)組,我們把data流向字符數(shù)組,即可進行類型轉(zhuǎn)換
3.5綜合例子:讀入每個學生的各科成績,并顯示學生各科成績及總成績
#include<iostream>
#include<fstream>
#include<sstream>
#include<string>
using namespace std;
int main()
{
ifstream in("D:\\vs code_code\\Teacher_DiXW\\lzy.txt");//文件以讀方式打開
if(in)
{
cout << "打開成功" << endl;
}
string strText;
string strName;//學生姓名
int nYuwen;//語文成績
int nMath;//數(shù)學成績
int nForeign;//外語成績
int nTotal;//總成績
while(!in.eof())//while(getline(in,strText)) 這樣可以不寫eof
{
getline(in,strText);//讀每行文本數(shù)據(jù)保存至字符串
istringstream inf(strText);//把該文本串封裝成字符串輸入流對象
inf >> strName >> nYuwen >> nMath >> nForeign;//通過字符串輸入流對象
nTotal=nYuwen+nMath+nForeign;
cout<<strName<<"\t"<<nYuwen<<"\t"<<nMath<<"\t"<<nForeign<<"\t"<< nTotal<<endl;//給姓名及各成績賦值
}
in.close();
return 0;
}
代碼分析:
首先用 open 函數(shù)以讀方式打開該文件,然后用 getline 函數(shù)一行行讀,并且把每行內(nèi)容存至strText,并把該靜態(tài)的字符串封裝成動態(tài)的字符串輸入流對象,之后以流的方式給學生姓名及各科成績賦值,算出總成績,輸出至屏幕
cout << strText;
雖然我們這么寫也可以得到結果,但是有一個差異就是這樣不會給變量賦值,而且我們的字符串還是靜態(tài)的
方法二:利用文件流+重載輸入輸出流
#include<iostream>
#include<fstream>
#include<sstream>
#include<string>
using namespace std;
class Student
{
public:
string strName;//姓名
int nYuwen;//語文
int nMath;//數(shù)學
int nForeign;//外語
};
istream& operator>>(istream& is,Student& s)
{
is >> s.strName >> s.nYuwen >> s.nMath >> s.nForeign;
return is;
}
ostream& operator<<(ostream& os,Student& s)
{
int nTotal=s.nYuwen+s.nMath+s.nForeign;
os << s.strName << "\t" << s.nYuwen << "\t" << s.nMath << "\t" << s.nForeign <<"\t"<<nTotal<<"\n";
return os;
}
int main()
{
ifstream in("D:\\vs code_code\\Teacher_DiXW\\lzy.txt");
Student s;
while(!in.eof())
{
in >> s;
cout << s;
}
in.close();
return 0;
}
代碼分析:
從面向?qū)ο蠼嵌葋砜?,方?更容易擴展和維護,我們將in賦值給s的時候,由于已經(jīng)重載,所以會用operator >> ;我們將s的數(shù)據(jù)輸出到cout的時候,由于已經(jīng)重載,所以會用到 operator << !
四、總結
答案一:哪些流
標準輸入輸出流、文件輸入輸出流、字符串輸入輸出流
答案二:stringstream與其他的區(qū)別
stringstream
是 C++ 標準庫中的一個類,它是 istringstream
和 ostringstream
的基類。stringstream
類提供了對字符串的輸入和輸出操作,可以將字符串作為流來處理。
與其他流(ifstream
、ofstream
、istringstream
和 ostringstream
)相比,stringstream
最大的區(qū)別在于它可以同時進行輸入和輸出操作。也就是說,可以在同一個 stringstream
對象上進行讀取和寫入操作。
下面是 stringstream
與其他流的一些不同之處:
-
輸入和輸出:
stringstream
可以像其他流一樣執(zhí)行輸入和輸出操作。你可以使用<<
運算符將數(shù)據(jù)寫入stringstream
,也可以使用>>
運算符從stringstream
中讀取數(shù)據(jù)。 -
字符串處理:
stringstream
主要用于對字符串進行處理。你可以將一個字符串傳遞給stringstream
構造函數(shù),也可以通過str()
方法獲取或設置當前的字符串內(nèi)容。stringstream (const string& str,ios_base::openmode which = ios_base::in | ios_base::out);
str()的返回值:一個字符串對象,包含流緩沖區(qū)中當前內(nèi)容的副本
-
數(shù)據(jù)格式轉(zhuǎn)換:
stringstream
提供了方便的數(shù)據(jù)格式轉(zhuǎn)換功能。你可以使用<<
運算符將不同類型的數(shù)據(jù)插入到stringstream
中,然后使用>>
運算符將其提取出來。這對于將數(shù)值類型轉(zhuǎn)換為字符串、字符串解析為數(shù)值類型等操作非常有用。 -
可變性:
stringstream
允許你在同一個對象上進行讀寫操作,而不需要創(chuàng)建額外的輸入流或輸出流對象。
總結來說,stringstream
是一個方便的工具,用于在內(nèi)存中對字符串進行輸入和輸出操作,并提供了數(shù)據(jù)格式轉(zhuǎn)換的功能。與其他流相比,它提供了更靈活的讀寫能力。
答案三:stringstream初始化
-
使用默認構造函數(shù)初始化:
std::stringstream ss; // 默認構造函數(shù)創(chuàng)建一個空的 stringstream 對象
-
使用字符串初始化:
std::string str = "Hello, World!"; std::stringstream ss(str); // 使用字符串來初始化 stringstream 對象
-
使用字符數(shù)組(C 風格字符串)初始化:
const char* cstr = "Hello, World!"; std::stringstream ss(cstr); // 使用字符數(shù)組來初始化 stringstream 對象
-
使用其他 stringstream 對象初始化:
std::stringstream src_ss("Hello, World!"); std::stringstream dest_ss(src_ss.str()); // 使用另一個 stringstream 對象的內(nèi)容來初始化
需要注意的是,初始化后的 stringstream
對象可以進行讀取和寫入操作,可以使用 <<
運算符向其中插入數(shù)據(jù),也可以使用 >>
運算符從其中提取數(shù)據(jù)。
以下是一個完整的示例,演示了不同的初始化方式和讀寫操作:
#include <sstream>
int main() {
// 初始化 stringstream 對象
std::stringstream ss("Hello, World!");
// 寫入數(shù)據(jù)
ss << " This is a stringstream.";
// 讀取數(shù)據(jù)并輸出
std::string output;
ss >> output;
std::cout << output << std::endl;
return 0;
}
以上示例將輸出:Hello,
,它先將 "Hello, World! This is a stringstream."
寫入 stringstream
對象,然后使用 >>
運算符提取出第一個字符串 "Hello,"
并輸出。
通過這些初始化方法,你可以根據(jù)需要來創(chuàng)建和初始化 stringstream
對象,并在其上執(zhí)行相應的讀寫操作。
答案四:文件流關聯(lián)文件
-
使用構造函數(shù)關聯(lián)文件:
std::ifstream inputFile("input.txt"); // 以輸入模式打開名為 "input.txt" 的文件 std::ofstream outputFile("output.txt"); // 以輸出模式打開名為 "output.txt" 的文件
-
使用成員函數(shù)
open()
關聯(lián)文件:std::ifstream inputFile; inputFile.open("input.txt"); // 以輸入模式打開名為 "input.txt" 的文件 std::ofstream outputFile; outputFile.open("output.txt"); // 以輸出模式打開名為 "output.txt" 的文件
以上代碼示例分別使用 ifstream
和 ofstream
類來關聯(lián)文件。ifstream
是用于讀取文件的輸入文件流類,而 ofstream
是用于寫入文件的輸出文件流類。
在關聯(lián)文件后,你可以通過文件流對象執(zhí)行相應的讀取和寫入操作。例如,對于 ifstream
,你可以使用 >>
運算符從文件中讀取數(shù)據(jù);對于 ofstream
,你可以使用 <<
運算符將數(shù)據(jù)寫入文件。
答案五:文件流指定文件打開方式
-
std::ios::in
:以輸入模式打開文件,用于讀取文件。(ifstream默認的打開方式) -
std::ios::out
:以輸出模式打開文件,用于寫入文件。(ofstream默認的打開方式) -
std::ios::app
:在文件末尾追加數(shù)據(jù)。 -
std::ios::binary
:以二進制模式打開文件。 -
std::ios::ate
:在打開文件時將文件指針移至文件末尾。 -
std::ios::trunc
:如果文件已存在則先清空該文件,若文件不存在則創(chuàng)建文件
可以使用按位或運算符 |
來組合不同的打開模式。例如:
std::ofstream outputFile("output.txt", std::ios::out | std::ios::app); // 以輸出和追加模式打開文件
outputFile.open("output.txt", std::ios::out | std::ios::app); // 使用 open() 成員函數(shù)以輸出和追加模式打開文件
答案六:控制流的游標位置
看上述文章2.6
答案七:流的標志位及如何檢測
看上述文章1.3
答案八:文件和字符串流讀寫數(shù)據(jù)
-
文件流讀入和寫出數(shù)據(jù):
-
讀入數(shù)據(jù):使用文件流類(如
ifstream
)可以從文件中讀取數(shù)據(jù)。#include <fstream> std::ifstream inputFile("input.txt"); // 打開要讀取的文件 int value; if (inputFile >> value) { // 讀取成功,可以使用讀取的值進行后續(xù)操作 } else { // 讀取失敗,處理錯誤情況 }
在上述代碼中,我們使用
>>
運算符從文件中讀取數(shù)據(jù),并將其存儲在變量value
中。如果讀取成功,則條件為真,可以使用讀取的值進行后續(xù)操作。如果讀取失?。ɡ缬龅椒欠ㄗ址蛞堰_到文件末尾),則條件為假,可以處理錯誤情況。 -
寫出數(shù)據(jù):使用文件流類(如
ofstream
)可以向文件中寫出數(shù)據(jù)。#include <fstream> std::ofstream outputFile("output.txt"); // 打開要寫入的文件 int value = 42; if (outputFile << value) { // 寫入成功 } else { // 寫入失敗,處理錯誤情況 }
在上述代碼中,我們使用
<<
運算符將數(shù)據(jù)寫入文件。如果寫入成功,則條件為真,可以繼續(xù)執(zhí)行后續(xù)操作。如果寫入失?。ɡ缬龅藉e誤或無法寫入),則條件為假,可以處理錯誤情況。
-
-
字符串流讀入和寫出數(shù)據(jù):
-
讀入數(shù)據(jù):使用字符串流類
std::stringstream
可以從字符串中讀取數(shù)據(jù)。#include <sstream> #include <string> std::string data = "42"; std::stringstream ss(data); // 將字符串作為輸入源 int value; if (ss >> value) { // 讀取成功,可以使用讀取的值進行后續(xù)操作 } else { // 讀取失敗,處理錯誤情況 }
在上述代碼中,我們將字符串
data
作為輸入源創(chuàng)建了一個字符串流ss
。然后,我們使用>>
運算符從字符串流中讀取數(shù)據(jù),并將其存儲在變量value
中。讀取成功與否的判斷和處理錯誤的方式與文件流類相似。 -
寫出數(shù)據(jù):使用字符串流類
std::stringstream
可以將數(shù)據(jù)寫入到字符串中。#include <sstream> #include <string> std::stringstream ss; int value = 42; if (ss << value) { std::string output = ss.str(); // 將寫入的數(shù)據(jù)轉(zhuǎn)換為字符串 // 可以使用字符串進行后續(xù)操作 } else { // 寫入失敗,處理錯誤情況 }
在上述代碼中,我們創(chuàng)建了一個空的字符串流
ss
,然后使用<<
運算符將數(shù)據(jù)寫入字符串流中。如果寫入成功,則可以通過str()
函數(shù)獲取字符串流中的內(nèi)容,并將其存儲在變量output
中,以便進行后續(xù)操作。
-
通過文件流和字符串流,你可以方便地進行讀取和寫入操作,無論是從文件還是從字符串中。
答案九:read和write操作
見上述文章2.4
答案十:stringstream的類型轉(zhuǎn)換
見3.4例子
答案十一(對我來說很重要,因為之前用string的時候沒有傳大?。?/h3>
-
char*
緩沖區(qū):
char buffer[100];
cin.getline(buffer, 100);
當使用 char*
緩沖區(qū)時,你需要提供一個字符數(shù)組作為緩沖區(qū),同時還需要指定緩沖區(qū)的大小。getline
函數(shù)將讀取的數(shù)據(jù)存儲到指定的字符數(shù)組中。需要注意的是,getline
函數(shù)會自動在讀取數(shù)據(jù)時添加空字符 \0
作為字符串的結尾標記。
區(qū)別:
-
char*
緩沖區(qū)需要手動指定緩沖區(qū)的大小,因此需要確保緩沖區(qū)足夠大以容納讀取的數(shù)據(jù)。
-
char*
緩沖區(qū)需要提前定義,長度固定,無法動態(tài)調(diào)整。
-
string
對象:
string buffer;
getline(cin, buffer);//看了博客,發(fā)現(xiàn)當初也是這么寫的,這個對我來說很不熟練,得多記
當使用 string
對象時,你只需要創(chuàng)建一個空的 string
對象作為緩沖區(qū)即可。getline
函數(shù)將讀取的數(shù)據(jù)存儲到這個 string
對象中。
區(qū)別:
-
string
對象不需要手動指定緩沖區(qū)的大小,它可以根據(jù)讀取的數(shù)據(jù)自動調(diào)整大小。
-
string
對象可以方便地進行字符串操作,如獲取字符串長度、連接字符串等。
char*
緩沖區(qū):
char buffer[100];
cin.getline(buffer, 100);
當使用 char*
緩沖區(qū)時,你需要提供一個字符數(shù)組作為緩沖區(qū),同時還需要指定緩沖區(qū)的大小。getline
函數(shù)將讀取的數(shù)據(jù)存儲到指定的字符數(shù)組中。需要注意的是,getline
函數(shù)會自動在讀取數(shù)據(jù)時添加空字符 \0
作為字符串的結尾標記。
區(qū)別:
-
char*
緩沖區(qū)需要手動指定緩沖區(qū)的大小,因此需要確保緩沖區(qū)足夠大以容納讀取的數(shù)據(jù)。 -
char*
緩沖區(qū)需要提前定義,長度固定,無法動態(tài)調(diào)整。
string
對象:
string buffer;
getline(cin, buffer);//看了博客,發(fā)現(xiàn)當初也是這么寫的,這個對我來說很不熟練,得多記
當使用 string
對象時,你只需要創(chuàng)建一個空的 string
對象作為緩沖區(qū)即可。getline
函數(shù)將讀取的數(shù)據(jù)存儲到這個 string
對象中。
區(qū)別:
-
string
對象不需要手動指定緩沖區(qū)的大小,它可以根據(jù)讀取的數(shù)據(jù)自動調(diào)整大小。 -
string
對象可以方便地進行字符串操作,如獲取字符串長度、連接字符串等。
無論使用 char*
緩沖區(qū)還是 string
對象作為 getline
函數(shù)的參數(shù),其功能和用法是一樣的。選擇使用哪種類型的緩沖區(qū)取決于你的具體需求和編程習慣。如果你需要更靈活和方便的字符串操作,建議使用 string
對象;如果你已經(jīng)有一個固定大小的字符數(shù)組,并且不需要頻繁改變緩沖區(qū)的大小,那么可以使用 char*
緩沖區(qū)。
答案十二:class私有怎么重載
用友元
//小羊謹記:寫這倆個運算符重載的時候 一定得在返回值和倆個參數(shù)都加上引用!
friend istream& operator>>(istream& in,Stu& s);
//聲名友元函數(shù) 要加關鍵字friend friend使得外部函數(shù)可以訪問
friend ostream& operator<<(ostream& out,Stu& s);
答案十三:如何訪問流的緩沖區(qū)
C++ 中的流對象(如 cin
、cout
等)都有自己的緩沖區(qū),用于暫存數(shù)據(jù)。當數(shù)據(jù)被寫入或讀取時,它們會先被存儲在緩沖區(qū)中,然后再被寫出或讀取到底層設備(如控制臺、磁盤文件等)或程序中的變量中。
如果你需要訪問流中的緩沖區(qū),可以使用流對象的 rdbuf()
函數(shù)來獲取指向緩沖區(qū)的指針。rdbuf()
函數(shù)返回一個指向 streambuf
對象的指針,streambuf
類是 C++ 中處理輸入輸出緩沖區(qū)的基類。通過這個指針,你可以直接訪問緩沖區(qū),例如獲取緩沖區(qū)中的數(shù)據(jù)、向緩沖區(qū)中添加數(shù)據(jù)等。文章來源:http://www.zghlxwxcb.cn/news/detail-759296.html
見上述文章2.5文章來源地址http://www.zghlxwxcb.cn/news/detail-759296.html
到了這里,關于【C++】萬字詳解IO流(輸入輸出流+文件流+字符串流)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!