主頁 > 軟體設計 > B編碼與BT種子檔案分析,以及模仿json-cpp寫一個B編碼決議器

B編碼與BT種子檔案分析,以及模仿json-cpp寫一個B編碼決議器

2021-06-15 07:27:53 軟體設計

B編碼與BT種子檔案分析,以及模仿json-cpp寫一個B編碼決議器

  • 1、什么是B編碼
  • 2、B編碼格式
  • 3、種子檔案結構
    • 3.1、主檔案結構
    • 3.2、info結構
  • 4、簡單的例子了解一下種子檔案和B編碼
  • 5、分析JSON-CPP的設計
    • 5.1、分析Json::Value::CZString的設計
    • 5.2、分析Json::Value的設計
      • 5.2.1、類成員設計
      • 5.2.2、類方法設計
    • 5.3、迭代器的設計
      • 5.3.1、Json::ValueIteratorBase
      • 5.3.2、Json::ValueConstIterator
      • 5.3.3、Json::ValueIterator
    • 5.4、總結
  • 6、B編碼決議器設計
    • 6.1、BEncode::Value
    • 6.2、BEncode::ValueIteratorBase
    • 6.3、BEncode::ValueConstIterator
    • 6.4、BEncode::ValueIterator
    • 6.5、決議函式
  • 7、總結并附上本文源代碼

1、什么是B編碼

B編碼是種子檔案以及tracker服務器回傳資訊的編碼格式,DHT協議和BT協議傳輸的格式都是經過B編碼壓縮的

2、B編碼格式

B編碼有以下4種資料型別:

  • 字串:編碼格式為<十進制ASCII編碼的長度>:<字串>,需要注意的是字串沒有開始結束符,如4:span,表示字串span,
  • 整數:編碼格式為i<十進制ASCII編碼的整數>e,以i開頭,e結尾,中間的數值可以是負數,如i-3e是有效的,
  • 串列:編碼格式為l<編碼值>e,以l開頭,e結尾,<編碼值>可以是任意的B編碼的字串、整數、字典或者其他串列,如l4:info3:path,表示由字串info、path組成的串列,
  • 字典:編碼格式為d<字串><編碼元素>e,以d開頭,e結尾, 需要注意的是<字串>必須是B編碼的字串,<編碼元素>可以是任意的B編碼的字串、整數、串列或者其他字典,

3、種子檔案結構

種子檔案里的內容其實就是文本,是經過B編碼之后的文本,它主要包含以下欄位

3.1、主檔案結構

  • info:必須,一個描述torrent檔案的字典,有兩種可能形式,一種是沒有目錄結構的“單一檔案”,一種是有目錄結構的“多檔案”
  • announce:可選,tracker服務器的地址URL(字串)
  • announce-list:可選,tracker服務器串列,這是官方規范的一個擴展,向后兼容,用來存盤備用服務器串列
  • creation date:可選,torrent檔案的創建時間,為Unix時間戳
  • comment:可選,備注資訊
  • created by:可選,說明torrent檔案是由哪個程式創建的
  • encoding:可選,種子檔案的默認編碼
  • nodes:可選,這個欄位包含一系列ip和相應埠的串列,用于連接DHT初始node

3.2、info結構

所有關于下載的檔案的資訊都在這個欄位里,它包括多個子欄位,而且根據下載的是單個檔案還是多個檔案,子欄位會有所不同

單檔案結構如下:

  • name:必須,檔案名(字串)
  • name.utf-8:可選,內容同上,區別在于使用了UTF-8編碼
  • length:檔案長度,單位位元組(整數)
  • piece length:必須,每個塊的大小,單位位元組(整數)
  • pieces:必須,檔案的特征資訊,該欄位比較大,實際上是種子內包含所有的檔案段的SHA1的校驗值的連接,即將所有檔案按照piece length的位元組大小分成塊,每塊計算一個SHA1值,然后將這些值連接起來就形成了pieces欄位,由于SHA1的校驗值為20Byte,所以該欄位的大小始終為20的整數倍位元組,該欄位是種子檔案中體積最大的部分,可見如果大檔案分塊很小,會造成種子檔案體積十分龐大
  • publisher:可選,發布者的名字
  • publisher.utf-8:可選,發布者的名字的utf-8編碼
  • publisher-url:可選,發布者的網址
  • publisher-url.utf-8:可選,發布者網址的utf-8編碼,

