首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Larbin 開源爬蟲核心資料結構剖析:FIFO、Vector、檔案讀取

     編輯:關於C語言

前言

Larbin 是經典 C++ 開源網頁爬蟲,其核心資料結構簡潔高效,無外部依賴。本文深度解析 readfile、Fifo、Vector 三大模組,展示生產級 C++ 容器與 I/O 實作細節。

一、readfile:動態緩衝讀取整個檔案

char* readfile(int fd) {
    size_t pos = 0, size = 512;
    char buf[500];
    ssize_t n;
    char* res = new char[size];

    while (true) {
        n = read(fd, buf, 500);
        if (n == 0) break;           // EOF
        if (n < 0) {                 // 錯誤
            if (errno != EINTR && errno != EIO) break;
            continue;
        }
        // 擴容策略:倍增
        if (pos + n >= size) {
            size *= 2;
            char* tmp = new char[size];
            memcpy(tmp, res, pos);
            delete[] res;
            res = tmp;
        }
        memcpy(res + pos, buf, n);
        pos += n;
    }
    res[pos] = '\0';
    return res; // 呼叫端負責 delete[]
}

亮點:定長堆疊緩衝 buf[500] 減少系統呼叫、指數級擴容攤還 O(1)、處理 EINTR 重試。

二、Fifo:環狀佇列 + 自動擴容

template <class T>
class Fifo {
    uint in, out, size;
    T** tab; // 指標陣列

public:
    Fifo(uint sz = 2000) : in(0), out(0), size(sz) {
        tab = new T*[size];
    }
    ~Fifo() { delete[] tab; }

    // 讀取但不移除
    inline T* read() { return tab[out]; }
    T* tryRead() { return (in==out) ? nullptr : tab[out]; }

    // 取出並移除
    T* get() {
        assert(in != out);
        T* tmp = tab[out];
        out = (out + 1) % size;
        return tmp;
    }
    T* tryGet() {
        if (in == out) return nullptr;
        T* tmp = tab[out];
        out = (out + 1) % size;
        return tmp;
    }

    // 放入,滿則倍增
    void put(T* obj) {
        tab[in] = obj;
        in = (in + 1) % size;
        if (in == out) { // 滿
            T** tmp = new T*[2*size];
            for (uint i = out; i < size; ++i) tmp[i] = tab[i];
            for (uint i = 0; i < in; ++i) tmp[i+size] = tab[i];
            in += size;
            size *= 2;
            delete[] tab;
            tab = tmp;
        }
    }

    // 歸還剛取出的元素(回退)
    void rePut(T* obj) {
        out = (out + size - 1) % size;
        tab[out] = obj;
    }

    int getLength() { return (in + size - out) % size; }
    bool isEmpty() { return in == out; }
};

設計點:

  • 儲存 T* 而非 T,避免大物件複製,語意為擁有權轉移
  • 環狀索引 (idx+1)%size,rePut 支援回退語意
  • 擴容時重新排列邏輯順序,保持 out 指向舊頭部

三、Vector:指標陣列 + 手動記憶體管理

template <class T>
class Vector {
    T** tab;
    uint pos, size;
public:
    Vector(uint sz = 100) : pos(0), size(sz) { tab = new T*[size]; }
    ~Vector() { for (uint i=0;i sz) { size = sz; delete[] tab; tab = new T*[size]; }
        pos = 0;
    }

    void addElement(T* elt) {
        if (pos == size) { // 擴容
            size *= 2;
            T** tmp = new T*[size];
            for (uint i=0;i

與 STL 差異:

  • 儲存指標,析構時 delete 每個元素 → 擁有權語意明確
  • 無拷貝/移動語意,禁止拷貝(未聲明 private)
  • recycle 重用記憶體,避免頻繁配置

四、現代 C++ 重構建議

原始現代替代收益
readfilestd::string s; std::getline(ifs, s, '\0'); 或 std::vector<char>RAII、例外安全
Fifo<T>std::deque<std::unique_ptr<T>> + 自定義擴容策略標準化、智慧指標擁有權
Vector<T>std::vector<std::unique_ptr<T>>移動語意、標準演算法相容

五、總結

Larbin 的容器雖無 STL 完備,但展現了 C++ 手動記憶體管理的經典模式:指標陣列、擁有權語意、指數擴容、環狀索引。理解這些底層邏輯,有助於在無法使用 STL 的嵌入式/核心環境下自行實作高效容器。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved