最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

三種在C++中高效獲取日志文件最后10行的方法

 更新時間:2026年01月07日 10:02:03   作者:weixin_pk138132  
C++編程中,你經(jīng)常需要處理文件,尤其是日志文件,一個非常常見的任務(wù)是:我不想看整個10GB的日志文件,我只想看最后?10?行,看看最近發(fā)生了什么,所以本文給大家介紹了三種在C++中高效獲取日志文件最后10行的方法,需要的朋友可以參考下

在C++編程中,你經(jīng)常需要處理文件,尤其是日志文件。一個非常常見的任務(wù)是:“我不想看整個10GB的日志文件,我只想看最后 10 行,看看最近發(fā)生了什么。”

這就像 Linux/macOS 上的 tail -n 10 命令。

一個簡單的比喻:“讀一本厚書的最后一章”

  • 問題: 你想讀一本 1000 頁巨著的最后一章(最后10行)。
  • “天真”的辦法 (Naive Method):你從第 1 頁開始,一頁一頁地讀并記住(存入內(nèi)存)所有 1000 頁內(nèi)容,最后再翻到你記住的第 990 頁開始看。
    • 缺點: 極度浪費內(nèi)存(O(N) 空間)和時間(O(N) 時間)。
  • “折中”的辦法 (Circular Buffer):你只拿 10 張便簽。你從第 1 頁開始讀,第 1 頁內(nèi)容寫在便簽1,…,第 10 頁寫在便簽10。當(dāng)你讀第 11 頁時,你擦掉便簽1,寫上第 11 頁的內(nèi)容。讀第 12 頁時,擦掉便簽2…
    • 缺點:仍然需要從頭到尾讀完 1000 頁(O(N) 時間)。
    • 優(yōu)點: 你只需要 10 張便簽的內(nèi)存(O(k) 空間)。
  • “專業(yè)”的辦法 (Seek from End):你直接把書翻到最后一頁seekg(0, ios::end))。然后,你開始一頁一頁往前翻,一邊翻一邊數(shù)你翻過了多少個“章節(jié)末尾”(\n 換行符)。當(dāng)你數(shù)到 10 個時,你就停下,然后從這里往后讀到結(jié)尾。
    • 優(yōu)點: 速度極快(只讀文件尾部,O(k*L) 時間,L為行長),幾乎不占內(nèi)存(O(1) 空間)。
    • 缺點: 邏輯最復(fù)雜。

在本教程中,你將學(xué)會:

  • 文件輸入流 ifstream:如何打開和讀取文件。
  • 方法 1 (“天真”法):讀取所有行到 vector。
  • 方法 2 (“折中”法):使用 deque (雙端隊列) 作為“循環(huán)緩沖區(qū)”。
  • 方法 3 (“專業(yè)”法):使用 seekgtellg 從文件末尾反向讀取。
  • 文件指針 (seekg)ios::end, ios::cur 的含義。
  • “X光透 視”:用調(diào)試器“親眼目睹” seekg 是如何反向計數(shù)的。

前置知識說明 (100% 自洽):

  • 變量 (Variable):理解存儲數(shù)據(jù)的“盒子”,如 int n = 10;。
  • string (字符串):C++標(biāo)準(zhǔn)庫提供的“魔法彈性盒子”,用于處理文本。你需要 #include <string>。
  • vector (向量):C++標(biāo)準(zhǔn)庫提供的一種“動態(tài)數(shù)組”(“魔法彈性盒子列表”)。你需要 #include <vector>。
  • deque (雙端隊列):類似 vector,但支持高效地頭部和尾部添加/刪除元素。你需要 #include <deque>。
  • ifstream (文件輸入流):C++ 用于讀取文件的工具。你需要 #include <fstream>。
  • seekg / tellgifstream 的成員函數(shù),用于“Seek Get” (移動讀取指針) 和 “Tell Get” (告知讀取指針位置)。
  • 編譯 (Compile):C++代碼(“食譜”)必須被“編譯”(“烘焙”),才能變成電腦可執(zhí)行的程序(“蛋糕”)。

準(zhǔn)備工作:創(chuàng)建一個測試文件testlog.txt

在運行代碼前,請在你的 .cpp 文件相同的目錄下,創(chuàng)建一個名為 testlog.txt 的文件,并填入以下內(nèi)容(確保最后一行有換行):