多檔案結構如下:

  • files:必須,是一個串列,存盤檔案的名字與大小資訊,該欄位包含以下三個子欄位:

      length:必須,檔案的大小(位元組)
      path:必須,是一個串列,從上到下識別,最末尾的欄位是檔案名,前面的是檔案路徑,內容在下載時不允許更改
      path.utf-8:可選,內容同上,區別在于使用了UTF-8編碼
    
  • name:必須,推薦的檔案夾名,此項可于下載時更改(字串)

  • name.utf-8:可選,內容同上,區別在于使用了UTF-8編碼

  • piece length:必須,每個塊的大小,單位位元組(整數)

  • pieces:必須,檔案的特征資訊,該欄位比較大,實際上是種子內包含所有的檔案段的SHA1的校驗值的連接,即將所有檔案按照piece length的位元組大小分成塊,每塊計算一個SHA1值,然后將這些值連接起來就形成了pieces欄位,由于SHA1的校驗值為20Byte,所以該欄位的大小始終為20的整數倍位元組,該欄位是種子檔案中體積最大的部分,可見如果大檔案分塊很小,會造成種子檔案體積十分龐大

  • publisher:可選,發布者的名字

  • publisher.utf-8:可選,發布者的名字的utf-8編碼

  • publisher-url:可選,發布者的網址

  • publisher-url.utf-8:可選,發布者網址的utf-8編碼,

當種子里包含單個檔案時,name欄位描述的是資源的名稱了,此時name欄位在下載時不允許更改

4、簡單的例子了解一下種子檔案和B編碼

工欲善其事,必先利其器,要想更好地了解B編碼,就需要拿一個種子檔案來分析其中的內容,我們可以自己去隨便一個網站下載一個種子檔案下來,下載的話使用用迅雷之類的軟體就行了,然后使用BEncode Editor這個軟體打開種子,可以看到類似下面的內容,和我們之前描述的差不多
在這里插入圖片描述

5、分析JSON-CPP的設計

決議器是參考JSON-CPP來寫的,大家可以自行去網上下載JSON-CPP的原始碼,或者 點擊這里 下載bifang框架原始碼,里面src/json/里面就是JSON-CPP的原始碼了

5.1、分析Json::Value::CZString的設計

Json::Value里面有一個CZString類,是為了用于統一管理陣列型別和物件型別的,如下所示

class CZString {
  public:
    enum DuplicationPolicy { noDuplication = 0, duplicate, duplicateOnCopy };
    CZString(ArrayIndex index);
    CZString(char const* str, unsigned length, DuplicationPolicy allocate);
    CZString(CZString const& other);
    CZString(CZString&& other) noexcept;
    ~CZString();
    CZString& operator=(const CZString& other);
    CZString& operator=(CZString&& other) noexcept;

    bool operator<(CZString const& other) const;
    bool operator==(CZString const& other) const;
    ArrayIndex index() const;
    // const char* c_str() const; ///< \deprecated
    char const* data() const;
    unsigned length() const;
    bool isStaticString() const;

  private:
    void swap(CZString& other);

    struct StringStorage {
      unsigned policy_ : 2;
      unsigned length_ : 30; // 1GB max
    };

    char const* cstr_; // actually, a prefixed string, unless policy is noDup
    union {
      ArrayIndex index_;
      StringStorage storage_;
    };
  };

從類成員中可以看到,該類可以存盤整型和字串型別的資料,我們設計時可以簡單粗暴地使用std::string來存盤字串型別就行,從后面的代碼中我們可以看到這個類是作為std::map的鍵值來使用的,這樣陣列型別和物件型別都可以用map來表示了,要實作這點,關鍵的一點就是要實作比較運算子的多載,如下所示,可以看到,當CZString表示的是陣列型別時,使用index_去作比較運算,表示的是物件時使用的是cstr_去作比較運算,完美解決問題,

bool Value::CZString::operator<(const CZString& other) const {
  if (!cstr_)
    return index_ < other.index_;
  // return strcmp(cstr_, other.cstr_) < 0;
  // Assume both are strings.
  unsigned this_len = this->storage_.length_;
  unsigned other_len = other.storage_.length_;
  unsigned min_len = std::min<unsigned>(this_len, other_len);
  JSON_ASSERT(this->cstr_ && other.cstr_);
  int comp = memcmp(this->cstr_, other.cstr_, min_len);
  if (comp < 0)
    return true;
  if (comp > 0)
    return false;
  return (this_len < other_len);
}

bool Value::CZString::operator==(const CZString& other) const {
  if (!cstr_)
    return index_ == other.index_;
  // return strcmp(cstr_, other.cstr_) == 0;
  // Assume both are strings.
  unsigned this_len = this->storage_.length_;
  unsigned other_len = other.storage_.length_;
  if (this_len != other_len)
    return false;
  JSON_ASSERT(this->cstr_ && other.cstr_);
  int comp = memcmp(this->cstr_, other.cstr_, this_len);
  return comp == 0;
}

5.2、分析Json::Value的設計

5.2.1、類成員設計

可以看到Json::Value是使用了一個聯合體來作為類成員使用的,由于每個Json::Value只可能代表一種型別(總不能又代表陣列型別又代表物件型別吧,,,),所以使用聯合體可以最大程度地節省空間,比較巧妙的做法,我們平時也可以在特定的場合使用這種技巧

using LargestInt = int;
using LargestUInt = unsigned int;
typedef std::map<CZString, Value> ObjectValues;
union ValueHolder {
  LargestInt int_;
  LargestUInt uint_;
  double real_;
  bool bool_;
  char* string_; // if allocated_, ptr to { unsigned, char[] }.
  ObjectValues* map_;
} value_;

5.2.2、類方法設計

這里看幾個典型的方法設計就行,大部分比較簡單大家可以自己去看看

  • append方法的實作如下,可以看到由于append是給陣列型別的資料使用的,所以每次插入資料之后不需要讓map重新排序啥的,可以直接在指定位置構造元素即可,所以使用了std::map的emplace方法(size方法的回傳值是ArrayIndex,如果不理解ArrayIndex為什么可以替代CZString的得自行去補一下C++的基礎,,,)
Value& Value::append(const Value& value) { return append(Value(value)); }

Value& Value::append(Value&& value) {
  JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue,
                      "in Json::Value::append: requires arrayValue");
  if (type() == nullValue) {
    *this = Value(arrayValue);
  }
  return this->value_.map_->emplace(size(), std::move(value)).first->second;
}
  • find方法的實作如下,沒什么特別的,就是使用了std::map的find而已,多載的operator[](const char* key)之類的方法中也是使用了這個find來實作的,難度不大,大家自己看一遍就明白了
Value const* Value::find(char const* begin, char const* end) const {
  JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
                      "in Json::Value::find(begin, end): requires "
                      "objectValue or nullValue");
  if (type() == nullValue)
    return nullptr;
  CZString actualKey(begin, static_cast<unsigned>(end - begin),
                     CZString::noDuplication);
  ObjectValues::const_iterator it = value_.map_->find(actualKey);
  if (it == value_.map_->end())
    return nullptr;
  return &(*it).second;
}

const Value& Value::operator[](const char* key) const {
  Value const* found = find(key, key + strlen(key));
  if (!found)
    return nullSingleton();
  return *found;
}

5.3、迭代器的設計

5.3.1、Json::ValueIteratorBase

迭代器基類設計如下,可以看到類成員的定義為 Value::ObjectValues::iterator current_,這只是簡單托管了Json::Value中**map_**的迭代器而已,沒有什么特殊的地方,

