【演算法】友誼與雪花的舞動,腳本解析器原理

来源:https://www.cnblogs.com/lan80/archive/2023/11/13/17825345.html
-Advertisement-
Play Games

目錄 Welcome to YARP - 1.認識YARP並搭建反向代理服務 Welcome to YARP - 2.配置功能 2.1 - 配置文件(Configuration Files) 2.2 - 配置提供者(Configuration Providers) 2.3 - 配置過濾器(Confi ...


在11月的下雪天,小悅身處於溫暖的辦公室中,窗外的雪花在燈光下翩翩起舞。她盯著電腦屏幕,不經意間,一個熟悉的身影從辦公室門口處經過,吸引了她的目光。那個人看上去很像是一個女孩,名叫蘇菲,是她在大學時期遇到的國外交換生。

小悅的心跳加速,她有些不敢相信自己的眼睛。在她的記憶中,蘇菲是一個溫柔、聰明且樂於助人的女孩。她們曾經一起上過電腦科學課,蘇菲對數學和編程的熱愛給小悅留下了深刻的印象。在課程中,蘇菲表現出了非凡的編程天賦和扎實的技術功底,她的編程能力讓小悅敬佩不已。

小悅忍不住站起來,快步走向那個人。她輕輕地拍了拍她的肩膀,問道:“蘇菲?”

那個女孩轉過身來,露出了一張熟悉的面孔。她的眼睛閃爍著聰慧的光芒,嘴角掛著一抹微笑。她驚喜地喊道:“小悅?”

兩人緊緊地擁抱在一起,重逢的喜悅讓她們感到溫暖。她們來到樓下的咖啡廳,開始回憶起大學時期的時光,談論著彼此的生活和工作。

小悅感到非常驚喜,沒想到蘇菲會來到她的辦公樓。蘇菲也並不知道小悅在這裡工作。或許是命運的安排,讓她們再次相遇。小悅心想,一定要好好和蘇菲聊聊天,暢談彼此的近況,重溫那段美好的大學時光。

小悅想起了那次電腦科學課的上機考試。她需要實現一個計算器表達式演算法。小悅在考試前嘗試著自己實現了一個多項式解析式演算法,但是效果並不甚理想。這個問題讓她感到很困惑,不知道如何著手。就在這時,蘇菲主動幫助了她。

蘇菲耐心地解釋瞭如何分析問題、拆分令牌、使用逆波蘭表達式演算法進行計算,以及如何使用棧來處理尾碼表達式。她的思路清晰、有條理,讓小悅瞬間明白了問題的解決方法。

在蘇菲的幫助下,小悅成功地解決了問題,並且在考試中取得了優異的成績。她一直感激蘇菲的幫助和鼓勵。

回憶起那段經歷,小悅不禁感慨萬分。她對蘇菲說:“謝謝你,蘇菲。你的幫助和支持讓我變得更加自信和勇敢。”

蘇菲微笑著說:“我們是朋友,小悅。無論何時何地,我都會儘力幫助你。”

她們聊了很久,談論著過去的點點滴滴和現在的變化。畢業後,蘇菲選擇繼續深造,攻讀電腦科學碩博學位。在國外留學期間,她積累了豐富的經驗和技能,志向是成為一名電腦科學家。然而,儘管小悅和蘇菲在大學時期形影不離,但畢業後兩人便各奔東西,相距太遠,漸漸失去了聯繫。她們還計划著再次回到校園,去湖心島看看那個曾經給她們帶來無限回憶的地方。

在分別前,小悅緊緊地擁抱了蘇菲一次。她心裡默默地想:謝謝你,蘇菲,謝謝你陪我度過了那段美好的時光。我會永遠珍惜這份友誼。

隨著時間的推移,雖然小悅和蘇菲的聯繫越來越少。但是她們的友誼和回憶永遠留在彼此的心中。在這個下雪的日子里,小悅重新與蘇菲相遇,讓她回憶起了那些青蔥校園歲月和湖心島的甜蜜時光。她感到無比幸福和感激,因為她知道這些美好的回憶將永遠伴隨著她走過人生的旅程。即使未來還有更多的挑戰等待著她,小悅也相信自己的能力能夠剋服一切困難。


當時小悅和蘇菲面對的考試題目為:要求計算一個包含數字和運算符的字元串的結果,包括加減乘除和冪運算,還有括弧。數字可以是整數或小數,字元串中可以有空格。要求處理字元串中的所有運算符和數字,並計算出最終的結果。

示例:3 * (4 +       (2 / 3) * 6 - 5)=9;

123      -( 4^ (       3 -   1) * 8 - 8      /(     1 + 1 ) *(3 -1) )=3; 


演算法實現1:

  1 using System;
  2 using System.Collections.Generic;
  3 
  4 public static class Edm {
  5 
  6   public static double calculate(string input)
  7     {
  8         // 從輸入中移除空格
  9         input = input.Replace(" ", "");
 10 
 11         // 將輸入轉換為令牌列表
 12         List<string> tokens = new List<string>();
 13         string currentToken = "";
 14         foreach (char c in input) {
 15             if (char.IsDigit(c) || c == '.') { // 如果字元是數字或小數點
 16                 currentToken += c; // 將字元添加到當前令牌中
 17             } else {
 18                 if (currentToken != "") { // 如果當前令牌不為空
 19                     tokens.Add(currentToken); // 將當前令牌添加到令牌列表中
 20                     currentToken = ""; // 重置當前令牌
 21                 }
 22                 tokens.Add(c.ToString()); // 將字元轉換為字元串並添加到令牌列表中
 23             }
 24         }
 25         if (currentToken != "") { // 如果當前令牌不為空
 26             tokens.Add(currentToken); // 將當前令牌添加到令牌列表中
 27         }
 28 
 29         // 使用逆波蘭演算法評估令牌
 30         Stack<string> operatorStack = new Stack<string>(); // 操作符棧
 31         Queue<string> outputQueue = new Queue<string>(); // 輸出隊列
 32         foreach (string token in tokens) {
 33             if (double.TryParse(token, out double num)) { // 如果令牌可以轉換為雙精度浮點數
 34                 outputQueue.Enqueue(num.ToString()); // 將數字轉換為字元串並添加到輸出隊列中
 35             } else if (IsOperator(token)) { // 如果令牌是操作符
 36                 while (operatorStack.Count > 0 && IsOperator(operatorStack.Peek()) && GetPrecedence(token) <= GetPrecedence(operatorStack.Peek())) {
 37                     outputQueue.Enqueue(operatorStack.Pop()); // 將操作符棧中的操作符彈出並添加到輸出隊列中
 38                 }
 39                 operatorStack.Push(token); // 將操作符添加到操作符棧中
 40             } else if (token == "(") { // 如果令牌是左括弧
 41                 operatorStack.Push(token); // 將左括弧添加到操作符棧中
 42             } else if (token == ")") { // 如果令牌是右括弧
 43                 while (operatorStack.Count > 0 && operatorStack.Peek() != "(") {
 44                     outputQueue.Enqueue(operatorStack.Pop()); // 將操作符棧中的操作符彈出並添加到輸出隊列中
 45                 }
 46                 if (operatorStack.Count == 0) {
 47                     throw new Exception("Mismatched parentheses"); // 拋出異常,提示括弧不匹配
 48                 }
 49                 operatorStack.Pop(); // 彈出左括弧
 50             } else {
 51                 throw new Exception("Invalid token: " + token); // 拋出異常,提示令牌無效
 52             }
 53         }
 54         while (operatorStack.Count > 0) {
 55             if (operatorStack.Peek() == "(") {
 56                 throw new Exception("Mismatched parentheses"); // 拋出異常,提示括弧不匹配
 57             }
 58             outputQueue.Enqueue(operatorStack.Pop()); // 將操作符棧中的操作符彈出並添加到輸出隊列中
 59         }
 60 
 61         // 評估尾碼表達式
 62         Stack<double> operandStack = new Stack<double>(); // 操作數棧
 63         foreach (string token in outputQueue) {
 64             if (double.TryParse(token, out double num)) { // 如果令牌可以轉換為雙精度浮點數
 65                 operandStack.Push(num); // 將數字添加到操作數棧中
 66             } else if (IsOperator(token)) { // 如果令牌是操作符
 67                 double b = operandStack.Pop();
 68                 double a = operandStack.Pop();
 69                 double result = EvaluateOperator(token, a, b); // 計算操作符對應的結果
 70                 operandStack.Push(result); // 將計算結果壓入操作數棧中
 71             } else {
 72                 throw new Exception("Invalid token: " + token); // 拋出異常,提示令牌無效
 73             }
 74         }
 75         if (operandStack.Count != 1) {
 76             throw new Exception("Invalid expression"); // 拋出異常,提示表達式無效
 77         }
 78         return operandStack.Pop(); // 返回操作數棧中唯一的元素作為計算結果
 79     }
 80 
 81     static bool IsOperator(string token) {
 82         return token == "+" || token == "-" || token == "*" || token == "/" || token == "^"; // 判斷一個字元串是否為操作符(+、-、*、/、^)
 83     }
 84     
 85     static int GetPrecedence(string op) {// 獲取操作符優先順序
 86         if (op == "^") {
 87             return 3;
 88         } else if (op == "*" || op == "/") {
 89             return 2;
 90         } else if (op == "+" || op == "-") {
 91             return 1;
 92         } else {
 93             return 0;
 94         }
 95     }
 96 
 97    // 計算兩個操作數和一個操作符的結果
 98     static double EvaluateOperator(string op, double a, double b) {
 99         if (op == "+") {
100             return a + b;
101         } else if (op == "-") {
102             return a - b;
103         } else if (op == "*") {
104             return a * b;
105         } else if (op == "/") {
106             return a / b;
107         } else if (op == "^") {
108             return Math.Pow(a, b);
109         } else {
110             throw new Exception("Invalid operator: " + op);
111         }
112     }
113 }

這段代碼實現了一個表達式計算器,能夠接受包含基本數學運算的字元串表達式,並返回計算結果。

  1. 導入了System和System.Collections.Generic命名空間,用於使用標準的數據結構和功能。

  2. 定義了一個靜態類Edm,其中包含一個名為calculate的靜態方法,該方法接受一個字元串輸入並返回一個雙精度浮點數。

  3. calculate方法首先去除輸入中的空格,然後將輸入轉換為一個令牌列表(tokens)。

  4. 使用逆波蘭演算法(shunting yard algorithm)對令牌進行評估。它使用了操作符棧(operatorStack)和輸出隊列(outputQueue)來對錶達式進行處理。

  5. 在這段代碼中,我們使用了操作符棧和輸出隊列來處理給定的令牌(tokens)。

    1. 首先,我們遍歷令牌數組,對每個令牌進行處理。
    2. 如果令牌是一個數字,我們將其轉換為字元串並添加到輸出隊列中。
    3. 如果令牌是一個操作符,我們需要根據操作符的優先順序來決定其在輸出隊列中的位置。我們將當前操作符與操作符棧頂的操作符進行比較,如果當前操作符的優先順序小於或等於棧頂操作符的優先順序,則將棧頂操作符彈出並添加到輸出隊列中,直到滿足條件為止,然後將當前操作符壓入操作符棧中。
    4. 如果令牌是左括弧,我們直接將其壓入操作符棧中。
    5. 如果令牌是右括弧,我們需要將操作符棧中的操作符彈出並添加到輸出隊列中,直到遇到左括弧為止。如果在彈出操作符時,操作符棧為空,或者沒有遇到左括弧,就會拋出異常提示括弧不匹配。
    6. 如果令牌不是數字、操作符、左括弧或右括弧,則拋出異常提示令牌無效。
    7. 最後,處理完所有令牌後,將操作符棧中剩餘的操作符依次彈出並添加到輸出隊列中。如果在此過程中遇到左括弧,也會拋出異常提示括弧不匹配。

    這樣,我們可以將中綴表達式轉換為尾碼表達式,並且在這個過程中處理了操作符的優先順序和括弧的匹配關係。

  6. 對尾碼表達式進行評估。這部分代碼使用了操作數棧(operandStack)來計算尾碼表達式的值。

  7. 評估尾碼表達式的詳細解釋:

    1. 創建一個空的操作數棧,用於存儲操作數。
    2. 遍歷尾碼表達式中的每個令牌。
    3. 如果令牌可以轉換為雙精度浮點數,則將其壓入操作數棧中。
    4. 如果令牌是操作符,則從操作數棧中彈出兩個操作數(假設為a和b),然後根據該操作符對這兩個操作數進行計算,得到結果。
    5. 將計算得到的結果壓入操作數棧中。
    6. 如果令牌不是數字也不是操作符,則拋出異常提示令牌無效。
    7. 完成對所有令牌的處理後,操作數棧中應該只剩下一個元素,即為最終的計算結果。如果操作數棧中的元素不止一個,說明表達式無效,拋出異常。
  8. IsOperator方法用於檢查一個字元串是否為操作符(+、-、*、/、^)。

  9. GetPrecedence方法用於獲取操作符的優先順序。

  10. EvaluateOperator方法用於計算兩個操作數和一個操作符的結果。


 逆波蘭表達式演算法由荷蘭電腦科學家Edsger Dijkstra在1960年代實現,它也是ALGOL60語言的基礎演算法,因為在ALGOL60上做出原理性貢獻,Dijkstra獲得了1972年的圖靈獎。而該演算法的名稱來源於其發明者,波蘭數學家Jan Łukasiewicz由1929年提出此數學方法。

逆波蘭表達式演算法的數學原理可以通過棧的數據結構來解釋。我們以中綴表達式 "3 + 4 * 2" 為例進行解釋。

  1. 遍歷中綴表達式中的每個令牌(數字、操作符)。
  2. 如果令牌是數字,則直接將其添加到輸出隊列中。
  3. 如果令牌是操作符: a. 如果操作符棧為空,或者操作符棧頂的操作符優先順序小於當前操作符,則將當前操作符壓入操作符棧中。 b. 如果操作符棧頂的操作符優先順序大於等於當前操作符,則將操作符棧頂的操作符彈出並添加到輸出隊列中,直到滿足條件為止,然後將當前操作符壓入操作符棧中。
  4. 處理完所有令牌後,將操作符棧中剩餘的操作符依次彈出並添加到輸出隊列中。

以中綴表達式 "3 + 4 * 2" 為例,通過上述步驟,我們可以將中綴表達式轉換為尾碼表達式 "3 4 2 * +"。

中綴表達式轉換為尾碼表達式的過程是為了更方便地讓電腦進行數學表達式的計算。尾碼表達式(也稱為逆波蘭表達式)具有以下優點:

  1. 沒有括弧:尾碼表達式不需要括弧來表示運算的優先順序,因此避免了括弧所帶來的歧義和複雜性。

  2. 易於計算:尾碼表達式的計算可以通過簡單的迭代演算法來實現,不需要遞歸或者棧來保存中間結果,這使得計算更加高效。

對於尾碼表達式 "3 4 2 * +",我們可以按照以下步驟進行計算:

  1. 從左到右掃描尾碼表達式,遇到操作數就將其壓入棧中。
  2. 遇到操作符時,從棧中彈出相應數量的操作數進行計算,並將計算結果壓入棧中。
  3. 最終棧中剩下的元素就是整個表達式的計算結果。

對於尾碼表達式 "3 4 2 * +",計算過程如下:

  • 遇到 "3",壓入棧中
  • 遇到 "4",壓入棧中
  • 遇到 "2",壓入棧中
  • 遇到 "*",彈出棧頂的兩個元素(4和2),計算結果為8,將結果壓入棧中
  • 遇到 "+",彈出棧頂的兩個元素(3和8),計算結果為11,將結果壓入棧中
  • 最終棧中剩下的唯一元素就是整個表達式的計算結果,即11。

因此,尾碼表達式的轉換和計算可以使數學表達式在電腦中的處理更加簡單和高效。

逆波蘭表達式演算法之所以受到廣泛應用,是因為它能夠有效地解決中綴表達式計算的問題。中綴表達式需要使用括弧來確定操作符的優先順序,而逆波蘭表達式則不需要,因為它使用尾碼表示法,操作符的優先順序可以通過操作符的順序來確定。

逆波蘭表達式演算法在計算器、編譯器、解釋器等領域都有廣泛應用。在計算器中,用戶輸入的數學算式,即中綴表達式通常需要轉換為逆波蘭表達式才能進行計算。在編譯器和sql解釋器中,逆波蘭表達式演算法可以用於計算sql表達式的值,以及將sql表達式轉換為可執行代碼。


演算法實現2:

 1 public static double calculate(string s){
 2      s=s.Replace(" ","");
 3      if (Regex.Match(s,"^[(][\\d\\.\\+\\-\\*/\\^]+[)]$").Success) s=Regex.Replace(s,"^[(]|[)]$","");
 4      if (Regex.Match(s,"^-?[\\d\\.]+$").Success) return double.Parse(s);//檢測到單個的正負數字,直接返回值
 5      if (Regex.Match(s,"^\\d[\\d+-\\.]+$").Success) return Regex.Matches(s,"-[\\d\\.]+|(?<=^|[+])[\\d\\.]+").OfType<Match>().Sum(x=>double.Parse(x.Value)); //檢測到只包含連續+-符號的算式,返回其運算值
 6      if (Regex.Match(s,"^[\\d\\.]+[*/\\^][\\d\\.]+$").Success) { //檢測到兩個數的乘除次方,返回其運算值
 7        var tmp=Regex.Split(s,"[*/\\^]").Select(double.Parse).ToArray();
 8        return s.Contains('*') ? tmp[0]*tmp[1] : s.Contains('/') ? tmp[0]/tmp[1] : Math.Pow(tmp[0],tmp[1]);
 9      }
10      if (s.Contains('(')) {  //檢測到括弧,先運算一個括弧單元,然後遞歸
11        var tmp=Regex.Match(s,"[(][\\d\\.\\+\\-\\*/\\^]+[)]").Value;
12        return calculate(s.Substring(0,s.IndexOf(tmp))+calculate(tmp)+s.Substring(s.IndexOf(tmp)+tmp.Length));
13      }
14      if (s.Contains('^')) {  //檢測次方符號,優先運算
15        var tmp=Regex.Match(s,"[\\d\\.]+[\\^][\\d\\.]+").Value;
16        return calculate(s.Substring(0,s.IndexOf(tmp))+calculate(tmp)+s.Substring(s.IndexOf(tmp)+tmp.Length));
17      }
18      if (s.Contains('*')||s.Contains('/')) {  //檢測乘除符號,優先運算
19        var tmp=Regex.Match(s,"[\\d\\.]+[\\*/][\\d\\.]+").Value;
20        return calculate(s.Substring(0,s.IndexOf(tmp))+calculate(tmp)+s.Substring(s.IndexOf(tmp)+tmp.Length));
21      }
22      return 0;
23    }

演算法2是一個用於計算數學表達式的演算法,使用了正則表達式和遞歸的思想,以及一些基本的操作符處理和字元串處理方法。這種類型的演算法通常用於電腦科學和編程中,以便對數學表達式進行求值。

在工作之餘,小悅回想起大學時期的那次考試。當時,她運用逆波蘭表達式演算法成功解決了算式計算器問題。而現在,憑藉著工作經驗的積累,她採用自己設計的方法——正則表達式和遞歸演算法。

小悅運用正則表達式的強大功能,將輸入的算式進行精準的解析和拆分。她巧妙地運用正則表達式的分組和捕獲功能,將算式拆分為一個個獨立的子表達式。然後,她利用遞歸演算法的特性,對這些子表達式進行遞歸計算。這種方法的優點在於,它能夠處理包含括弧、運算符優先順序等複雜元素在內的算式,同時還能保證計算過程的靈活性和高效性。

但演算法2對於更複雜的腳本語言解釋器來說,對於更複雜的表達式,比如函數調用、變數賦值等,就無法處理,可能需要考慮更多的功能和錯誤處理機制。如果需要實現一個更完整的腳本解釋器,還是需要通過演算法1進行擴展。


演算法1和演算法2的比較如下:

演算法1優點:

  1. 逆波蘭表達式演算法不需要使用括弧,因此可以減少括弧匹配的複雜性。
  2. 逆波蘭表達式演算法可以直接利用棧結構進行計算,簡化了中綴表達式轉換為尾碼表達式的過程。
  3. 逆波蘭表達式演算法可以用於解釋各種類型的語言和數據格式,從而實現各種解釋器。這些解釋器可以用於實現各種應用,例如命令行工具、頁面腳本語言、配置文件解析器、sql資料庫查詢解析器等。

演算法1缺點:

  1. 中綴表達式轉換為尾碼表達式的過程可能較為複雜,需要額外的轉換步驟。
  2. 逆波蘭表達式演算法需要額外的數據結構(棧)來進行計算,可能需要更多的記憶體空間。

演算法2優點:

  1. 演算法2可以直接對中綴表達式進行遞歸計算,不需要額外的轉換步驟。
  2. 演算法2可以直接處理括弧、乘除次方等運算符,邏輯相對清晰。

演算法2缺點:

  1. 演算法2中使用了正則表達式進行匹配,可能在處理複雜表達式時效率較低。
  2. 演算法2在處理嵌套括弧的情況時可能需要多次遞歸計算,效率較低。
  3. 演算法2對於更複雜的腳本語言解釋器來說,不容易擴展和優化。

綜合來看,演算法1在處理簡單表達式時可能更加高效,而演算法2在處理複雜數學表達式時可能更加直觀和易於理解。具體選擇哪種演算法取決於實際需求和對演算法的偏好。


測試用例:

  1  using NUnit.Framework;
  2   using System;
  3   [TestFixture]
  4   public class CalculatorTest
  5   {
  6     public bool close(double a, double b)
  7     {
  8       if (Math.Abs(a-b)<0.000000001) return true;
  9       return false;
 10     }
 11     [Test]
 12     public void EasyTests()
 13     {
 14       Assert.AreEqual(true, close(Edm.calculate("3 + 5"), 8));
 15       Assert.AreEqual(true, close(Edm.calculate("5 + 41"), 46));
 16       Assert.AreEqual(true, close(Edm.calculate("5 - 3"), 2));
 17       Assert.AreEqual(true, close(Edm.calculate("5 - 5"), 0));
 18       Assert.AreEqual(true, close(Edm.calculate("3 * 5"), 15));
 19       Assert.AreEqual(true, close(Edm.calculate("2 * 23"), 46));
 20       Assert.AreEqual(true, close(Edm.calculate("123 / 3"), 41));
 21       Assert.AreEqual(true, close(Edm.calculate("22 / 1"), 22));
 22     }
 23     
 24     [Test]
 25     public void MediumTests()
 26     {
 27       Assert.AreEqual(true, close(Edm.calculate("3 + 5 * 2"), 13));
 28       Assert.AreEqual(true, close(Edm.calculate("5 - 3 * 8 / 8"), 2));
 29       Assert.AreEqual(true, close(Edm.calculate("6*(2 + 3)"), 30));
 30       Assert.AreEqual(true, close(Edm.calculate("2 ^ 5"), 32));
 31       Assert.AreEqual(true, close(Edm.calculate("5 ^0"), 1));
 32       Assert.AreEqual(true, close(Edm.calculate("23.2- 15.2"), 8));
 33       Assert.AreEqual(true, close(Edm.calculate("22 / 5"), 22.0/5));
 34     }
 35     
 36     [Test]
 37     public void HardTests()
 38     {
 39       Assert.AreEqual(true, close(Edm.calculate("3 * (4 +       (2 / 3) * 6 - 5)"), 9));
 40       Assert.AreEqual(true, close(Edm.calculate("123      -( 4^ (       3 -   1) * 8 - 8      /(     1 + 1 ) *(3 -1) )"), 3));
 41       Assert.AreEqual(true, close(Edm.calculate("4 + 2 * ( (226 - (5 * 3) ^ 2) ^ 2 + (10.7 - 7.4) ^ 2 - 6.89)"),14));
 42       Assert.AreEqual(true, close(Edm.calculate(" (226 - (5 * 3) ^ 2) ^ 2"),1));
 43     }
 44     
 45     [Test]
 46     public void RandomTest() //some really simple random tests just to get this older kata out of beta (@smile67, 26.10.2017)
 47     {
 48        string [] f=new string []{"a * b   -c","a + b/   (b +  c)","(a   + c+  b)  * b  *a"};
 49        Random rnd = new Random();
 50        for (int i=1;i<51;i++) {
 51           int r= rnd.Next(0,3), a= rnd.Next(0,100), b= rnd.Next(0,100), c= rnd.Next(0,100);
 52           string t=f[r].Replace("a",a+"").Replace("b",b+"").Replace("c",c+"");
 53           double s1=Edm.calculate(t), s2=calculate2(t);
 54           Console.WriteLine(i+". Tested for: 	   

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

-Advertisement-
Play Games
更多相關文章
  • 公眾號「架構成長指南」,專註於生產實踐、雲原生、分散式系統、大數據技術分享。 概述 在之前的教程中,我們看到了使用 RestTemplate 的 Spring Boot 微服務通信示例。 從 5.0 開始,RestTemplate處於維護模式,很快就會被棄用。因此 Spring 團隊建議使用org. ...
  • 大家好,我是棧長。 昨天 17 點多,棧長興緻來了,忙裡偷閑正在看了一把 LOL S13 淘汰賽,沒想到比賽還沒看完朋友圈就已經炸鍋了: 朋友圈有人開玩笑說,阿裡 35 歲的人是不是都被優化了?還是雙 11 後都鬆懈了?這大周末的還讓加班?讓不讓人省心點。。 這我看完也有點懵 B ,大家還記得上次的 ...
  • 眾所周知,mybatisplus提供了強大的代碼生成能力,他預設生成的常用的CRUD方法(例如插入、更新、刪除、查詢等)的定義,能夠幫助我們節省很多體力勞動 ...
  • Docker在服務端的應用中已經非常廣泛,所以服務端項目支持Docker將是必不可少的存在,此篇講述如何講一個Rust項目發佈到Docker的公共鏡像里,以供他人使用 ...
  • 1.HighLightingSystem 用於3D物體高亮顯示 在項目中的使用方法:導入插件後在需要高亮顯示的3d物體上附加Highlighter組件,在需要顯示高亮效果的攝像機上附加Highlighting Renderer組件。在代碼中調整Highlighter屬性即可控制物體高亮效果的開關、閃 ...
  • 一:背景 1. 講故事 前幾天有位朋友找到我,說他的窗體程式有卡死現象,讓我幫忙看下怎麼回事,解決這種問題就需要在卡死的時候抓一個dump下來,拿到dump之後就可以分析了。 二:為什麼會卡死 1. 觀察主線程 窗體程式的卡死,需要觀察主線程此時正在做什麼,可以用 !clrstack 命令觀察。 0 ...
  • 上篇文章講述了C#特性(Attribute)知識點,本文將介紹多線程的知識點。多線程編程是現代軟體開發中的重要組成部分,它能夠充分利用多核處理器,提高應用程式的性能和響應性。C#作為.NET開發的主要語言,提供了強大的多線程支持。本文將介紹C#多線程知識點,幫助.NET開發者更好地應對多線程編程。 ...
  • 前言 Redis是一款強大的高性能鍵值存儲資料庫,也是目前NOSQL中最流行比較流行的一款資料庫,它在廣泛的應用場景中扮演著至關重要的角色,包括但不限於緩存、消息隊列、會話存儲等。在本文中,我們將介紹如何基於C# Socket來實現一個簡單的Redis客戶端類RedisClient,來演示構建請求和 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...