Line 1: The quick brown fox
Line 2: jumps over
Line 3: the lazy dog.
Line 4: ---
Line 5: C++ File I/O
Line 6: is powerful.
Line 7: ---
Line 8: Testing line 8.
Line 9: Testing line 9.
Line 10: Testing line 10.
Line 11: Testing line 11.
Line 12: Testing line 12.
Line 13: This is the final line.

第一部分:方法 1 (“天真”法) —— 讀取所有行

邏輯: 把文件的每一行都讀入一個 vector<string>,然后只打印這個 vector 的最后 10 個元素。

naive_tail.cpp

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;

void printLast10_Naive(const string& filename) {
    ifstream file(filename);
    if (!file.is_open()) {
        cerr << "錯誤: 無法打開文件 " << filename << endl;
        return;
    }

    vector<string> allLines;
    string line;
    
    // 1. “天真”地讀取 *所有* 行
    while (getline(file, line)) {
        allLines.push_back(line);
    }
    file.close();

    // 2. 計算從哪里開始打印
    int totalLines = allLines.size();
    int start_index = 0;
    if (totalLines > 10) {
        start_index = totalLines - 10;
    }

    // 3. 打印最后 10 (或更少) 行
    cout << "--- 方法 1 (Naive) ---" << endl;
    for (int i = start_index; i < totalLines; ++i) {
        cout << allLines[i] << endl;
    }
}

int main() {
    printLast10_Naive("testlog.txt");
    return 0;
}
  • 優(yōu)點: 邏輯最簡單,易于理解。
  • 缺點: 極度浪費內(nèi)存。如果 testlog.txt 是 10GB,你的程序會嘗試申請 10GB 內(nèi)存!

第二部分:方法 2 (“折中”法) —— 循環(huán)緩沖區(qū)

邏輯: 我們只保留一個固定大小(10)的“緩沖區(qū)”(使用 deque)。從頭到尾讀取文件,每讀一行,就把它塞進緩沖區(qū),如果緩沖區(qū)“滿了”(超過10),就從前面擠掉”最舊的那一行。

circular_buffer.cpp

#include <iostream>
#include <fstream>
#include <string>
#include <deque> // 需要雙端隊列
using namespace std;

void printLast10_Circular(const string& filename, int N = 10) {
    ifstream file(filename);
    if (!file.is_open()) {
        cerr << "錯誤: 無法打開文件 " << filename << endl;
        return;
    }

    deque<string> buffer;
    string line;

    // 1. 仍然讀取 *所有* 行
    while (getline(file, line)) {
        // 2. 添加到“隊尾”
        buffer.push_back(line);
        
        // 3. 如果緩沖區(qū)“超載”,從“隊首”擠掉
        if (buffer.size() > N) {
            buffer.pop_front();
        }
    }
    file.close();

    // 4. 打印緩沖區(qū)中剩下的 N 行
    cout << "--- 方法 2 (Circular Buffer) ---" << endl;
    for (const string& s : buffer) {
        cout << s << endl;
    }
}

int main() {
    printLast10_Circular("testlog.txt");
    return 0;
}
  • 優(yōu)點: 內(nèi)存效率極高(O(k) 空間)。
  • 缺點: 仍然需要從頭到尾讀取整個文件(O(N) 時間),對于 10GB 的文件,這仍然很慢。

第三部分:方法 3 (“專業(yè)”法) ——seekg反向讀取

邏輯: 像“tail 命令”一樣,直接跳到文件末尾,然后一個字節(jié)一個字節(jié)地往前“挪”,同時**“數(shù)”**換行符 \n。當(dāng)我們數(shù)到 10 個換行符時,我們就找到了第 10 行的開頭。

seekg_pro.cpp (推薦的方式)

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

