HNU個人項目互評

来源:https://www.cnblogs.com/meaning-of-fragrance/archive/2023/09/20/17718734.html
-Advertisement-
Play Games

一、前言 這篇博客是對軟體工程導論的個人項目進行互評,項目要求實現一個簡單的中小學數學卷子自動生成程式。我的搭檔謝先衍同學使用Python完成了項目,而我則是使用java。儘管語言不同增加了一定的閱讀成本,但是接觸到另一種新語言並體會編程者發揮語言特性獨特的心得,確實是拓展了眼界。一個項目,最終歸結 ...


一、前言

這篇博客是對軟體工程導論的個人項目進行互評,項目要求實現一個簡單的中小學數學卷子自動生成程式。我的搭檔謝先衍同學使用Python完成了項目,而我則是使用java。儘管語言不同增加了一定的閱讀成本,但是接觸到另一種新語言並體會編程者發揮語言特性獨特的心得,確實是拓展了眼界。一個項目,最終歸結到不同問題,無論用什麼語言,面臨的問題都是一致的,但是語言的特性和編程者的思想卻是和而不同,由此給人以啟發

二、要求

用戶:

小學、初中和高中數學老師。

功能:

1、命令行輸入用戶名和密碼,兩者之間用空格隔開(程式預設小學、初中和高中各三個賬號,具體見附表),如果用戶名和密碼都正確,將根據賬戶類型顯示“當前選擇為XX出題”,XX為小學、初中和高中三個選項中的一個。否則提示“請輸入正確的用戶名、密碼”,重新輸入用戶名、密碼;

2、登錄後,系統提示“準備生成XX數學題目,請輸入生成題目數量(輸入-1將退出當前用戶,重新登錄):”,XX為小學、初中和高中三個選項中的一個,用戶輸入所需出的卷子的題目數量,系統預設將根據賬號類型進行出題。每道題目的操作數在1-5個之間,操作數取值範圍為1-100;

3、題目數量的有效輸入範圍是“10-30”(含10,30,或-1退出登錄),程式根據輸入的題目數量生成符合小學、初中和高中難度的題目的卷子(具體要求見附表)。同一個老師的卷子中的題目不能與以前的已生成的卷子中的題目重覆(以指定文件夾下存在的文件為準,見5);

4、在登錄狀態下,如果用戶需要切換類型選項,命令行輸入“切換為XX”,XX為小學、初中和高中三個選項中的一個,輸入項不符合要求時,程式控制台提示“請輸入小學、初中和高中三個選項中的一個”;輸入正確後,顯示“”系統提示“準備生成XX數學題目,請輸入生成題目數量”,用戶輸入所需出的卷子的題目數量,系統新設置的類型進行出題;

5、生成的題目將以“年-月-日-時-分-秒.txt”的形式保存,每個賬號一個文件夾。每道題目有題號,每題之間空一行;

個人項目9月17日晚上10點以前提交至創新課程管理系統。提交方式:工程文件打包,壓縮包名為“幾班+姓名.rar”。遲交2天及以內者扣分,每天扣20%。遲交2天及以上者0分。

附表-1:賬號密碼

賬戶類型 賬戶 密碼 備註
小學 張三1 123
張三2 123
張三3 123
初中 李四1 123
李四2 123
李四3 123
高中 王五1 123
王五2 123
王五3 123

附表-2:小學、初中、高中題目難度要求

小學 初中 高中
難度要求 +,-,*./ 平方,開根號 sin,cos,tan
備註 只能有+,-,*./和() 題目中至少有一個平方或開根號的運算符 題目中至少有一個sin,cos或tan的運算符

三、功能檢查

背景

環境:Python 3.11.5

軟體:VScode

登錄

命令行輸入用戶名和密碼,兩者之間用空格隔開(程式預設小學、初中和高中各三個賬號,具體見附表),如果用戶名和密碼都正確,將根據賬戶類型顯示“當前選擇為XX出題”,XX為小學、初中和高中三個選項中的一個。否則提示“請輸入正確的用戶名、密碼”,重新輸入用戶名、密碼

