主頁 > 軟體設計 > 【專案】撰寫一個在線OJ的專案,快進到自己造題刷

【專案】撰寫一個在線OJ的專案,快進到自己造題刷

2021-01-31 10:40:43 軟體設計

【專案】撰寫一個在線OJ的專案

  • 1.專案目標
  • 2.專案環境
  • 3.模快劃分
    • 1.試題模塊
    • 2. 編譯模塊
    • 3.http模塊
    • 4.工具模塊
  • 4.各模塊具體實作:
    • 4.1 http模塊
      • 4.1.1 回應獲取整個試題串列的請求
      • 4.1.2 回應獲取單個試題的請求
      • 4.1.3 回應編譯運行請求
    • 4.2 試題模塊
      • 4.2.0 試題保存形式及其內容
      • 4.2.1 試題模塊模板
      • 4.2.2 加載config檔案,獲取題目資訊
      • 4.2.3 上層呼叫介面,獲取所有試題介面
      • 4.2.4 上層呼叫介面,獲取單個試題介面
    • 4.3 工具模板
      • 4.3.1 讀取檔案內容
      • 4.3.2 字串切割
      • 4.3.3 決議資料包并解碼
      • 4.3.4 向HTML填充資訊
      • 4.3.5獲取時間戳
      • 4.3.6日志記錄
    • 4.4 編譯運行模塊
      • 4.4.0 檢查引數
      • 4.4.1 將代碼存入檔案中
      • 4.4.2 編譯
      • 4.4.3 運行
      • 4.4.4 構造回應
      • 4.4.5 洗掉臨時檔案

1.專案目標

做出一個在線oj系統,支持查看題目串列,支持點擊單個題目,支持代碼塊書寫代碼,支持提交書寫的代碼到后端,支持后端編譯+運行,支持回傳結果

2.專案環境

1.利用開源庫cpp-httplib中的httplib.h頭檔案鏈接如下:
https:llgitee.comliqxglcpp-httplib?_from=gitee_search

2.安裝jsoncpp:
yum install jsoncpp
yun install jsoncpp-devel

3.安裝boost環境:
sudo yum install -y snappy-devel boost-devel zlib-devel.x86_64 python-pip
sudo pip install BeautifulSoup4

git clone https://gitee.com/HGtz2222/ThirdPartLibForCpp.git
cd ./ThirdPartLibForCpp/el7.x86_64/
sh install.sh

3.模快劃分

B/S瀏覽器+服務器模式
在這里插入圖片描述

1.試題模塊

在這里插入圖片描述

  1. 獲取所有試題資訊,回傳上層呼叫
  2. 獲取單個試題資訊,回傳上層呼叫

2. 編譯模塊

在這里插入圖片描述

1.編譯運行,將結果回傳上層呼叫

3.http模塊

在這里插入圖片描述

提供三個介面, 分別處理三個請求:

  1. 請求顯示所有試題:獲取整個試題串列,回復回應
  2. 請求顯示單個試題:獲取單個試題資訊,回復回應
  3. 請求顯示編譯運行:獲取編譯運行結果,回復回應

4.工具模塊

提供加載檔案、字串切割、解碼輔助、html頁面填充渲染方法等等

4.各模塊具體實作:

4.1 http模塊

#include <iostream>
#include <cstdio>
#include "httplib.h"
#include "oj_model.hpp"
#include "oj_view.hpp"

int main()
{
    using namespace httplib;
    OjModel model;
    //1.初始化httplib庫的server物件
    Server svr;

    //2.提供三個介面, 分別處理三個請求
    //2.1 獲取整個試題串列, get
    svr.Get() {};

    //2.2 獲取單個試題
    svr.Get(){};

    //2.3 編譯運行
    svr.Post(){};

    svr.listen("0.0.0.0", 17878);
    return 0;
}

4.1.1 回應獲取整個試題串列的請求

在這里插入圖片描述

 svr.Get("/all_questions", [&model](const Request& req, Response& resp){
            //1.回傳試題串列
            std::vector<Question> questions;
            model.GetAllQuestion(&questions);
            std::string html;
            OjView::DrawAllQuestions(questions, &html);

            resp.set_content(html, "text/html");
            });
  1. 呼叫試題模板的GetAllQuestion方法獲取所有題目資訊,存盤于vector< Question > questions
  2. 呼叫工具模板的DrawALLQuestion方法,questions中保存的題目資訊渲染到HTML頁面中,回應用戶