class JSON_API ValueIteratorBase {
public:
  using iterator_category = std::bidirectional_iterator_tag;
  using size_t = unsigned int;
  using difference_type = int;
  using SelfType = ValueIteratorBase;

  bool operator==(const SelfType& other) const { return isEqual(other); }

  bool operator!=(const SelfType& other) const { return !isEqual(other); }

  difference_type operator-(const SelfType& other) const {
    return other.computeDistance(*this);
  }

  /// Return either the index or the member name of the referenced value as a
  /// Value.
  Value key() const;

  /// Return the index of the referenced Value, or -1 if it is not an
  /// arrayValue.
  UInt index() const;

  /// Return the member name of the referenced Value, or "" if it is not an
  /// objectValue.
  /// \note Avoid `c_str()` on result, as embedded zeroes are possible.
  String name() const;

  /// Return the member name of the referenced Value. "" if it is not an
  /// objectValue.
  /// \deprecated This cannot be used for UTF-8 strings, since there can be
  /// embedded nulls.
  JSONCPP_DEPRECATED("Use `key = name();` instead.")
  char const* memberName() const;
  /// Return the member name of the referenced Value, or NULL if it is not an
  /// objectValue.
  /// \note Better version than memberName(). Allows embedded nulls.
  char const* memberName(char const** end) const;

protected:
  /*! Internal utility functions to assist with implementing
   *   other iterator functions. The const and non-const versions
   *   of the "deref" protected methods expose the protected
   *   current_ member variable in a way that can often be
   *   optimized away by the compiler.
   */
  const Value& deref() const;
  Value& deref();

  void increment();

  void decrement();

  difference_type computeDistance(const SelfType& other) const;

  bool isEqual(const SelfType& other) const;

  void copy(const SelfType& other);

private:
  Value::ObjectValues::iterator current_;
  // Indicates that iterator is for a null value.
  bool isNull_{true};

public:
  // For some reason, BORLAND needs these at the end, rather
  // than earlier. No idea why.
  ValueIteratorBase();
  explicit ValueIteratorBase(const Value::ObjectValues::iterator& current);
};

設計上是沒有什么特別的,因為是利用std::map的迭代器進行封裝的,下面簡單看一下幾個方法的實作就行,我們封裝B編碼的也采用差不多的方法

Value& ValueIteratorBase::deref() { return current_->second; }
const Value& ValueIteratorBase::deref() const { return current_->second; }
void ValueIteratorBase::increment() { ++current_; }
void ValueIteratorBase::decrement() { --current_; }

5.3.2、Json::ValueConstIterator

繼承于迭代器基類,非常簡單,大家自行看一下就行

class JSON_API ValueIterator : public ValueIteratorBase {
  friend class Value;

public:
  using value_type = Value;
  using size_t = unsigned int;
  using difference_type = int;
  using reference = Value&;
  using pointer = Value*;
  using SelfType = ValueIterator;

  ValueIterator();
  explicit ValueIterator(const ValueConstIterator& other);
  ValueIterator(const ValueIterator& other);

private:
  /*! \internal Use by Value to create an iterator.
   */
  explicit ValueIterator(const Value::ObjectValues::iterator& current);

public:
  SelfType& operator=(const SelfType& other);

  SelfType operator++(int) {
    SelfType temp(*this);
    ++*this;
    return temp;
  }

  SelfType operator--(int) {
    SelfType temp(*this);
    --*this;
    return temp;
  }

  SelfType& operator--() {
    decrement();
    return *this;
  }

  SelfType& operator++() {
    increment();
    return *this;
  }

  /*! The return value of non-const iterators can be
   *  changed, so the these functions are not const
   *  because the returned references/pointers can be used
   *  to change state of the base class.
   */
  reference operator*() { return deref(); }
  pointer operator->() { return &deref(); }
};

5.3.3、Json::ValueIterator

繼承于迭代器基類,非常簡單,大家自行看一下就行

class JSON_API ValueIterator : public ValueIteratorBase {
  friend class Value;

public:
  using value_type = Value;
  using size_t = unsigned int;
  using difference_type = int;
  using reference = Value&;
  using pointer = Value*;
  using SelfType = ValueIterator;