成功登錄的情況

測試:錯誤的賬號密碼

在錯誤的情況下,提示“請輸入正確的用戶名、密碼”

測試:不符合格式的賬號密碼

在輸入含多個空格的輸入後,判斷格式錯誤

出題

登錄後,系統提示“準備生成XX數學題目,請輸入生成題目數量(輸入-1將退出當前用戶,重新登錄):”,XX為小學、初中和高中三個選項中的一個,用戶輸入所需出的卷子的題目數量,系統預設將根據賬號類型進行出題。每道題目的操作數在1-5個之間,操作數取值範圍為1-100;

題目數量的有效輸入範圍是“10-30”(含10,30,或-1退出登錄),程式根據輸入的題目數量生成符合小學、初中和高中難度的題目的卷子(具體要求見附表)。同一個老師的卷子中的題目不能與以前的已生成的卷子中的題目重覆

生成的題目將以“年-月-日-時-分-秒.txt”的形式保存,每個賬號一個文件夾。每道題目有題號,每題之間空一行;

成功出題


成功按指定數目出題,並且附帶題號,題與題之間空有一行,在對應的用戶文件夾之下生成以時間為名的題目文件

退出

成功退出到上一頁面

測試:不在範圍內的輸入

不在範圍內的輸入會提示範圍

測試:不規範的輸入

不和規範的輸入會被認為是字元串,進而判斷是否是切換選項

測試:間隔少於1s的快速輸入

存放生成題目的文件是按時間命名的,最低單位是秒。如果快速輸入,1秒中輸入多次呢?


結果是生成了第一次的文件

切換

成功切換

生成了不同類型的題目,併成功存入對應的目錄之下

測試:錯誤輸入

匹配的字元串只有三種,此外的字元串都會觸發提示

總結

功能的測試完備,符合文檔的需求,可以說作者在編寫代碼的時候非常嫻熟精準,落實到了文檔的每一個功能實現之中。雖然在一個測試中出現了點小問題,但考慮到並非文檔指明的需求,無傷大雅。

四、代碼分析

代碼

account.py

#!/usr/bin/env python3.10.9
# -*- coding: utf-8 -*-

import json


class Account(object):
    """Account class.
  
    Attributes:
        account: The account, such as '張三1'.
        password: The password of the account.
        grade: The corresponding grade of the account, to generate exam with
            different difficuty.
    """

    def __init__(self, account, password, grade) -> None:
        """Init the account."""
        self.account = account
        self.password = password
        self.grade = grade


class Accounts(object):
    """Accounts class, to manage accounts.
  
    Attributes:
        accounts: The accounts list, read from accounts.json.
    """

    def __init__(self) -> None:
        """Read accounts from accounts.json to init the accounts list."""
        self.accounts = []
        with open("accounts.json", "r", encoding="utf-8") as f:
            accounts_dir = json.loads(f.read())
        for key in accounts_dir.keys():
            for account in accounts_dir[key]:
                self.accounts.append(
                    Account(account["account"], account["password"], key))

    def check_account(self, account, password) -> str:
        for acc in self.accounts:
            if acc.account == account and acc.password == password:
                return acc.grade
        return None

    def login(self) -> (str, str):
        """Login to the system.
      
        Returns:
            account: The account try to login in.
            grade: The corresponding grade of the account.
        """
        while True:
            account_passwd = input("請輸入用戶名和密碼,兩者之間用空格隔開: ")
            try:
                account, passwd = account_passwd.split(" ")
            except:
                print("格式錯誤!")
                continue
            grade = self.check_account(account, passwd)
            if grade is None:
                print("請輸入正確的用戶名、密碼!")
            else:
                return account, grade

優點

  1. 清晰的代碼結構: 代碼使用了面向對象的方法,使用類來組織數據和功能,使代碼具有良好的結構。
  2. 良好的註釋和文檔字元串: 代碼中有註釋和文檔字元串,解釋了類和方法的功能,這有助於其他開發人員理解代碼。
  3. 封裝性: 帳戶數據和操作被封裝在 Account​ 和 Accounts​ 類中,提高了代碼的可維護性和可擴展性。