4.1.2 回應獲取單個試題的請求

在這里插入圖片描述

 	//  瀏覽器提交的資源路徑是  /question/[試題編號] 
    //  \d+ : 正則運算式:表示多位數字 
    svr.Get(R"(/question/(\d+))", [&model](const Request& req, Response& resp){
            //1.獲取url當中關于試題的數字 & 獲取單個試題的資訊
            std::cout << req.version << " " << req.method << std::endl;
            std::cout << req.path <<  std::endl;
            Question ques;
            model.GetOneQuestion(req.matches[1].str(), &ques);

            //2.渲染模版的html檔案
            std::string html;
            OjView::DrawOneQuestion(ques, &html);
            resp.set_content(html, "text/html");
            });
  1. 呼叫試題模板的GetOneQuestion方法獲取單個題目資訊,存盤于Question ques
  2. 呼叫工具模板的DrawOneQuestion方法,ques中保存的題目資訊渲染到HTML頁面中,回應用戶

4.1.3 回應編譯運行請求

在這里插入圖片描述

 svr.Post(R"(/compile/(\d+))", [&model](const Request& req, Response& resp){
            //1.獲取試題編號 & 獲取試題內容
            Question ques;
            model.GetOneQuestion(req.matches[1].str(), &ques);
            //ques.tail_cpp_ ==> main函式呼叫+測驗用例
            //post 方法在提交代碼的時候, 是經過encode的, 要想正常獲取瀏覽器提交的內容, 需要進行
            //decode, 使用decode完成的代碼和tail.cpp進行合并, 產生待編譯的原始碼
            std::unordered_map<std::string, std::string> body_kv;
            UrlUtil::PraseBody(req.body, &body_kv);

            std::string user_code = body_kv["code"];
            //2.構造json物件, 交給編譯運行模塊
            Json::Value req_json;
            req_json["code"] = user_code + ques.tail_cpp_;
            req_json["stdin"] = "";

            std::cout << req_json["code"].asString() << std::endl;

            Json::Value resp_json;
            Compiler::CompileAndRun(req_json, &resp_json);

            //獲取的回傳結果都在 resp_json當中
            std::string err_no = resp_json["errorno"].asString();
            std::string case_result = resp_json["stdout"].asString();
            std::string reason = resp_json["reason"].asString();

            std::string html;
            OjView::DrawCaseResult(err_no, case_result, reason, &html);

            resp.set_content(html, "text/html");
            });
  1. 根據URL的后綴名http://192.168.21.129:17878/question/1,最后數字為題目編號,因此通過正則表達(/compile/(\d+)進行路徑匹配,
  2. 通過req.matches[1].str(),找到試題編號,接著呼叫試題模塊的GetOneQuestion方法獲取該試題的所有資訊
  3. 對資料包進行拆分PraseBody獲取未解碼的代碼,解碼后與tail.cpp檔案組合成待編譯的檔案
  4. 構造json物件,進行組織管理,交給編譯模塊運行CompileAndRun
  5. 獲取編譯運行的回傳結果存盤在 resp_json當中
  6. DrawCaseResult將結果填充給html頁面,回傳回應set_content

4.2 試題模塊

4.2.0 試題保存形式及其內容

1.由各個試題序號命名的檔案夾 + 共用一個config檔案組成:
在這里插入圖片描述


2.config檔案包含所有試題資訊:題目序號、題目名字、題目難易程度、以題目序號命名的檔案夾路徑(以tab鍵分隔)

在這里插入圖片描述


3.以題目序號命名的檔案夾包含:
在這里插入圖片描述
以檔案1中的三個檔案為例子:

  • header.cpp[保存檔案頭部]
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;

class Solution {
  public:
    bool isPalindrome(int x) {
      return true;         
    }
};
  • desc.txt[題目的描述]
判斷一個整數是否是回文數,回文數是指正序(從左向右)和倒序(從右向左)讀都是一樣的整數,

示例 1:

輸入: 121
輸出: true
示例 2:

輸入: -121
輸出: false
解釋: 從左向右讀,-121 , 從右向左讀,121- ,因此它不是一個回文數,
示例 3:

