首頁 > 軟體

C++檔案IO流及stringstream流讀寫檔案和字串操作詳解

2023-09-05 18:00:34

一、引入

int main()
{
	string str;
	while (cin >> str)
	{
		cout << str << endl;
	}
	return 0;
}

我們在OJ的時候經常會用到while(cin >> str),這裡的流提取實際上是個阻塞操作,只要緩衝區還有資料就繼續讀,預設以空格或者換行結束,有空格說明是把兩段字串尾插到str。

那麼它是怎麼結束呢?

答案是輸入[Ctrl]-c或者[Ctrl]-z + 換行

[Ctrl]-c是傳送訊號結束程序。

[Ctrl]-z + 換行是通過返回值條件判斷結束while迴圈,具體看下面講解。

二、自定義型別隱式型別轉換

cin >> str的返回值是一個istream類

實際上返回的就是cin物件。而c++98支援了隱式型別轉換,把istream轉換為bool,所以能夠條件判斷。

具體是怎麼轉換的呢?

看下面這個例子:

class A
{
public:
	A(int a)
		: _a(a)
	{}
private:
	int _a;
};
int main()
{
	// 內建型別轉換成自定義型別
	A a = 1;
	return 0;
}

這裡按道理來說是構造一個臨時物件再拷貝構造,而編譯器優化成了直接構造。如果沒有單引數的建構函式就無法轉換。

那如果我們想要讓自定義型別轉換成內建型別呢?

直接int aa = a;肯定會報錯。

但是我們可以加一個特殊的過載函數。

class A
{
public:
	A(int a)
		: _a(a)
	{}
	operator int()
	{
		return _a;
	}
private:
	int _a;
};
int main()
{
	// 內建型別轉換成自定義型別
	A a = 1;
	// 自定義型別轉化成內建型別
	int aa = a;
	cout << aa << endl;
	return 0;
}

而我們上面說的把istream轉化成bool型別就是類似這樣實現的。

operator bool() 裡面會檢查是特殊字元([Ctrl]-z )就會返回false。

三、sync_with_stdio同步

我們知道cin和scanf都有自己的緩衝區,而如果我們用scanf寫入再用cout輸出,就會導致速度變慢很多(緩衝區拷貝)。

而sync_with_stdio函數是一個“是否相容stdio”的開關,C++為了相容C,保證程式在使用了std::printf和std::cout的時候不發生混亂,將輸出流綁到了一起。

決定C++標準streams(cin,cout,cerr…)是否與相應的C標準程式庫檔案(stdin,stdout,stderr)同步,也就是是否使用相同的stream緩衝區,預設情況是同步的,但由於同步會帶來某些不必要的負擔,因此該函數作用就是我們自己可以取消同步 。

#include <iostream>
int main() 
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    // IO
}

四、檔案IO流

檔案的讀寫有兩種:

1️⃣ 二進位制讀寫

2️⃣ 文字讀寫

ofstream是寫入檔案,而ifstream是從檔案中讀取。

4.1 open和close檔案

這裡的參數列示我們想以什麼樣的方式開啟檔案。

比方說當我們想用二進位制的方式開啟檔案:

ofs.open ("test.txt", std::ofstream::out | std::ofstream::binary)

而我們也可以在構造的時候直接傳進引數。

ofstream ofs("test.txt", std::ios_base::out | std::ios_base::binary)

4.2 寫入檔案與讀出檔案

struct ServerInfo
{
	char _address[32];
	int _port;
};
struct Config
{
public:
	Config(const char* filename)
		: _filename(filename)
	{}
	void Write(ServerInfo info)
	{
		ofstream ofs("test.txt", std::ios_base::out | std::ios_base::binary);
		ofs.write((char*)&info, sizeof info);
	}
	void Read(ServerInfo& info)
	{
		ifstream ifs("test.txt", std::ios_base::in | std::ios_base::binary);
		ifs.read((char*)&info, sizeof info);
	}
private:
	string _filename;
};
int main()
{
	Config con("text.txt");
	ServerInfo si = { "aaaaaa", 910 };
	con.Write(si);
	return 0;
}