void printLast10_Pro(const string& filename, int N = 10) {
    ifstream file(filename);
    if (!file.is_open()) {
        cerr << "錯誤: 無法打開文件 " << filename << endl;
        return;
    }

    // 1. 跳轉(zhuǎn)到文件末尾
    //    (ios::ate 模式可以打開文件并立即定位到末尾)
    //    或者使用 seekg:
    file.seekg(0, ios::end); 

    // 2. 獲取當(dāng)前位置(即文件總大?。?
    long long pos = file.tellg();
    
    // 如果文件為空
    if (pos == 0) {
        cout << "文件為空。" << endl;
        return;
    }
    
    int newlineCount = 0;
    string lineBuffer; // 用于讀取最后的殘行

    // 3. “行內(nèi)預(yù)警”:我們從 *最后一個字符* 開始往前“跳”
    // (pos 是文件大小,最后一個字符的索引是 pos - 1)
    for (long long i = pos - 1; i >= 0; i--) {
        file.seekg(i); // “跳”到第 i 個字節(jié)
        
        char c = file.get(); // 讀取那 1 個字節(jié)
        
        if (c == '\n') {
            newlineCount++;
        }
        
        // 4. “剎車”:當(dāng)我們找到 N 個換行符時
        // (注意:GFG的例子是 == N,但 >= N 更健壯)
        if (newlineCount >= N) {
            // “行內(nèi)預(yù)警”:我們需要跳到 *這個換行符之后* 的位置
            file.seekg(i + 1); 
            break; // 停止“回溯”
        }
    }
    
    // 5. 如果文件行數(shù)不足 N,我們最終會跳到開頭
    if (newlineCount < N) {
        file.seekg(0); // 重置到文件開頭
    }

    // 6. 現(xiàn)在,從我們“停下”的位置,*順序* 讀到文件末尾
    cout << "--- 方法 3 (Seek from End) ---" << endl;
    string line;
    while (getline(file, line)) {
        cout << line << endl;
    }

    file.close();
}

int main() {
    printLast10_Pro("testlog.txt");
    return 0;
}

“手把手”終端模擬 (所有方法):

PS C:\MyCode> g++ ... # 編譯所有 .cpp 文件
PS C:\MyCode> .\naive_tail.exe
--- 方法 1 (Naive) ---
Line 4: ---
Line 5: C++ File I/O
Line 6: is powerful.
Line 7: ---
Line 8: Testing line 8.
Line 9: Testing line 9.
Line 10: Testing line 10.
Line 11: Testing line 11.
Line 12: Testing line 12.
Line 13: This is the final line.

PS C:\MyCode> .\circular_buffer.exe
--- 方法 2 (Circular Buffer) ---
Line 4: ---
... (輸出同上) ...
Line 13: This is the final line.

PS C:\MyCode> .\seekg_pro.exe
--- 方法 3 (Seek from End) ---
Line 4: ---
... (輸出同上) ...
Line 13: This is the final line.

頓悟時刻: 三種方法結(jié)果相同,但效率(尤其是內(nèi)存和I/O)天差地別!seekg 是處理大文件的“專業(yè)”選擇。

第四部分:“X光透 視”——親眼目睹“反向搜尋”

讓我們用“X光眼鏡”(調(diào)試器)來觀察 seekg_pro.cpp 是如何工作的。

“X光”實戰(zhàn)(基于seekg_pro.cpp)

設(shè)置斷點:

  • 動作: 在VS Code中,把你的鼠標(biāo)移動到第32行if (c == '\n') 那一行)的行號左邊。
  • 點擊那個小 red dot,設(shè)置一個斷點。

啟動“子彈時間”(F5):

  • 動作: 按下 F5 鍵。
  • 你會看到: file 被打開,seekg(0, ios::end) 被執(zhí)行,pos 被設(shè)為文件大小(例如 250 字節(jié))。

第一次“凍結(jié)” (i = 249, 假設(shè)):

  • for 循環(huán)開始。i 是 249 (文件的最后一個字符)。
  • file.seekg(249) 執(zhí)行。
  • file.get() 讀取 testlog.txt 的最后一個字符(假設(shè)是 \n)。
  • 程序“凍結(jié)”在第32行。
  • 開啟“X光”(觀察變量):
    • pos: 250
    • i: 249
    • newlineCount: 0
    • c: '\n'
  • 動作: 按下 F10(“Step Over”,步過)。
  • 你會看到: newlineCount 變成了 1。