  ValueIterator();
  explicit ValueIterator(const ValueConstIterator& other);
  ValueIterator(const ValueIterator& other);

private:
  /*! \internal Use by Value to create an iterator.
   */
  explicit ValueIterator(const Value::ObjectValues::iterator& current);

public:
  SelfType& operator=(const SelfType& other);

  SelfType operator++(int) {
    SelfType temp(*this);
    ++*this;
    return temp;
  }

  SelfType operator--(int) {
    SelfType temp(*this);
    --*this;
    return temp;
  }

  SelfType& operator--() {
    decrement();
    return *this;
  }

  SelfType& operator++() {
    increment();
    return *this;
  }

  /*! The return value of non-const iterators can be
   *  changed, so the these functions are not const
   *  because the returned references/pointers can be used
   *  to change state of the base class.
   */
  reference operator*() { return deref(); }
  pointer operator->() { return &deref(); }
};

5.4、總結

JSON-CPP的實作還是非常巧妙的,里面的很多設計和stl很相似,類提供的包括迭代器的設計也是這樣的,這也是JSON-CPP使用起來很簡單的一個原因.我們可以很輕易地借鑒這些優秀的代碼來設計出一個B編碼的決議器

6、B編碼決議器設計

6.1、BEncode::Value

和JSON-CPP的實作差不多,舍棄了一些沒用的功能,簡化了代碼,如下所示

class Value
{
friend class ValueIteratorBase;

private:
    class CZString
    {
    public:
        CZString(size_t index);
        CZString(const std::string& str);
        CZString(const CZString& other);
        ~CZString();

        CZString& operator=(const CZString& other);
        
        bool operator<(const CZString& other) const;
        bool operator==(const CZString& other) const;

        std::string* data() const { return m_str;   }
        size_t index()      const { return m_index; }

    private:
        std::string* m_str;
        size_t m_index;
    };

public:
    typedef std::map<CZString, Value> DictionaryValues;
    using const_iterator = ValueConstIterator;
    using iterator = ValueIterator;

    enum Type
    {
        BCODE_INTEGER = 0,
        BCODE_STRING,
        BCODE_LIST,
        BCODE_DICTIONARY,
    };

    Value(Type type = BCODE_INTEGER);
    Value(int64_t value);
    Value(const std::string& value);
    Value(const Value& other);
    ~Value();

    Type getType()      const { return m_type;                     }
    bool isInteger()    const { return m_type == BCODE_INTEGER;    }
    bool isString()     const { return m_type == BCODE_STRING;     }
    bool isList()       const { return m_type == BCODE_LIST;       }
    bool isDictionary() const { return m_type == BCODE_DICTIONARY; }
    size_t size() const;
    bool empty() const;
    std::string typeToString() const;

    void setType(Type type);
    void clear();
    void resize(size_t newSize);

    /**
     * brief: You may need to say 'value[0u]' to get your compiler to distinguish
     *        this from the operator[] which takes a string
     */
    Value& operator[](size_t index);
    Value& operator[](const std::string& key);

    void swap(Value& other);
    Value& operator=(const Value& other);

    bool operator<(const Value& other) const;
    bool operator>(const Value& other) const;
    bool operator<=(const Value& other) const;
    bool operator>=(const Value& other) const;
    bool operator==(const Value& other) const;
    bool operator!=(const Value& other) const;

    int64_t asInt() const;
    std::string asString() const;

    Value& append(const Value& value);
    bool insert(size_t index, const Value& newValue);

    Value get(size_t index, const Value& defaultValue) const;
    Value get(const std::string& key, const Value& defaultValue) const;

    const_iterator find(const std::string& key) const;
    iterator find(const std::string& key);

    bool isMember(const std::string& key) const;
    void removeMember(const std::string& key);
    bool removeMember(const std::string& key, Value* removed);
    bool removeIndex(size_t index, Value* removed);
    std::vector<std::string> getMemberNames() const;