而我們也可以把資料讀回來。

int main()
{
	Config con("text.txt");
	//ServerInfo si = { "aaaaaa", 910 };
	//con.Write(si);
	ServerInfo si;
	con.Read(si);
	cout << si._address << " " << si._port << endl;
	return 0;
}

可以看到記憶體中和寫出去顯示出來的不一樣。

當然我們可以用文字讀寫的方式。

struct ServerInfo
{
	char _address[32];
	int _port;
};
struct Config
{
public:
	Config(const char* filename)
		: _filename(filename)
	{}
	void Write(ServerInfo info)
	{
		ofstream ofs(_filename);
		// 過載
		ofs << info._address << endl;
		ofs << info._port << endl;
	}
	void Read(ServerInfo& info)
	{
		ifstream ifs(_filename);
		// 過載
		ifs >> info._address;
		ifs >> info._port;
	}
private:
	string _filename;
};
int main()
{
	Config con("text.txt");
	ServerInfo si = { "aaaaaa", 910 };
	con.Write(si);
	/*ServerInfo si;
	con.Read(si);
	cout << si._address << " " << si._port << endl;*/
	return 0;
}

五、stringstream流的使用

在程式中如果想要使用stringstream,必須要包含標頭檔案。在該標頭檔案下,標準庫三個類:

istringstream、ostringstream 和 stringstream,分別用來進行流的輸入、輸出和輸入輸出操作。

5.1 將數值型別資料格式化為字串

int main()
{
	int a = 123;
	const char* b = "456";
	double c = 78.9;
	ostringstream os;
	os << a;
	os << b;
	os << c;
	cout << os.str() << endl;
	return 0;
}

當然我們也可以把每個資料都提取出來。但此時輸入的時候就要空格或者換行隔開。

int main()
{
	int a = 123;
	const char* b = "456";
	double c = 78.9;
	ostringstream os;
	os << a << " ";
	os << b << " ";
	os << c << " ";
	string ret = os.str();
	cout << ret << endl;
	int d;
	char e[20];
	double f;
	istringstream is(ret);
	is >> d >> e >> f;
	cout << d << " ";
	cout << e << " ";
	cout << e << " ";
	return 0;
}

5.2 序列化和反序列化

序列化指的是將一個記憶體物件轉化成一串位元組資料(儲存在一個位元組陣列中),可用於儲存到本地檔案或網路傳輸。反序列化就是將位元組資料還原成記憶體物件。

總結

序列化:將物件變成位元組流的形式傳出去。

反序列化:從位元組流恢復成原來的物件。

簡單來說,物件序列化通經常使用於兩個目的:

1️⃣ 將物件儲存於硬碟上,便於以後反序列化使用;

2️⃣ 在網路上傳送物件的位元組序列

我們現在模擬一個聊天的傳送視窗。

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)
	{}
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
{
	friend istream& operator >> (istream& in, ServerInfo& si);
	friend ostream& operator << (ostream& out, ServerInfo& si);
	string _name;// 暱稱
	Date _d;// 時間
	string _msg;// 資訊
};
istream& operator >> (istream& in, ServerInfo& si)
{
	in >> si._name  >> si._d >> si._msg;
	return in;
}
ostream& operator << (ostream& out, ServerInfo& si)
{
	out << si._name << " ";
	out << si._d << " ";
	out << si._msg << " ";
	return out;
}
int main()
{
	ServerInfo p{ "海闊天空", {2023, 4, 19}, "hello" };
	stringstream os;
	os << p;
	string ret = os.str();
	ServerInfo is;
	stringstream oss(ret);
	oss >> is;
	cout << "-------------------------------------------------------" << endl;
	cout << "暱稱:" << is._name << " ";
	cout << is._d << endl;
	cout << is._name << ": " << is._msg << endl;
	cout << "-------------------------------------------------------" << endl;
	return 0;
}

到此這篇關於C++檔案IO流及stringstream流讀寫檔案和字串操作詳解的文章就介紹到這了,更多相關C++檔案IO流內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com