C語言中我們用到的最頻繁的輸入輸出方式就是scanf ()與printf()。 scanf(): 從標準輸入設(shè)備(鍵盤)讀取數(shù)據(jù),并將值存放在變量中。printf(): 將指定的文字/字符串輸出到標準輸出設(shè)備(屏幕)。
注意寬度輸出和精度輸出控制。C語言借助了相應(yīng)的緩沖區(qū)來進行輸入與輸出。如下圖所示:
這里的設(shè)備除了終端或者是控制臺之外,還可能是磁盤文件,或者是網(wǎng)卡。
對輸入輸出緩沖區(qū)的理解:
1.可以屏蔽掉低級I/O的實現(xiàn),低級I/O的實現(xiàn)依賴操作系統(tǒng)本身內(nèi)核的實現(xiàn),所以如果能夠屏
蔽這部分的差異,可以很容易寫出可移植的程序。
2.可以使用這部分的內(nèi)容實現(xiàn)“行”讀取的行為,對于計算機而言是沒有“行”這個概念,有了這部分,就可以定義“行”的概念,然后解析緩沖區(qū)的內(nèi)容,返回一個“行”。
printf/scanf
fprintf/fscanf
sprintf/sscanf
int main()
{int a=0;
printf("%d",a);
scanf("%d",&a);
printf("%d\n",a);
}
二、C++中的IO流
istream:流提取
ostream:流輸出
因為提取和輸出都要寫一個類,非常麻煩,就寫了一個特iostream,繼承了這兩個類的功能
(但是因為這里存在菱形繼承,所以非常復(fù)雜)
int main()
{int i=0;
double j=2.2;
cout<
這是因為庫底層都對其進行了函數(shù)的重載。
istream和ostream可以更好地支撐自定義類型對象的流插入和流提取。
自定義類型可以自己重載,控制流提取和流插入的方式。
(string,日期類都可以進行流插入和流提取的重載)
#includeint main()
{int year ,month ,day;
//輸入多個值默認輸入是用空格或者換行分隔開來的。
cin>>year>>month>>day;
scanf("%d%d%d",&year,&month,&day);
scanf("%d %d %d",&year,&month,&day);//不需要去加空格
//如果日期是20221128這樣輸入的
//我們可以用這種方式將我們的日期進行分隔
scanf("%4d%2d%2d",&year,&month,&day);
cout<>str;
year=stoi(str.substr(0,4));
month=stoi(str.substr(4,2));
day=stoi(str.substr(6,2));
cout<
多行測試用例如何寫輸入int main()
{int year ,month ,day;
string str;
//使用了operate bool
//怎么終止?ctrl+c直接kill -9終止進程。
//ctr+z給一個流結(jié)束的標志。
while(cin>>str)
{year=stoi(str.substr(0,4));
month=stoi(str.substr(4,2));
day=stoi(str.substr(6,2));
cout<
返回值是cin類型的對象。
一般是整型指針,比較運算符表達式道德結(jié)果可以做邏輯條件判斷,0就是假,非0就是真,自定義類型一般是不能做while的條件類型判斷的,這里存在隱式類型轉(zhuǎn)換
想要讓自定義類型能夠進行轉(zhuǎn)換,我們需要將其進行強制類型轉(zhuǎn)換,但是一般用于類型轉(zhuǎn)換的()被占用了,所以我們就用operator bool,將我們的類型轉(zhuǎn)化成一個bool值,并將這個bool值進行返回。
這就類似于我們下面的將我們的自定義類型a賦值給內(nèi)置類型int的過程。
class A
{public:
A(int a)
:_a(a)
{}
private:
int _a;
};
int main()
{//內(nèi)置類型轉(zhuǎn)換成自定義類型
//隱式類型轉(zhuǎn)換
A aa1=1;//用1構(gòu)造A的臨時對象,再拷貝構(gòu)造aa1,優(yōu)化后直接構(gòu)造aa1
}
如何讓我們的自定義類型轉(zhuǎn)換成內(nèi)置類型
class A
{public:
A(int a)
:_a(a)
{}
//重載運算符
operator int()
{return _a;
}
private:
int _a;
};
int main()
{//自定義類型轉(zhuǎn)換成內(nèi)置類型1
int i=aa1;
cout<
三、文件流
ifstream讀取文件int main()
{ifstream ifs("/Users/Test.cpp");
//讀取一個字符
char ch=ifs.get();
while(ifs)
{cout<
讀取文件中不同類型的數(shù)據(jù)這是我們test.txt中的內(nèi)容
可以更好地兼容自定義類型的讀寫
class Date
{friend ostream& operator<< (ostream& out, const Date& d);
friend istream& operator >>(istream& in, Date& d);
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{}
operator bool()
{// 這里是隨意寫的,假設(shè)輸入_year為0,則結(jié)束
if (_year == 0)
return false;
else
return true;
}
private:
int _year;
int _month;
int _day;
};
istream& operator >>(istream& in, Date& d)
{in >>d._year >>d._month >>d._day;
return in;
}
ostream& operator<< (ostream& out, const Date& d)
{out<< d._year<< " "<< d._month<<" "<< d._day ;
return out;
}
int main()
{ifstream ifs("/Users/test.txt");
//可以使用c語言的方式從文件中讀取不同類型的數(shù)據(jù)
// fscanf("%d%s%f",)
//在c++中使用ifstream會方便一些
int i;
string s;
double d;
//對于自定義類型也是可以的
//當(dāng)然,可以的前提是日期類對象重載了流提取
Date de;
//流提取
ifs>>i>>s>>d>>de;
cout<
二進制讀寫和文本讀寫二進制讀寫:就是文件在內(nèi)存中是如何存儲的,就怎樣寫到磁盤文件中
文本讀寫:序列化(json,xml),將對象數(shù)據(jù)按照某種格式進行轉(zhuǎn)換,序列化成字符串,寫入磁盤當(dāng)中。讀取回來的也是字符串,反序列化,再轉(zhuǎn)換成對象數(shù)據(jù)。
二進制文件的寫出二進制讀寫:
優(yōu)點:快
缺點:寫出去的內(nèi)容看不見
(打開就大概率是亂碼了,比方說存了一個100,但是二進制讀寫出來是100的補碼)
文本讀寫:
優(yōu)點:可以看見寫出去的是什么
缺點:存在一個轉(zhuǎn)換的過程,要慢一些
#include#includeusing namespace std;
struct ServerInfo
{char _address[32];
int _port;
};
struct ConfigureManager
{public:
ConfigureManager(const char* filename="./server.config")
:_filename(filename)
{}
void WriteBin (const ServerInfo& info)
{//默認的寫都是覆蓋的模式進行寫入
//但是app就是追加寫,也就是append
ofstream ofs(_filename,ios_base::out|ios_base::binary);
//需要傳入一個char*的字符串,然后第二個參數(shù)是要讀取的數(shù)據(jù)的大小
ofs.write((char*)&info,sizeof(info));
}
private:
string _filename;
};
int main()
{ServerInfo info={"127.0.0.1",888};
ConfigureManager cm;
cm.WriteBin(info);
return 0;
}
這里我們的文本是不支持直接查看二進制文件的,所以我們這里的除了數(shù)字,別的我們文本的編碼都是不可見的。
#include#includeusing namespace std;
struct ServerInfo
{char _address[32];
int _port;
};
struct ConfigureManager
{public:
ConfigureManager(const char* filename="./server.config")
:_filename(filename)
{}
void ReadBin (const ServerInfo& info)
{//默認的寫都是覆蓋的模式進行寫入
//但是app就是追加寫,也就是append
ifstream ifs(_filename,ios_base::in|ios_base::binary);
//需要傳入一個char*的字符串,然后第二個參數(shù)是要讀取的數(shù)據(jù)的大小
ifs.read((char*)&info,sizeof(info));
}
private:
string _filename;
};
int main()
{//二進制的讀取
ServerInfo rinfo;
ConfigureManager cm;
cm.ReadBin(rinfo);
cout<
二進制讀寫的坑我們將這里的char*的address改成string,并且將這里的address的地址改成我們博客的連接
#include#includeusing namespace std;
struct ServerInfo
{//char _address[32];
string _address;
int _port;
};
struct ConfigureManager
{public:
ConfigureManager(const char* filename="./server.config")
:_filename(filename)
{}
void WriteBin (const ServerInfo& info)
{//默認的寫都是覆蓋的模式進行寫入
//但是app就是追加寫,也就是append
ofstream ofs(_filename,ios_base::out|ios_base::binary);
//需要傳入一個char*的字符串,然后第二個參數(shù)是要讀取的數(shù)據(jù)的大小
ofs.write((char*)&info,sizeof(info));
}
void ReadBin (const ServerInfo& info)
{//默認的寫都是覆蓋的模式進行寫入
//但是app就是追加寫,也就是append
ifstream ifs(_filename,ios_base::in|ios_base::binary);
//需要傳入一個char*的字符串,然后第二個參數(shù)是要讀取的數(shù)據(jù)的大小
ifs.read((char*)&info,sizeof(info));
}
private:
string _filename;
};
int main()
{//二進制寫出去
// ServerInfo info={"https://editor.csdn.net/md/?articleId=128086656",888};
// ConfigureManager cm;
// cm.WriteBin(info);
//二進制的讀取
ServerInfo rinfo;
ConfigureManager cm;
cm.ReadBin(rinfo);
cout<
我們先將數(shù)據(jù)寫出去,然后重新讀取進來。
(如果我們的address比較短的話,是不會報下列的錯誤的,但是如果address比較長的話,就會報下面的錯誤)
在windows下的string,短的時候存的就·直接用共這里的_buff[16]將其進行存儲,如果這里的字符串太長了,就用string中的_ptr指向這個string的地址。
_buff[16]
_ptr
_size
_capacity
_port
ServerInfo info={"https://editor.csdn.net/md/?articleId=128086656",888};
字符串是存在我們的堆上面的,我們將我們的字符串寫出去,也就是調(diào)用write寫出去的時候,調(diào)用的是這個字符串的地址。這里我們的是分兩次運行的,也就是我們寫二進制文件的時候,這個字符串的地址。當(dāng)我們的讀的進程結(jié)束之后,我們再運行讀取的進程,我們將這個指針讀取回來的時候,這個指針已經(jīng)不是我們剛剛那個string的指針了!它已經(jīng)是一個野指針了?。?/p>
文本的方式寫和讀取所以二進制讀寫的時候,一定要用數(shù)組,char[],不要使用string!!
#include#includeusing namespace std;
struct ServerInfo
{string _address;
int _port;
};
struct ConfigureManager
{public:
ConfigureManager(const char* filename="./server.config")
:_filename(filename)
{}
void WriteText(const ServerInfo& info)
{//這里我們將指定的文件進行寫的操作
ofstream ofs(_filename, ios_base::out);
//我們需要傳入一個寫入的子串和這個字符串的大小
ofs.write(info._address.c_str(), info._address.size());
//然后我們可以用換行將我們的字符串和后面的內(nèi)容進行分隔
//當(dāng)然這里傳入一個換行符的話,我們可以使用上面的write,或者是下面的這種簡單形式的put
ofs.put('\n');
//將我們的_port,(原本是整型)轉(zhuǎn)換成字符串的類型
const string str = to_string(info._port);
//然后將我們的_port的字符串輸出,第一個人參數(shù)是字符串,第二個參數(shù)是字符串的大小
ofs.write(str.c_str(), str.size());
}
void ReadText(ServerInfo& info)
{//讀取指定的文件
ifstream ifs(_filename, ios_base::in);
//創(chuàng)建一個字符串緩沖區(qū)
char buff[128];
//讀取一行,放入我們上面剛剛創(chuàng)建的buffer當(dāng)中
ifs.getline(buff, 128);
//然后將我們的address中的內(nèi)容賦值成buff
info._address = buff;
//然后我們再讀取進來一行數(shù)據(jù)
ifs.getline(buff, 128);
//然后將我們的_port,因為它原本是整型,我們需要將其重新轉(zhuǎn)換成整型然后賦值給_port
info._port = stoi(buff);
}
private:
string _filename;
};
int main()
{//文本方式寫出去
ServerInfo info={"127.0.0.1",888};
ConfigureManager cm;
cm.WriteText(info);
//文本方式讀進來
ServerInfo rinfo;
// ConfigureManager cm;
cm.ReadText(rinfo);
cout<
可以成功將文本寫出,并且正常讀取
可以讀取到我們的文本中的內(nèi)容
文件流寫和讀(c++)但是這樣的的方式非常麻煩,我們需要在寫入的時候手動成員的每一個參數(shù)轉(zhuǎn)換成字符串類型,再讀取的時候,還要將讀取到的字符串轉(zhuǎn)換成對應(yīng)的類型放入我們的成員中。
所以我們可以用C++給我們提供的文件流的讀和寫來幫助我們進行上述操作。
#include#includeusing namespace std;
class Date
{//如果是自定義類的話,我們需要重載流插入和流提取
friend ostream& operator<< (ostream& out, const Date& d);
friend istream& operator >>(istream& in, Date& d);
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{}
operator bool()
{// 這里是隨意寫的,假設(shè)輸入_year為0,則結(jié)束
if (_year == 0)
return false;
else
return true;
}
private:
int _year;
int _month;
int _day;
};
istream& operator >>(istream& in, Date& d)
{in >>d._year >>d._month >>d._day;
return in;
}
ostream& operator<< (ostream& out, const Date& d)
{out<< d._year<< " "<< d._month<<" "<< d._day ;
return out;
}
struct ServerInfo
{string _address;
int _port;
Date date;
};
struct ConfigureManager
{public:
ConfigureManager(const char* filename="./server.config")
:_filename(filename)
{}
void WriteText(const ServerInfo& info){ofstream ofs(_filename,ios_base::out);
//這里我們流插入一個成員,我們最好加一個換行或者空格
//這樣可以防止我們的流提取的時候,讀取到的數(shù)據(jù)粘連在一起
ofs<ifstream ifs(_filename,ios_base::in|ios_base::binary);
ifs>>info._address>>info._port>>info.date;
}
private:
string _filename;
};
int main()
{//二進制寫出去
// ServerInfo info={"127.0.0.1",888};
ServerInfo info={"127.0.0.1",888,{2022,11,30}};
ConfigureManager cm;
cm.WriteText(info);
//二進制的讀取
ServerInfo rinfo;
// ConfigureManager cm;
cm.ReadText(rinfo);
cout<
文本方式讀寫的文件是可以直接進行查看的
四、istringstream 序列化和反序列化 ostringstream和istringstream然后我們也是可以正常讀取到的
#includeusing namespace std;
class Date
{friend ostream& operator<< (ostream& out, const Date& d);
friend istream& operator >>(istream& in, Date& d);
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{}
operator bool()
{// 這里是隨意寫的,假設(shè)輸入_year為0,則結(jié)束
if (_year == 0)
return false;
else
return true;
}
private:
int _year;
int _month;
int _day;
};
istream& operator >>(istream& in, Date& d)
{in >>d._year >>d._month >>d._day;
return in;
}
ostream& operator<< (ostream& out, const Date& d)
{out<< d._year<< " "<< d._month<<" "<< d._day ;
return out;
}
struct ChatInfo
{string _name; // 名字
int _id; // id
Date _date; // 時間
string _msg; // 聊天信息
};
int main()
{//序列化
ChatInfo winfo={"催逝員",114514,{2022,11,30},"啊哈哈哈哈,雞湯來嘍,哈哈哈"};
//對應(yīng)的是sprintf
ostringstream oss;
oss<>rInfo._name;
iss>>rInfo._id;
iss>>rInfo._date;
iss>>rInfo._msg;
cout<<"------------------------------------------"<
stringstream在庫中stringstream繼承了istringstream和ostringstream的功能,所以直接調(diào)用這個stringstream就可以了
我們可以將我們上面的代碼修改如下
#includeusing namespace std;
class Date
{friend ostream& operator<< (ostream& out, const Date& d);
friend istream& operator >>(istream& in, Date& d);
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{}
operator bool()
{// 這里是隨意寫的,假設(shè)輸入_year為0,則結(jié)束
if (_year == 0)
return false;
else
return true;
}
private:
int _year;
int _month;
int _day;
};
istream& operator >>(istream& in, Date& d)
{in >>d._year >>d._month >>d._day;
return in;
}
ostream& operator<< (ostream& out, const Date& d)
{out<< d._year<< " "<< d._month<<" "<< d._day ;
return out;
}
struct ChatInfo
{string _name; // 名字
int _id; // id
Date _date; // 時間
string _msg; // 聊天信息
};
int main()
{//序列化
ChatInfo winfo={"催逝員",114514,{2022,11,30},"啊哈哈哈哈,雞湯來嘍,哈哈哈"};
//對應(yīng)的是sprintf
stringstream oss;
oss<>rInfo._name;
iss>>rInfo._id;
iss>>rInfo._date;
iss>>rInfo._msg;
cout<<"------------------------------------------"<
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