輸入: 10
輸出: false
解釋: 從右向左讀,01 ,因此它不是一個回文數,
進階:

你能不將整數轉為字串來解決這個問題嗎?

  • tail.cpp[檔案末尾,包含測驗用例以及呼叫邏輯]
#ifndef CompileOnline
// 這是為了撰寫用例的時候有語法提示. 實際線上編譯的程序中這個操作是不生效的.
#include "header.cpp"
#endif

///
// 此處約定:
// 1. 每個用例是一個函式
// 2. 每個用例從標準輸出輸出一行日志
// 3. 如果用例通過, 統一列印 [TestName] ok!
// 4. 如果用例不通過, 統一列印 [TestName] failed! 并且給出合適的提示.
///

void Test1() {
  bool ret = Solution().isPalindrome(121);
  if (ret) {
    std::cout << "Test1 ok!" << std::endl;
  } else {
    std::cout << "Test1 failed! input: 121, output expected true, actual false" << std::endl;
  }
}

void Test2() {
  bool ret = Solution().isPalindrome(-10);
  if (!ret) {
    std::cout << "Test2 ok!" << std::endl;
  } else {
    std::cout << "Test2 failed! input: -10, output expected false, actual true" << std::endl;
  }
}

int main() {
  Test1();
  Test2();
  return 0;
}


4.2.1 試題模塊模板

#pragma once
#include <iostream>
#include <string>
#include <unordered_map>
#include <fstream>
#include <vector>
#include "tools.hpp"

struct Question
{
    std::string id_; //題目id
    std::string title_; //題目標題
    std::string star_; //題目的難易程度
    std::string path_; //題目路徑

    std::string desc_; //題目的描述
    std::string header_cpp_; //題目預定義的頭
    std::string tail_cpp_; //題目的尾, 包含測驗用例以及呼叫邏輯
};


class OjModel
{
    public:
        OjModel()
        {
        	//加載config檔案
        	Load("【config檔案路徑】");
        }

        ~OjModel()
        {
        }

        //從config檔案夾當中獲取題目資訊
        bool Load(const std::string& filename)
        {
        }

        //提供給上層呼叫這一個獲取所有試題的介面
        bool GetAllQuestion(std::vector<Question>* questions)
        {
        }

        //提供給上層呼叫者一個獲取單個試題的介面     
        bool GetOneQuestion(const std::string& id, Question* ques)
        {
        }
    private:
        std::unordered_map<std::string, Question> ques_map_;
};

  1. 采用Question結構體存盤一道題目的所有資訊
  2. 采用unordered_map將每道題目以id為鍵, Question為值,組織管理所有試題

4.2.2 加載config檔案,獲取題目資訊

 bool Load(const std::string& filename)
        {
            //fopen    open     C++      fstream
            std::ifstream file(filename.c_str());
            if(!file.is_open())
            {
                std::cout << "config file open failed" << std::endl;
                return false;
            }            
            std::string line;
            while(std::getline(file, line))
            {
                
                std::vector<std::string> vec;
                StringUtil::Split(line, "\t", &vec);
                //boost::spilt分割函式,line中字串以tab進行分割
                Question ques;
                ques.id_ = vec[0];
                ques.title_ = vec[1];
                ques.star_ = vec[2];
                ques.path_ = vec[3];
                std::string dir = vec[3];
                FileUtil::ReadFile(dir + "/desc.txt", &ques.desc_);
                FileUtil::ReadFile(dir + "/header.cpp", &ques.header_cpp_);
                FileUtil::ReadFile(dir + "/tail.cpp", &ques.tail_cpp_);
                ques_map_[ques.id_] = ques;
            }

            file.close();
            return true;
        }
  1. 通過加載config檔案獲取所有試題資訊:【id】 【名字】 【難易】 【路徑】,將每一道題的資訊存盤在Question結構體中的id_title_star_path_
  2. 呼叫工具模塊的ReadFile函式分別讀取【路徑】下的desc.txt,header.cpp,tail.cpp檔案,獲取題目描述,題目預定義的頭,測驗用例,存盤在Question結構體中的desc_header_cpp_tail_cpp_
  3. 這樣我們讀取了config檔案中的一行資訊,也就是一道題的所有資訊存盤在Question結構體物件ques中,
  4. 將每道題目的id作為鍵,ques物件作為值,存入unordered_map<std::string, Question> ques_map_當中進行組織管理