繼續(xù)執(zhí)行 (F5):

  • 動作: 連續(xù)按下 F5(“Continue”,讓程序在斷點處循環(huán))。
  • 你會看到: 調(diào)試器會一次又一次地停在第32行。
  • 觀察 inewlineCount 的變化:
    • i遞減 (248, 247, …)。
    • 只有當(dāng) c 恰好\n 時,newlineCount 才會增加。
  • 第十次“凍結(jié)”在 \n
    • 假設(shè) i 此時是 60。
    • c: '\n'。
    • newlineCount 變成了 9。
  • 動作: 按下 F10 鍵,newlineCount 變?yōu)?10。
  • 動作: 再按 F10 鍵,if (newlineCount >= 10) true!
  • 動作: 按下 F11 鍵(“Step Into”) 進入 if 塊。
  • 你會看到: 高亮條移動到 file.seekg(i + 1); (即 file.seekg(61);)。
  • 動作:F10 執(zhí)行 break;
  • 頓悟時刻: 循環(huán)終止!程序“定位”到了第10個換行符(索引60)。

(程序繼續(xù)) file.seekg(61) 將指針設(shè)置到“第4行”的開頭,while (getline(...)) 開始順序打印,直到文件末尾。

動手試試!(終極挑戰(zhàn):你的“可配置tail”)

現(xiàn)在,你來當(dāng)一次“工具開發(fā)者”。

任務(wù):

  1. 復(fù)制本教程“方法 2 (循環(huán)緩沖區(qū))”的代碼(printLast10_Circular)。
  2. 修改這個函數(shù),使其能夠返回一個 vector<string>,而不是 void(打?。?/li>
  3. main 函數(shù)中,調(diào)用這個新函數(shù)(比如 vector<string> lastLines = getLastNLines("testlog.txt", 5);),并自己遍歷打印這個返回的 vector。
  4. (進階) 復(fù)制本教程“方法 3 (專業(yè) seekg)”的代碼,并同樣將其修改為返回 vector<string>,而不是 void(打印)。(提示:在 file.seekg(pos); 之后,你需要使用 getline 循環(huán)把剩余的行讀入一個新的 vector 并返回)。

flexible_tail.cpp (你的 TODO - 挑戰(zhàn)方法 2):

#include <iostream>
#include <fstream>
#include <string>
#include <deque>
#include <vector>
using namespace std;

// --- TODO 1 & 2: 修改函數(shù),使其返回 vector<string> ---
vector<string> getLastNLines_Circular(const string& filename, int N = 10) {
    ifstream file(filename);
    deque<string> buffer;
    
    // (如果打開失敗,返回一個空 vector)
    if (!file.is_open()) {
        cerr << "錯誤: 無法打開文件 " << filename << endl;
        return vector<string>(); 
    }
    
    string line;
    while (getline(file, line)) {
        buffer.push_back(line);
        if (buffer.size() > N) {
            buffer.pop_front();
        }
    }
    file.close();

    // --- TODO 2: 將 deque 轉(zhuǎn)換為 vector 并返回 ---
    // (提示:vector 有一個構(gòu)造函數(shù)可以直接接收兩個迭代器)
    // return vector<string>(buffer.begin(), buffer.end());
}

int main() {
    // --- TODO 3: 調(diào)用新函數(shù)并打印 ---
    cout << "--- 測試 getLastNLines (N=5) ---" << endl;
    
    // vector<string> last5Lines = getLastNLines_Circular("testlog.txt", 5);
    
    // for (const string& s : last5Lines) {
    //     cout << s << endl;
    // }
    
    return 0;
}

這個挑戰(zhàn)讓你把“打印”邏輯和“數(shù)據(jù)獲取”邏輯分離開,這是更健壯的函數(shù)設(shè)計。如果你能進一步挑戰(zhàn)并修改 seekg 版本,你就能完全掌握C++中高效文件讀取的精髓!

以上就是三種在C++中高效獲取日志文件最后10行的方法的詳細(xì)內(nèi)容,更多關(guān)于C++獲取日志文件最后10行的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論

手游| 金秀| 延安市| 衡山县| 荔浦县| 双江| 仙桃市| 麻栗坡县| 阳春市| 白银市| 洛阳市| 柘荣县| 海宁市| 嘉禾县| 富民县| 顺义区| 泉州市| 武平县| 皋兰县| 会宁县| 永和县| 孝义市| 武功县| 泸水县| 本溪| 辽宁省| 沙河市| 大名县| 双辽市| 富蕴县| 乌拉特中旗| 乌拉特后旗| 河源市| 班戈县| 龙门县| 乐清市| 阿拉尔市| 安义县| 西乌珠穆沁旗| 大洼县| 响水县|