怎么做類似清風(fēng)dj網(wǎng)站自己做一個(gè)網(wǎng)站
1 流和緩沖區(qū)
C++中,流( stream )和緩沖區(qū)( buffer )是兩個(gè)緊密相關(guān)的概念,它們?cè)谔幚磔斎牒洼敵鰰r(shí)起著重要的作用。
流( Stream )
流是一種抽象的概念,用于表示數(shù)據(jù)的流動(dòng)。在 C++ 中,流是一個(gè)類對(duì)象,它封裝了與輸入/輸出設(shè)備(如鍵盤、顯示器、文件等)的交互。C++標(biāo)準(zhǔn)庫提供了多種流類,如 std::cin (標(biāo)準(zhǔn)輸入流,通常用于從鍵盤讀取數(shù)據(jù))、 std::cout (標(biāo)準(zhǔn)輸出流,通常用于向顯示器輸出數(shù)據(jù))以及 std::fstream (文件流,用于文件的讀寫)。
緩沖區(qū)( Buffer )
緩沖區(qū)是一個(gè)用于臨時(shí)存儲(chǔ)數(shù)據(jù)的內(nèi)存區(qū)域。當(dāng)使用流進(jìn)行輸入或輸出時(shí),數(shù)據(jù)通常不會(huì)直接發(fā)送到設(shè)備或從設(shè)備讀取,而是首先存儲(chǔ)在緩沖區(qū)中。緩沖區(qū)的使用可以提高輸入/輸出的效率,因?yàn)樗试S程序以塊的形式處理數(shù)據(jù),而不是一個(gè)字符一個(gè)字符地處理。
對(duì)于輸出流,程序首先將數(shù)據(jù)寫入緩沖區(qū),然后當(dāng)緩沖區(qū)滿或程序顯式地刷新緩沖區(qū)時(shí),數(shù)據(jù)才會(huì)被發(fā)送到輸出設(shè)備。對(duì)于輸入流,程序從緩沖區(qū)讀取數(shù)據(jù),當(dāng)緩沖區(qū)為空時(shí),流會(huì)從輸入設(shè)備讀取更多的數(shù)據(jù)填充緩沖區(qū)。
緩沖區(qū)的類型
C++中的流緩沖區(qū)主要有三種類型:
(1)全緩沖:當(dāng)緩沖區(qū)滿時(shí),緩沖區(qū)的數(shù)據(jù)才會(huì)被發(fā)送。這種方式適用于大量數(shù)據(jù)的輸入/輸出。
(2)行緩沖:當(dāng)遇到換行符時(shí),緩沖區(qū)的數(shù)據(jù)才會(huì)被發(fā)送。這種方式通常用于終端設(shè)備,如鍵盤和顯示器。
(3)不帶緩沖:數(shù)據(jù)立即被發(fā)送,不經(jīng)過緩沖區(qū)。這種方式通常用于錯(cuò)誤報(bào)告和緊急情況。
iostream 文件
C++ 中的 iostream 文件中包含了一些用來實(shí)現(xiàn)管理流和緩沖區(qū)的類。iostream 中的 io 是 “input/output” 的縮寫,而 stream 表示流,即數(shù)據(jù)流動(dòng)的通道。
在 iostream 庫中,主要包含了以下幾個(gè)組件:
(1)緩沖區(qū)( streambuf 類): streambuf 類為流提供了一個(gè)緩沖區(qū),這個(gè)緩沖區(qū)用于暫存輸入/輸出的數(shù)據(jù),從而允許更高效的I/O操作。streambuf 是一個(gè)抽象基類,不能直接實(shí)例化。它的實(shí)現(xiàn)(如 filebuf 、 stringbuf 等)會(huì)作為其他流類(如 ifstream 、 ofstream 、 istringstream 、 ostringstream 等)的成員被使用。
(2)輸入流( istream 類):用于從輸入設(shè)備(如鍵盤)讀取數(shù)據(jù)。
(3)輸出流( ostream 類):用于向輸出設(shè)備(如顯示器)寫入數(shù)據(jù)。
(4)輸入輸出流 ( iostream 類):從 istream 和 ostream 繼承了輸入和輸出的方法(多重繼承),既可以讀取數(shù)據(jù)也可以寫入數(shù)據(jù)。
(5)流對(duì)象:如 std::cin(標(biāo)準(zhǔn)輸入流,通常用于從鍵盤讀取數(shù)據(jù))、std::cout(標(biāo)準(zhǔn)輸出流,通常用于向顯示器輸出數(shù)據(jù))、std::cerr(用于輸出錯(cuò)誤消息)和 std::clog(用于輸出日志消息)。
(6)操縱器( manipulators):這些是用來控制流的狀態(tài)或格式的函數(shù),例如 std::endl、std::flush、std::setprecision 等。
(7)流提取運(yùn)算符( >> ):用于從輸入流中讀取數(shù)據(jù)。
(8)流插入運(yùn)算符( << ):用于向輸出流中寫入數(shù)據(jù)。
重定向
可以使用 C 標(biāo)準(zhǔn)庫函數(shù) freopen 函數(shù)來重定向標(biāo)準(zhǔn)輸入流 std::cin 或標(biāo)準(zhǔn)輸出流 std::cout 到文件或其他流。當(dāng)重定向一個(gè)流時(shí),實(shí)際上是改變了流與底層設(shè)備(如文件或控制臺(tái))的關(guān)聯(lián)。例如,可以將 std::cin 重定向到一個(gè)文件,這樣當(dāng)從 std::cin 讀取時(shí),實(shí)際上是從文件中讀取數(shù)據(jù)。如下為樣例代碼:
#include <iostream>
#include <fstream> using namespace std;int main() {// 重定向 std::cin 到一個(gè)輸入文件:在這個(gè)文件中輸入 1 ,并保存freopen("input.txt", "r", stdin);// 重定向 std::cout 到一個(gè)輸出文件 freopen("output.txt", "w", stdout);int val;cout << "get a number from input.txt : "; // cout 寫入到 output.txt cin >> val; // 實(shí)際上從 input.txt 讀取 cout << val << endl; // cout 寫入到 output.txt // 關(guān)閉重定向 //freopen(NULL, "r", stdin); // 重置 std::cin 到標(biāo)準(zhǔn)輸入 //freopen(NULL, "w", stdout); // 重置 std::cout 到標(biāo)準(zhǔn)輸出 return 0;
}
執(zhí)行完這段代碼后,output.txt 被存入字符串:“get a number from input.txt : 1” 。
在上面代碼中,freopen 函數(shù)將 std::cin 重定向到名為 input.txt 的文件用于讀取,將 std::cout 重定向到名為 output.txt 的文件用于寫入。因此,當(dāng)從 std::cin 讀取時(shí),實(shí)際上是在讀取 input.txt 文件的內(nèi)容,而當(dāng)向 std::cout 寫入時(shí),數(shù)據(jù)會(huì)被寫入到 output.txt 文件中。
注意,重定向流可能會(huì)影響程序的其他部分,因?yàn)樗淖兞肆鞯哪J(rèn)行為。因此,在重定向流之后,務(wù)必小心處理輸入和輸出,以避免意外的行為或錯(cuò)誤。
另外,如果想要在執(zhí)行完一些重定向操作之后恢復(fù)流的原始狀態(tài),可以使用 freopen 函數(shù)將流重新關(guān)聯(lián)到其原始設(shè)備(比如將第一個(gè)參數(shù)設(shè)置為 NULL 即可將輸入與輸出定向?yàn)闃?biāo)準(zhǔn)輸入與輸出)。
2 輸出
輸出流( output stream )是 I/O 流庫中的一個(gè)關(guān)鍵概念,它允許程序?qū)?shù)據(jù)發(fā)送到特定的目的地,如控制臺(tái)、文件或其他類型的設(shè)備。C++ 標(biāo)準(zhǔn)庫中的 ostream 類是輸出流的基礎(chǔ)。
2.1 std::cout 的基本使用
std::cout 是 C++ 中最常用的輸出流對(duì)象,它通常與控制臺(tái)(或終端)相關(guān)聯(lián),用于向用戶顯示信息??梢允褂貌迦脒\(yùn)算符 << 向 std::cout 發(fā)送數(shù)據(jù),該運(yùn)算符將數(shù)據(jù)發(fā)送到輸出流中。
。如下為樣例代碼:
#include <iostream>
#include <string> using namespace std;int main() {int val1 = 1;double val2 = 1.21;char val3 = 'a';string val4 = "hello";std::cout << "int val1 = " << val1 << std::endl;std::cout << "double val2 = " << val2 << std::endl;std::cout << "char val3 = " << val3 << std::endl;std::cout << "string val4 = " << val4 << std::endl;return 0;
}
上面代碼的輸出為:
int val1 = 1
double val2 = 1.21
char val3 = a
string val4 = hello
除了 std::cout ,C++ 標(biāo)準(zhǔn)庫還提供了其他一些輸出流對(duì)象,例如:
std::cerr:用于輸出錯(cuò)誤消息。與 std::cout 不同的是,std::cerr 通常不會(huì)被緩沖,因此它用于需要立即顯示的錯(cuò)誤或診斷信息。
std::clog:用于輸出日志消息。與 std::cerr 類似,但它通常會(huì)被緩沖。
std::wcout:與 std::cout 類似,但用于輸出寬字符(wchar_t 類型)。
std::wcerr 和 std::wclog:與 std::cerr 和 std::clog 類似,但用于輸出寬字符。
此外,還可以創(chuàng)建自定義的輸出流,通過繼承 std::ostream 類并重載插入運(yùn)算符來實(shí)現(xiàn)。這允許控制數(shù)據(jù)如何被發(fā)送到特定的目的地,例如寫入到特定的文件或進(jìn)行特定的格式化處理。
在處理輸出流時(shí),還可以使用操縱器( manipulators )來更改流的狀態(tài)或格式。例如,std::endl 操縱器不僅插入一個(gè)新行,還刷新輸出緩沖區(qū),確保所有數(shù)據(jù)都被發(fā)送到其目標(biāo)。 std::flush 操縱器也可以用來刷新輸出緩沖區(qū)。
2.2 std::cout 的格式化
std::cout 可以使用各種格式化標(biāo)志和操縱器(manipulators)來控制輸出的格式。以下是一些常見的格式化選項(xiàng):
(1)整數(shù)輸出
(1.1)十進(jìn)制
int val = 12;
std::cout << val << std::endl;// 輸出 12
(1.2)十六進(jìn)制
int val = 12;
std::cout << std::hex << val << std::endl;// 輸出 c
(1.3)八進(jìn)制
int val = 12;
std::cout << std::oct << val << std::endl;// 輸出 14
(1.4)二進(jìn)制
std::cout 默認(rèn)不直接支持二進(jìn)制數(shù)據(jù)的輸出。如果想要輸出一個(gè)整數(shù)的二進(jìn)制表示,則要手動(dòng)將整數(shù)轉(zhuǎn)換為二進(jìn)制字符串,然后再使用 std::cout 輸出這個(gè)字符串。
#include <iostream>
#include <bitset> void printBinary(int val) {// std::bitset 可以將整數(shù)轉(zhuǎn)換為二進(jìn)制字符串 std::bitset<32> binaryVal(val); // 4 個(gè)字節(jié),共 32 位 // 輸出二進(jìn)制字符串 std::cout << binaryVal << std::endl;
}int main() {int val = 12;printBinary(val); // 輸出 num 的二進(jìn)制表示 return 0;
}
上面代碼輸出為:
00000000000000000000000000001100
(2)浮點(diǎn)數(shù)輸出
(2.1)固定點(diǎn)表示法
默認(rèn)情況下,std::cout 使用科學(xué)記數(shù)法來輸出非常大或非常小的浮點(diǎn)數(shù)。使用 std::fixed 使浮點(diǎn)數(shù)總是以固定的小數(shù)位數(shù)顯示。
double pi = 3.141592653589793;// 不使用 std::fixed,默認(rèn)輸出可能使用科學(xué)記數(shù)法
std::cout << "default output: " << pi << std::endl;// 使用 std::fixed 以小數(shù)位數(shù)顯示
std::cout << std::fixed << "fixed output: " << pi << std::endl;
(2.2)固定點(diǎn)表示法
默認(rèn)情況下,std::cout 使用科學(xué)記數(shù)法來輸出非常大或非常小的浮點(diǎn)數(shù)。使用 std::fixed 使浮點(diǎn)數(shù)總是以固定的小數(shù)位數(shù)顯示。
double pi = 3.141592653589793;
std::cout << std::scientific << pi << std::endl;// 輸出 3.141593e+00
(2.2)設(shè)置精度
設(shè)置精度使用接口 std::setprecision ,注意該接口需要引用頭文件: #include
double pi = 3.141592653589793;
std::cout << std::setprecision(3) << pi << std::endl;// 輸出 3.14
(3)字符串輸出
std::cout << "Hello, World!" << std::endl;// 輸出 Hello, World!std::string str = "Hello, World!";
std::cout << str << std::endl;// 輸出 Hello, World!
(4)布爾值輸出
布爾值默認(rèn)以整數(shù)形式顯示( true為 1 , false 為 0 )??梢酝ㄟ^ std::boolalpha 來改變這一點(diǎn),使 true 和 false 以文字形式顯示。
bool flag = true;
std::cout << flag << std::endl; // 輸出 1std::cout << std::boolalpha << flag << std::endl;// 輸出 true
(5)設(shè)置填充和寬度
使用 std::setw 操縱器可以設(shè)置下一個(gè)輸出字段的寬度。如果輸出數(shù)據(jù)小于這個(gè)寬度,std::cout 會(huì)在數(shù)據(jù)前面或后面填充空格,直到達(dá)到指定的寬度。此外還可以使用 std::setfill 操縱器來設(shè)置填充字符。
int val = 123;
std::cout << std::setw(5) << std::setfill('0') << val << std::endl; // 輸出 00123
(6)設(shè)置左對(duì)齊和右對(duì)齊
使用std::left或std::right來控制輸出的對(duì)齊方式。
#include <iostream>
#include <iomanip>int main() {std::cout << std::left << std::setw(10) << "Hello" << std::endl; // 左對(duì)齊 std::cout << std::right << std::setw(10) << "Hello" << std::endl; // 右對(duì)齊return 0;
}
上面代碼輸出為:
HelloHello
2.3 多線程中應(yīng)用 std::cout
C++ 中,std::cout 并不是線程安全的。這意味著如果從多個(gè)線程同時(shí)寫入 std::cout,可能會(huì)遇到數(shù)據(jù)競(jìng)爭(zhēng)(data race)的問題,這會(huì)導(dǎo)致未定義的行為(程序可能會(huì)崩潰或者無響應(yīng))。
可以使用互斥鎖(如 std::mutex )來保護(hù)對(duì) std::cout 的訪問。每個(gè)線程在寫入 std::cout 之前必須獲取鎖,并在寫入完成后釋放鎖。這樣可以確保每次只有一個(gè)線程可以訪問 std::cout。如下為樣例代碼:
#include <iostream>
#include <thread>
#include <mutex> std::mutex g_coutMutex; // 全局互斥鎖 void safePrint(const std::string& message) {std::lock_guard<std::mutex> lock(g_coutMutex); // std::lock_guard是一個(gè)方便的RAII包裝器,它會(huì)在構(gòu)造時(shí)鎖定互斥鎖,并在析構(gòu)時(shí)解鎖。這使得代碼更加簡(jiǎn)潔,并減少了出錯(cuò)的可能性。std::cout << message << std::endl;
}int main() {std::thread t1(safePrint, "Hello from thread 1");std::thread t2(safePrint, "Hello from thread 2");t1.join();t2.join();return 0;
}
2.4 printf
printf 是 C 語言標(biāo)準(zhǔn)庫中的一個(gè)函數(shù),用于格式化輸出到標(biāo)準(zhǔn)輸出流(通常是終端或控制臺(tái)窗口)。它接受一個(gè)格式字符串和與之對(duì)應(yīng)的值作為參數(shù),然后根據(jù)格式字符串中的說明符將值打印出來。 printf 函數(shù)是線程安全的,所以在 C++ 的多線程開發(fā)中,比 std::cout 用的更方便一些。
** printf 常用的格式說明符及其含義:**
%d 或 %i:帶符號(hào)十進(jìn)制整數(shù)。
%u:無符號(hào)十進(jìn)制整數(shù)。
%f:浮點(diǎn)數(shù)(默認(rèn)保留小數(shù)點(diǎn)后六位)。
%c:字符。
%s:字符串。
%p:指針地址。
%x 或 %X:無符號(hào)十六進(jìn)制整數(shù)(小寫或大寫)。
%o:無符號(hào)八進(jìn)制整數(shù)。
%%:輸出一個(gè) % 字符。
除了轉(zhuǎn)換說明符,格式字符串還可以包含以下可選的標(biāo)志、寬度、精度和長(zhǎng)度修飾符:
標(biāo)志:
-:左對(duì)齊輸出。
+:在正數(shù)前面顯示符號(hào)。
(空格):在正數(shù)前面顯示空格。
#:對(duì)于 f、e、E、g、G,輸出小數(shù)點(diǎn);對(duì)于 o,輸出前導(dǎo)零;對(duì)于 x 或 X,輸出 0x 或 0X 前綴。
0:用零填充空白處。
精度:
對(duì)于整數(shù)(d、i、o、u、x、X),指定最小數(shù)字個(gè)數(shù)。
對(duì)于浮點(diǎn)數(shù)(e、E、f、g、G),指定小數(shù)點(diǎn)后的數(shù)字個(gè)數(shù)。
對(duì)于字符串(s),指定最大字符數(shù)。
長(zhǎng)度修飾符:
h:指定短整型(short)或單字符(char)。
l:指定長(zhǎng)整型(long)。
ll:指定長(zhǎng)長(zhǎng)整型(long long)。
L:指定寬字符或?qū)捵址?br /> j:指定 intmax_t 類型。
z:指定 size_t 類型。
t:指定 ptrdiff_t 類型。
3 輸入
C++ 的輸入流(Input Stream)通常指的是從某個(gè)數(shù)據(jù)源(如鍵盤、文件等)讀取數(shù)據(jù)的流。C++標(biāo)準(zhǔn)庫中的 頭文件提供了基本的輸入流功能,主要通過 std::istream 類及其派生類 std::cin 來實(shí)現(xiàn)。
std::cin是預(yù)定義的對(duì)象,代表從標(biāo)準(zhǔn)輸入(通常是鍵盤)接收數(shù)據(jù)的輸入流。可以使用 >> 運(yùn)算符從std::cin讀取數(shù)據(jù),這些數(shù)據(jù)會(huì)被存儲(chǔ)在相應(yīng)的變量中。
如下為樣例代碼(使用 std::cin 從鍵盤讀取數(shù)據(jù)):
#include <iostream> int main() { int val; std::cout << "input an integer : "; std::cin >> val; // 從標(biāo)準(zhǔn)輸入讀取整數(shù)并存儲(chǔ)在變量 val 中 std::cout << "the input integer is: " << val << std::endl; return 0;
}
4 文件操作
C++ 的文件操作主要涉及文件的打開、關(guān)閉、讀取和寫入。C++標(biāo)準(zhǔn)庫中的 頭文件提供了用于文件操作的類,其中 std::ifstream 用于讀取文件, std::ofstream 用于寫入文件,而 std::fstream 則既可以讀取也可以寫入文件。
打開文件
在讀取或?qū)懭胛募?#xff0c;需要使用相應(yīng)的文件流對(duì)象打開一個(gè)文件??梢酝ㄟ^提供文件名來構(gòu)造一個(gè)文件流對(duì)象,該對(duì)象會(huì)在構(gòu)造時(shí)嘗試打開文件。如下為樣例代碼:
#include <fstream>
#include <iostream> int main() {// 創(chuàng)建一個(gè)用于寫入的文件流對(duì)象 std::ofstream outfile("test.txt");// 檢查文件是否成功打開 if (!outfile) {std::cerr << "failed to open the file" << std::endl;return 1;}// 寫入一些數(shù)據(jù)到文件 outfile << "hello" << std::endl;// 關(guān)閉文件 outfile.close();return 0;
}
讀取文件
使用 std::ifstream 可以讀取文件的內(nèi)容。可以使用流提取運(yùn)算符 >> 或 getline 函數(shù)來讀取數(shù)據(jù)。如下為樣例代碼:
#include <fstream>
#include <iostream>
#include <string> int main() {// 創(chuàng)建一個(gè)用于讀取的文件流對(duì)象 std::ifstream infile("test.txt");// 檢查文件是否成功打開 if (!infile) {std::cerr << "failed to open the file" << std::endl;return 1;}// 讀取文件內(nèi)容 std::string line;while (std::getline(infile, line)) {std::cout << line << std::endl;}// 關(guān)閉文件 infile.close();return 0;
}
快速讀取大文件
如果需要快速讀取一個(gè)大文件,則要避免逐行讀取或者逐字符讀取帶來的額外開銷。一種快速讀取文件的方法是使用文件流的 read 成員函數(shù),它可以一次讀取多個(gè)字符,這樣可以減少系統(tǒng)調(diào)用的次數(shù),從而提高讀取效率。如下為樣例代碼:
#include <fstream>
#include <iostream>
#include <vector> int main() {// 打開文件 std::ifstream file("large_file.bin", std::ios::binary);// 檢查文件是否成功打開 if (!file){std::cerr << "failed to open the file" << std::endl;return 1;}// 獲取文件大小 file.seekg(0, std::ios::end);std::streamsize fileSize = file.tellg();file.seekg(0, std::ios::beg);// 分配足夠的內(nèi)存來存儲(chǔ)文件內(nèi)容 std::vector<char> buffer(fileSize);// 讀取文件內(nèi)容到buffer if (!file.read(buffer.data(), fileSize)) {std::cerr << "讀取文件失敗" << std::endl;return 1;}// 在這里處理文件內(nèi)容,例如可以將其輸出到標(biāo)準(zhǔn)輸出 std::cout.write(buffer.data(), fileSize);// 關(guān)閉文件 file.close();return 0;
}
讀寫文件
下面是一個(gè)同時(shí)包含讀取和寫入操作的示例:
#include <fstream>
#include <iostream>
#include <string> int main() {// 寫入文件 std::ofstream outfile("test.txt");if (!outfile){std::cerr << "failed to open the file" << std::endl;return 1;}outfile << "first line" << std::endl; //寫入第一行outfile << "second line" << std::endl; //寫入第二行outfile.close();// 讀取文件 std::ifstream infile("test.txt");if (!infile) {std::cerr << "failed to open the file" << std::endl;return 1;}std::string line;while (std::getline(infile, line)){std::cout << line << std::endl;}infile.close();return 0;
}
文件流的狀態(tài)
可以使用文件流對(duì)象的 is_open() 成員函數(shù)來檢查文件是否已成功打開。此外,還可以使用 fail() , eof() , 和 bad() 等成員函數(shù)來檢查流的狀態(tài)。
文件流的其他操作
clear(): 重置流的狀態(tài)標(biāo)志。
seekg() 和 seekp(): 移動(dòng)文件的讀寫指針。
tellg() 和 tellp(): 返回文件的讀寫指針當(dāng)前位置。
flush(): 清空輸出緩沖區(qū),確保所有數(shù)據(jù)都被寫入文件。