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