4.2.3 上層呼叫介面,獲取所有試題介面

 bool GetAllQuestion(std::vector<Question>* questions)
        {
            //1.遍歷無序的map, 將試題資訊回傳給呼叫者
            //對于每一個試題, 都是一個Question結構體物件
            for(const auto& kv : ques_map_)
            {
                questions->push_back(kv.second);
            }

            //2.排序
            std::sort(questions->begin(), questions->end(), [](const Question& l, const Question& r){
                    //比較Question當中的題目編號, 按照升序規則
                    return std::stoi(l.id_) < std::stoi(r.id_);
                    });
            return true;
        }
  1. 遍歷ques_map_中的值,通過 questions作為出參存盤所有試題資訊,返還給上層呼叫

4.2.4 上層呼叫介面,獲取單個試題介面

bool GetOneQuestion(const std::string& id, Question* ques)
        {
            auto it = ques_map_.find(id);
            if(it == ques_map_.end())
            {
                return false;
            }
            *ques = it->second;
            return true;
        }
  1. 根據ques_map_中的鍵(題目id)找到對應試題資訊,通過 ques存盤試題資訊作為出參,返還給上層呼叫

4.3 工具模板

4.3.1 讀取檔案內容

class FileUtil
{
    public:
        //讀檔案介面
        //file_name: 檔案名稱
        //content: 讀到的內容, 輸出引數, 返還呼叫者
        static bool ReadFile(const std::string& file_name, std::string* content)
        {
            //1.清空content當中的內容
            content->clear();

            std::ifstream file(file_name.c_str());
            if(!file.is_open())
            {
                return false;
            }

            //檔案被打開了
            std::string line;
            while(std::getline(file, line))
            {
                (*content) += line + "\n";
            }
            file.close();
            return true;
        }
};
  1. content作為出參,保存檔案內容

4.3.2 字串切割

class StringUtil
{
    public:
        static void Split(const std::string& input, const std::string& split_char, std::vector<std::string>* output)
        {
            boost::split(*output, input, boost::is_any_of(split_char), boost::token_compress_off);
        }
};
  1. 根據字符切割字串

4.3.3 解析資料包并解碼

//body從httplib.h當中的request類的成員變數獲得
        //  body:
        //     key=value&key1=value1   ===> 并且是進行過編碼
        //  1.先切割
        //  2.在對切割之后的結果進行轉碼
        static void PraseBody(const std::string& body, std::unordered_map<std::string, std::string>* body_kv)
        {
            std::vector<std::string> kv_vec;
            StringUtil::Split(body, "&", &kv_vec);

            for(const auto& t : kv_vec)
            {
                std::vector<std::string> sig_kv;
                StringUtil::Split(t, "=", &sig_kv);

                if(sig_kv.size() != 2)
                {
                    continue;
                }

                //在保存的時候, 針對value在進行轉碼
                (*body_kv)[sig_kv[0]] = UrlDecode(sig_kv[1]);
            }
        }
static std::string UrlDecode(const std::string& str)  
{  
    std::string strTemp = "";  
    size_t length = str.length();  
    for (size_t i = 0; i < length; i++)  
    {  
        if (str[i] == '+') strTemp += ' ';  
        else if (str[i] == '%')  
        {  
            assert(i + 2 < length);  
            unsigned char high = FromHex((unsigned char)str[++i]);  
            unsigned char low = FromHex((unsigned char)str[++i]);  
            strTemp += high*16 + low;  
        }  
        else strTemp += str[i];  
    }  
    return strTemp;  
} 

在這里插入圖片描述

  1. 決議資料包code=xxx&&stdin=xxx
  2. 根據=拆分資料包,獲得正文
  3. 正文進行解碼,轉化為代碼

4.3.4 向HTML填充資訊