    const_iterator begin() const;
    const_iterator end() const;

    iterator begin();
    iterator end();

private:
    void free();

private:
    Type m_type;
    union ValueHolder
    {
        int64_t m_int;
        std::string* m_string;
        DictionaryValues* m_map;
    } m_value;
};

6.2、BEncode::ValueIteratorBase

class ValueIteratorBase
{
public:
    using SelfType = ValueIteratorBase;

    ValueIteratorBase();
    explicit ValueIteratorBase(const Value::DictionaryValues::iterator& current);

public:
    bool operator==(const SelfType& other) const { return isEqual(other);  }
    bool operator!=(const SelfType& other) const { return !isEqual(other); }
    int64_t operator-(const SelfType& other) const { return other.computeDistance(*this); }

    Value key() const;

    size_t index() const;

    std::string name() const;

protected:
    Value& deref() { return m_current->second; }
    const Value& deref() const { return m_current->second; }

    void increment() { m_current++; }
    void decrement() { m_current--; }

    int64_t computeDistance(const SelfType& other) const;

    bool isEqual(const SelfType& other) const;

    void copy(const SelfType& other);

private:
    Value::DictionaryValues::iterator m_current;
    bool m_isNull = true;
};

6.3、BEncode::ValueConstIterator

class ValueConstIterator : public ValueIteratorBase
{
friend class Value;

public:
    using value_type = const Value;
    using reference = const Value&;
    using pointer = const Value*;
    using SelfType = ValueConstIterator;

    ValueConstIterator();
    ValueConstIterator(const ValueIterator & other);

private:
    explicit ValueConstIterator(const Value::DictionaryValues::iterator& current);

public:
    SelfType& operator=(const ValueIteratorBase& other)
    {
        copy(other);
        return *this;
    }

    SelfType operator++(int)
    {
        SelfType temp(*this);
        ++*this;
        return temp;
    }

    SelfType operator--(int)
    {
        SelfType temp(*this);
        --*this;
        return temp;
    }

    SelfType& operator--()
    {
        decrement();
        return *this;
    }

    SelfType& operator++()
    {
        increment();
        return *this;
    }

    reference operator*() const { return deref(); }

    pointer operator->() const { return &deref(); }
};

6.4、BEncode::ValueIterator

class ValueIterator : public ValueIteratorBase
{
friend class Value;

public:
    using value_type = Value;
    using reference = Value&;
    using pointer = Value*;
    using SelfType = ValueIterator;

    ValueIterator();
    ValueIterator(const SelfType& other);
    explicit ValueIterator(const ValueConstIterator& other);

private:
    explicit ValueIterator(const Value::DictionaryValues::iterator& current);

public:
    SelfType& operator=(const SelfType& other)
    {
        copy(other);
        return *this;
    }

    SelfType operator++(int)
    {
        SelfType temp(*this);
        ++*this;
        return temp;
    }

    SelfType operator--(int)
    {
        SelfType temp(*this);
        --*this;
        return temp;
    }

    SelfType& operator--()
    {
        decrement();
        return *this;
    }

    SelfType& operator++()
    {
        increment();
        return *this;
    }

    reference operator*() { return deref(); }

    pointer operator->() { return &deref(); }
};

6.5、決議函式

決議函式是自己寫的,遞回決議B編碼,沒有什么特別的地方,大家可以自行去下載源代碼去除錯一下,由于是個人撰寫的,沒有經過大量的測驗,大家如果測驗了有問題歡迎提出改進意見

7、總結并附上本文源代碼

本文先是介紹了B編碼的格式及用途,再介紹了BT種子檔案的格式,最后通過模仿JSON-CPP的代碼自己寫了一個B編碼的決議器,
這里附上源代碼一份,有錯漏的歡迎大家提出修改意見,謝謝
c++撰寫的B編碼決議器原始碼

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/287420.html

標籤:其他

上一篇:TCP通信細節—貳

下一篇:還沒有女朋友的朋友們,你們有福了,學會CycleGAN把男朋友變成女朋友

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more