缺點

  1. 代碼耦合度高: Accounts​ 類直接依賴於文件 I/O 和 JSON 解析,這導致了代碼的耦合度較高。最好將這些依賴項解耦,以便更容易進行單元測試和擴展。

examgenerator.py

#!/usr/bin/env python3.10.9
# -*- coding: utf-8 -*-

from abc import ABC
from abc import abstractmethod
import os
import random
import re
import time


class ExamGenerator(ABC):
    """Abstract class for exam generator.
  
    Attributes:
        operators: The operators to use.
    """

    def __init__(self) -> None:
        self.operators = ["+", "-", "*", "/"]

    @abstractmethod
    def generate(self, num_range: tuple) -> str:
        """
        Generate a math problem.
        """

    def unary_op(self, op: str, obj: str) -> str:
        """Unary operator such as ^2, sqrt, sin, cos, tan.

        If the operator is ^2, then the operator is behind the object, if the 
        operator is sqrt, sin, cos or tan, then the operator is before the
        object. Otherwise, the operator is not used.
              
        Arguments:
            op: The unary operator to use.
            obj: The object to use the unary operator.
          
        Returns:
            the result str of the object with the unary operator.
        """
        if op == "^2":
            return f"({obj})^2"
        elif op in ["sqrt", "sin", "cos", "tan"]:
            return f"{op}({obj})"
        else:
            return obj
      
    def reverse(self, prob: str) -> str:
        """Reverse the prob str if it's operators number is 2.
      
        Arguments:
            prob: The prob str to reverse.
          
        Returns:
            the reversed prob str.
        """
        pattern = r'\d+'  # Match the number using regular expression
        prob = prob.replace("^2", "^")  # To avoid the ^2 operator being matched
        matches = re.findall(pattern, prob)
        if len(matches) == 2:
            prob = prob.replace(matches[1], matches[0])
            prob = prob.replace(matches[0], matches[1], 1)
        prob = prob.replace("^", "^2")
        return prob

    def check_repeat(self, account: str, prob: str) -> bool:
        """Check if the prob is repeated.
      
        Arguments:
            account: The account using the program.
            prob: The prob str to check.
          
        Returns:
            True if the prob is repeated, otherwise False.
        """
        if os.path.exists(f"exams/{account}"):
            for file in os.listdir(f"exams/{account}"):
                with open(f"exams/{account}/{file}", "r") as f:
                    lines = f.readlines()[:-1:2]  # Remove the '\n'
                    for line in lines:
                        if (prob == line[4:-1] or  # Remove the prob index and space and '\n'
                            self.reverse(prob) == line[4:-1]):
                            return True
        return False

    def save_probs(self, account: str, probs_num: int) -> None:
        """Save the probs to the file.
      
        Arguments:
            account: The account using the program.
            probs_num: The number of probs to generate.
          
        Returns:
            None.
        """
        probs = []
        for i in range(probs_num):
            while True:
                prob = f"{self.generate()}="
                if ((prob not in probs) and
                    (not self.check_repeat(account, prob))):
                    break
            probs.append(prob)
        if not os.path.exists(f"exams/{account}"):
            os.mkdir(f"exams/{account}")
        # filename: 年-月-日-時-分-秒.txt
        filename = f"{time.strftime('%Y-%m-%d-%H-%M-%S', time.localtime())}.txt"
        with open(f"exams/{account}/{filename}", "w") as f:
            index = 1
            for prob in probs:
                space = "  " if index < 10 else " "  # Add space to align the index
                f.write(f"{index}.{space}{prob}\n\n")
                index += 1