class OjView
{
    public:
        static void DrawAllQuestions(std::vector<Question>& questions, std::string* html)
        {
            //1. 創建template字典
            ctemplate::TemplateDictionary dict("all_questions");

            //2.遍歷vector, 遍歷vector就相當于遍歷多個試題, 每一個試題構造一個子字典
            for(const auto& ques : questions)
            {
                TemplateDictionary* AddSectionDictionary(const TemplateString section_name);
                ctemplate::TemplateDictionary* sub_dict = dict.AddSectionDictionary("question");
                //void SetValue(const TemplateString variable, const TemplateString value);
                /*
                 *   variable: 指定的是在預定義的html當中的變數名稱
                 *   value: 替換的值
                 * */
                sub_dict->SetValue("id", ques.id_);
                sub_dict->SetValue("id", ques.id_);
                sub_dict->SetValue("title", ques.title_);
                sub_dict->SetValue("star", ques.star_);
            }

            //3.填充
            ctemplate::Template* tl = ctemplate::Template::GetTemplate("./template/all_questions.html", ctemplate::DO_NOT_STRIP);
            //all_questions.html內容被加載到記憶體中,以逐字逐句的方式,原檔案未被修改
            //渲染
            tl->Expand(html, &dict);
        }

        static void DrawOneQuestion(const Question& ques, std::string* html)
        {
            ctemplate::TemplateDictionary dict("question");
            dict.SetValue("id", ques.id_);
            dict.SetValue("title", ques.title_);
            dict.SetValue("star", ques.star_);
            dict.SetValue("desc", ques.desc_);
            dict.SetValue("id", ques.id_);
            dict.SetValue("code", ques.header_cpp_);
            ctemplate::Template* tl = ctemplate::Template::GetTemplate("./template/question.html", ctemplate::DO_NOT_STRIP);
            //渲染
            tl->Expand(html, &dict);
        }
};
  1. 運用ctemplate創建根字典
  2. 根據每道題目創建子字典
  3. 依據關鍵字匹配,通過字典對html頁面進行填充渲染,

4.3.5獲取時間戳

//獲取時間戳
class TimeUtil
{
    public:
        static int64_t GetTimeStampMs()
        {
            struct timeval tv;
            gettimeofday(&tv, NULL);
            return tv.tv_sec + tv.tv_usec / 1000;
        }

        //[年月日 時分秒]
        static void GetTimeStamp(std::string* TimeStamp)
        {
            time_t st;
            time(&st);

            struct tm* t = localtime(&st);
            
            char buf[30] = { 0 };
            snprintf(buf, sizeof(buf) - 1, "%04d-%02d-%02d %02d:%02d:%02d", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
            TimeStamp->assign(buf, strlen(buf));
        }
};

4.3.6日志記錄

enum LogLevel
{
    INFO = 0,
    WARNING,
    ERROR,
    FATAL,
    DEBUG
};


const char* Level[] = 
{
    "INFO",
    "WARNING",
    "ERROR",
    "FATAL",
    "DEBUG"
};

/*
 * lev:日志等級
 * file: 檔案名稱
 * line : 行號
 * logmsg : 想要記錄的日志內容
 * */

inline std::ostream& Log(LogLevel lev, const char* file, int line, const std::string& logmsg)
{
    std::cout << "begin log" << std::endl;
    std::string level_info = Level[lev];
    std::cout << level_info << std::endl;
    std::string TimeStamp;
    TimeUtil::GetTimeStamp(&TimeStamp);
    std::cout << TimeStamp << std::endl;

    std::cout << "[" << TimeStamp << " " << level_info << " " << file << ":" << line << "]" << " " << logmsg;
    return std::cout;
}


#define LOG(lev, msg) Log(lev, __FILE__, __LINE__, msg)

4.4 編譯運行模塊

4.4.0 檢查引數

if(Req["code"].empty())
            {
                (*Resp)["errorno"] = PRAM_ERROR;
                (*Resp)["reason"] = "Pram error";

                return;
            }
  1. 引數是否是錯誤的, json串當中的code是否為空

4.4.1 將代碼存入檔案中

std::string code = Req["code"].asString();
            std::string file_nameheader = WriteTmpFile(code);
            if(file_nameheader == "")
            {
                (*Resp)["errorno"] = INTERNAL_ERROR;
                (*Resp)["reason"] = "write file failed";
                return;
            }
  1. 檔案命名約定: tmp_時間戳_src.cpp

4.4.2 編譯

        static bool Compile(const std::string& file_name)
        {            
            int pid = fork();
            if(pid > 0)
            {
                //father
                waitpid(pid, NULL, 0);
            }
            else if (pid == 0)
            {
                //child
                //行程程式替換--》 g++ SrcPath(filename) -o ExePath(filename) "-std=c++11"
                int fd = open(CompileErrorPath(file_name).c_str(), O_CREAT | O_WRONLY, 0666);
                if(fd < 0)
                {
                    return false;
                }

                //將標準錯誤(2)重定向為 fd, 標準錯誤的輸出, 就會輸出在檔案當中
                dup2(fd, 2);

                execlp("g++", "g++", SrcPath(file_name).c_str(), "-o", ExePath(file_name).c_str(), "-std=c++11", "-D", "CompileOnline", NULL);

                close(fd);
                //如果替換失敗了, 就直接讓子行程退出了,如果替換成功了, 不會走該邏輯了
                exit(0);
            }
            else
            {
                return false;
            }

            //如果說編譯成功了, 在tmp_file這個檔案夾下, 一定會產生一個可執行程式, 如果
            //當前代碼走到這里,判斷有該可執行程式, 則我們認為g++執行成功了, 否則, 認為執行失敗
            
            struct stat st;
            int ret = stat(ExePath(file_name).c_str(), &st);
            if(ret < 0)
            {
                return false;
            }

            return true;
        }
  1. 創建子行程,子行程進行行程程式替換
  2. 將標準錯誤(2)重定向為 fd,將錯誤進行保存,如果編譯失敗,將其回傳,
  3. 如果說編譯成功了, 在tmp_file這個檔案夾下, 一定會產生一個可執行程式

4.4.3 運行

        static int Run(const std::string& file_name)
        {  
            int pid = fork();
            if(pid < 0)
            {
                return -1;
            }
            else if(pid > 0)
            {
                //father
                int status = 0;
                waitpid(pid, &status, 0);
                return status & 0x7f;
            }
            else
            {
                //注冊一個定時器, alarm
                //[限制運行時間]
                alarm(1);
                //child
                //行程程式替換, 替換編譯創建出來的可執行程式
                //[限制記憶體] // #include <sys/resource.h>
                struct rlimit rlim;
                rlim.rlim_cur = 30000 * 1024;   //3wk
                rlim.rlim_max = RLIM_INFINITY;
                setrlimit(RLIMIT_AS, &rlim);
                int stdout_fd = open(StdoutPath(file_name).c_str(), O_CREAT | O_WRONLY, 0666);
                if(stdout_fd < 0)
                {
                    return -2;
                }
                dup2(stdout_fd, 1);

                int stderr_fd = open(StderrPath(file_name).c_str(), O_CREAT | O_WRONLY, 0666);
                if(stderr_fd < 0)
                {
                    return -2;
                }
                dup2(stderr_fd, 2);
                
                
                execl(ExePath(file_name).c_str(), ExePath(file_name).c_str(), NULL);
                exit(0);
            }

            return 0;
        }
  1. 創建子行程,子行程進行行程程式替換execl,進行運行編譯產生的可執行程式
  2. 將運行結果重定向到stdout_fd
  3. 將標準錯誤重定向到stderr_fd

4.4.4 構造回應

(*Resp)["errorno"] = OK;
            (*Resp)["reason"] = "Compile and Run ok";

            std::string stdout_str;
            FileUtil::ReadFile(StdoutPath(file_nameheader), &stdout_str);
            (*Resp)["stdout"] = stdout_str;

            std::string stderr_str;
            FileUtil::ReadFile(StderrPath(file_nameheader), &stderr_str);
            (*Resp)["stderr"] = stderr_str;
  1. 將所有結果放入json中進行組織,作為出參傳遞給上一層

4.4.5 洗掉臨時檔案

static void Clean(const std::string& filename)
        {
            unlink(SrcPath(filename).c_str());
            unlink(CompileErrorPath(filename).c_str());
            unlink(ExePath(filename).c_str());
            unlink(StdoutPath(filename).c_str());
            unlink(StderrPath(filename).c_str());
        }

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

標籤:其他

上一篇:C++11新特性:移動建構式和移動賦值

下一篇:uniapp(vue通用)整合騰訊位置服務SDK---多平臺小程式通用

標籤雲
其他(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