class ExamGenerator1(ExamGenerator):
    """Exam generator for primary school."""

    def __init__(self) -> None:
        super().__init__()

    def generate(self, nums_range=(1, 5)) -> str:
        op_num = random.randint(nums_range[0], nums_range[1])
        op = random.choice(self.operators)
        if op_num == 1:
            if nums_range[1] == 5:
                return self.generate((1, 5))  # If the result is only 1 number, generate again
            else:
                return str(random.randint(1, 100))
        elif op_num == 2:
            left_op = self.generate((1, 1))
            right_op = self.generate((1, 1))
            if random.random() < 0.45 and nums_range[1] != 5:
                return f"({left_op}{op}{right_op})"  # 45% to add brackets
            else:
                return f"{left_op}{op}{right_op}"
        else:
            left_op = self.generate((1, op_num // 2))
            right_op = self.generate((1, op_num - op_num // 2))
            if random.random() < 0.45 and nums_range[1] != 5:
                return f"({left_op}{op}{right_op})"
            else:
                return f"{left_op}{op}{right_op}"


class ExamGenerator2(ExamGenerator):
    """Exam generator for junior high school."""

    def __init__(self) -> None:
        super().__init__()
        self.operators.extend(["^2", "sqrt"])

    def generate(self, nums_range=(1, 5)) -> str:
        op_num = random.randint(nums_range[0], nums_range[1])
        op = random.choice(self.operators)
        op1 = random.choice(self.operators[:4])
        if op_num == 1:
            if op in self.operators[4:] and random.random() < 0.66:
                result = self.unary_op(op, str(random.randint(1, 100)))
            else:
                result = str(random.randint(1, 100))
        elif op_num == 2:
            left_op = self.generate((1, 1))
            right_op = self.generate((1, 1))
            if op in ["^2", "sqrt"]:
                return self.unary_op(op, f"{left_op}{op1}{right_op}")
            else:
                result = f"{left_op}{op}{right_op}"
        else:
            left_op = self.generate((1, op_num // 2))
            right_op = self.generate((1, op_num - op_num // 2))
            if op in ["^2", "sqrt"]:
                result = self.unary_op(op, f"{left_op}{op1}{right_op}")
            else:
                result = f"{left_op}{op}{right_op}"
        if (nums_range[1] == 5 and result.find("sqrt") == -1 and 
            result.find("^2") == -1):  # If the result don't contain sqrt or ^2, generate again
            return self.generate((1, 5))
        return result


class ExamGenerator3(ExamGenerator):
    """Exam generator for senior high school."""

    def __init__(self) -> None:
        super().__init__()
        self.operators.extend(["^2", "sqrt", "sin", "cos", "tan"])

    def generate(self, nums_range=(1, 5)) -> str:
        op_num = random.randint(nums_range[0], nums_range[1])
        op = random.choice(self.operators)
        op1 = random.choice(self.operators[:4])
        if op_num == 1:
            if op in self.operators[4:] and random.random() < 0.66:
                result = self.unary_op(op, str(random.randint(1, 100)))
            else:
                result = str(random.randint(1, 100))
        elif op_num == 2:
            left_op = self.generate((1, 1))
            right_op = self.generate((1, 1))
            if op in self.operators[4:]:
                result = self.unary_op(op, f"{left_op}{op1}{right_op}")
            else:
                result = f"{left_op}{op}{right_op}"
        else:
            left_op = self.generate((1, op_num // 2))
            right_op = self.generate((1, op_num - op_num // 2))
            if op in self.operators[4:]:
                result = self.unary_op(op, f"{left_op}{op1}{right_op}")
            else:
                result = f"{left_op}{op}{right_op}"
        if (nums_range[1] == 5 and result.find("sin") == -1 and 
            result.find("cos") == -1 and result.find("tan") == -1):  # If the result don't contain sin, cos or tan, generate again
            return self.generate((1, 5))
        return result

優點

1 抽象類和多態: ExamGenerator​ 是一個抽象基類,定義了一個抽象方法 generate​,並且在子類中進行了實現。這利用了Python的多態性,允許不同子類提供不同的實現

缺點

  1. 部分魔法數值: 代碼中出現了一些魔法數值,如 0.45、0.66 等,這些值沒有明確的解釋和註釋,可能會導致代碼的可讀性和可維護性降低。最好將這些數值提取為常量,並提供相關註釋。

  2. 文件操作錯誤處理不足: 代碼中的文件操作沒有足夠的錯誤處理機制,如果文件無法創建或寫入,代碼會引發異常而無法處理。

  3. 生成題目的方法命名不一致: 不同級別的生成器子類中的 generate​ 方法簽名不一致,這可能會導致混淆和錯誤。最好統一方法名。

  4. 未考慮邊界情況: 代碼中未考慮一些邊界情況,如生成的數值範圍、一元運算符的頻率等,這可能導致生成的題目不夠多樣化或有問題。

main.py

#!/usr/bin/env python3.10.9
# -*- coding: utf-8 -*-

from account import Accounts
from examgenerator import ExamGenerator
from examgenerator import ExamGenerator1
from examgenerator import ExamGenerator2
from examgenerator import ExamGenerator3


def Exam_generator(grade: str) -> ExamGenerator:
    """Return the corresponding ExamGenerator according to the grade.
  
    Arguments:
        grade: The grade of the account.
      
    Returns:
        The corresponding ExamGenerator.
    """
    if grade == "小學":
        return ExamGenerator1()
    elif grade == "初中":
        return ExamGenerator2()
    elif grade == "高中":
        return ExamGenerator3()
    else:
        return ExamGenerator()


def main():
    """Main function of the program."""
    accounts = Accounts()
    account, grade = accounts.login()

    exam_generator = Exam_generator(grade)

    prob_num = 0
    while True:
        try:
            prob_num = input(
                f"準備生成{grade}數學題目,請輸入生成題目數量(輸入-1將退出當前用戶重新登錄,輸入切換為XX可以切換身份): ")
            if prob_num.startswith("切換為"):
                if prob_num[3:] in ["小學", "初中", "高中"]:
                    grade = prob_num[3:]
                    exam_generator = Exam_generator(grade)
                else:
                    print("請輸入小學、初中和高中三個選項中的一個!")
            elif int(prob_num) >= 10 and int(prob_num) <= 30:
                exam_generator.save_probs(account, int(prob_num))
                print(f"{grade}數學題目生成完畢,已保存到exams/{account}目錄下!")
            elif int(prob_num) == -1:
                account, grade = accounts.login()
                exam_generator = Exam_generator(grade)
            else:
                print("請輸入10-30之間的數字")
        except ValueError:
            print("請輸入10-30之間的數字或切換為小學、初中和高中三個選項中的一個!")


if __name__ == '__main__':
    main()

accounts.json

{
    "小學": [
        {
            "account": "張三1",
            "password": "123"
        },
        {
            "account": "張三2",
            "password": "123"
        },
        {
            "account": "張三3",
            "password": "123"
        }
    ],
    "初中": [
        {
            "account": "李四1",
            "password": "123"
        },
        {
            "account": "李四2",
            "password": "123"
        },
        {
            "account": "李四3",
            "password": "123"
        }
    ],
    "高中": [
        {
            "account": "王五1",
            "password": "123"
        },
        {
            "account": "王五2",
            "password": "123"
        },
        {
            "account": "王五3",
            "password": "123"
        }
    ]
}

優點

模塊化設計: 代碼使用了模塊化的設計,將不同功能的代碼分別放在了不同的模塊(account​ 和 examgenerator​)中,提高了代碼的可維護性和可重用性。

Google代碼規範

大體遵守了Google Python代碼規範:

1. 模塊和函數命名

代碼中的模塊和函數命名在大多數情況下是清晰和符合規範的。例如,Accounts​ 類和 ExamGenerator​ 抽象類的命名是符合規範的。

2. 函數參數類型註釋

在一些方法中,使用了參數類型的註釋,這有助於理解參數的預期類型。這是符合Google代碼規範的一項實踐。

def generate(self, num_range: tuple) -> str:
    """
    Generate a math problem.
    """
    ...

def check_repeat(self, account: str, prob: str) -> bool:
    """Check if the prob is repeated.
  
    Arguments:
        account: The account using the program.
        prob: The prob str to check.
    
    Returns:
        True if the prob is repeated, otherwise False.
    """
    ...

3. 異常處理

碼中有一些異常處理,這是符合Google代碼規範的一項實踐。異常處理有助於處理潛在的錯誤情況。

try:
    # 異常處理代碼
except ValueError:
    print("請輸入10-30之間的數字或切換為小學、初中和高中三個選項中的一個!")

也有值得改進的地方:

文檔字元串(Docstrings)

代碼中缺少文檔字元串(Docstrings)。文檔字元串是對模塊、類、函數和方法功能的詳細描述,以及參數和返回值的說明。這是Google代碼規範強烈鼓勵的一項實踐,有助於提高代碼的可讀性和可維護性。

class Account(object):
    """Account class.
  
    Attributes:
        account: The account, such as '張三1'.
        password: The password of the account.
        grade: The corresponding grade of the account, to generate exam with
            different difficuty.
    """
    ...

class Accounts(object):
    """Accounts class, to manage accounts.
  
    Attributes:
        accounts: The accounts list, read from accounts.json.
    """
    ...

五、總結

第一次寫博客評價他人的項目,我深切感受到了寫代碼還得是一個團隊活動,人和人之間交流彼此的意見和經驗,以求共同進步,這樣的學習方式更加高效。搭檔的項目寫得很好,相比我寫的java而言,代碼更簡練優美,希望大家有所啟發。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 查詢SQL語句執行頻率 查詢 mysql 服務啟動時長 SHOW STATUS LIKE 'uptime'; 下列輸出表示服務啟動了276324秒 + + + | Variable_name | Value | + + + | Uptime | 276324 | + + + 查詢全局SQL執行的頻率 ...
  • 一、前言 MySQL的服務實現通過後臺多個線程、記憶體池、文件交互來實現對外服務的,不同線程實現不同的資源操作,各個線程相互協助,共同來完成資料庫的服務。MySQL常用的後臺線程概括如下,分為Master Thread,IO Thread,Purge Thread,Page Cleaner Threa ...
  • 眾所周知,在現實世界中,每一個資源都有其提供能力的最大上限,當單一資源達到最大上限後就得讓多個資源同時提供其能力來滿足使用方的需求。同理,在電腦世界中,單一資料庫資源不能滿足使用需求時,我們也會考慮使用多個資料庫同時提供服務來滿足需求。當使用了多個資料庫來提供服務時,最為關鍵的點是如何讓每一個數據 ...
  • web前端JavaScript交互 點擊事件 意義: JavaScript中的點擊事件是指當用戶在頁面上點擊某個元素時觸發的事件。這個事件可以用於執行各種操作,如改變元素的樣式、修改頁面內容等。這是Web應用程式中最常用 的交互方式之一,允許用戶與網頁進行交互,提高用戶體驗。 案例: 隨機點名器 知 ...
  • 工作中經常遇到按照指定格式的時間進行展示。可參考以下腳本邏輯滿足需求 Date.prototype.PtTimeByFormat = function (fmt){ var o = { "M+": this.getMonth() + 1, //月份 "d+": this.getDate(), //日 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言 可選鏈運算符(?.),大家都很熟悉了,直接看個例子: const result = obj?.a?.b?.c?.d 很簡單例子,上面代碼?前面的屬性如果是空值(null或undefined),則result值是undefined,反 ...
  • import React, { useEffect, useState } from 'react'; hook 是react 16.8的新增特性 ,他可以讓你不在編寫class的情況下shiystate以及react的特性 Hooks的出現,首先解決了以下問題: 告別了令人疑惑的生命周期 告別類組 ...
  • 設計模式 學習推薦設計模式目錄:22種設計模式 (refactoringguru.cn) 圖說設計模式 — Graphic Design Patterns (design-patterns.readthedocs.io) UML類圖初見 什麼是統一建模語言(UML)? (visual-paradig ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...