程式語言(Programming Language)是人與電腦溝通的橋樑,用來編寫指令、控制電腦運作與實現各種應用程式。
根據最新的程式語言排名,以下是 2024 年度排名前 20 的程式語言:
Lambda 表達式是一種匿名函數,通常用於簡化代碼,特別是在需要傳遞小型函數或回調的情況下。Lambda 表達式的語法簡潔,可以在一行中定義函數邏輯。Lambda 表達式最常見於 C++、JavaScript、Python 和 C# 等語言。
Lambda 表達式的基本語法通常包括參數、箭頭符號 =>
和函數體,例如:
(參數) => 函數體
具體語法因語言而異,例如:
[capture](parameters) { function body }
lambda parameters: expression
(parameters) => expression
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector numbers = {1, 2, 3, 4, 5};
// 使用 lambda 表達式來計算偶數的和
int sum = 0;
std::for_each(numbers.begin(), numbers.end(), [&sum](int n) {
if (n % 2 == 0) sum += n;
});
std::cout << "偶數的總和: " << sum << std::endl;
return 0;
}
# 使用 lambda 表達式來計算兩數的和
add = lambda x, y: x + y
print(add(5, 10)) # 輸出: 15
using System;
using System.Collections.Generic;
using System.Linq;
class Program {
static void Main() {
List numbers = new List { 1, 2, 3, 4, 5 };
// 使用 Lambda 表達式篩選出偶數並計算總和
int sum = numbers.Where(n => n % 2 == 0).Sum();
Console.WriteLine($"偶數的總和: {sum}");
}
}
map
、filter
和 reduce
等高階函數來操作集合。# Python:列舉並讀取物件屬性
class User:
def __init__(self): self.id = 0; self.name = "Ann"
u = User()
for k, v in vars(u).items():
print(k, v) # id 0 / name Ann
// C#:取得欄位/屬性並讀值
using System;
using System.Reflection;
var t = typeof(MyType);
foreach (var p in t.GetProperties(BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic))
Console.WriteLine($"{p.Name}={p.GetValue(obj)}");
// JavaScript:動態列舉與呼叫
const obj = { x: 1, y: 2, add(){ return this.x + this.y; } };
for (const [k,v] of Object.entries(obj)) console.log(k, v);
console.log(obj["add"]()); // 3
// Java:使用反射讀/寫欄位
import java.lang.reflect.*;
class U { private int id = 42; }
U u = new U();
Field f = U.class.getDeclaredField("id");
f.setAccessible(true);
System.out.println(f.getInt(u)); // 42
// Go:reflect 走訪結構
import "reflect"
func Dump(v any) {
val := reflect.ValueOf(v)
for i := 0; i < val.NumField(); i++ {
name := val.Type().Field(i).Name
fmt.Println(name, val.Field(i).Interface())
}
}
語言 | 支援度 | 說明 |
---|---|---|
Python | ✅ | 動態、完整反射,輕鬆遞迴物件/容器檢查。 |
JavaScript / TypeScript | ✅ | 物件為 key-value,可用 Object.values 遞迴。 |
Ruby | ✅ | instance_variables 反射成員。 |
PHP | ✅ | get_object_vars() 或 Reflection。 |
C# (.NET) | ✅ | Reflection 取得欄位/屬性,型別安全佳。 |
Java | ✅ | java.lang.reflect 可掃描欄位。 |
Kotlin | ✅ | JVM 反射完善,與 Java 類似。 |
Go | ✅ | reflect 可走訪 struct 欄位。 |
Swift | ◑ | Mirror 可走訪,但場景有限、需額外處理型別。 |
C++ (至 C++23) | ❌ | 無 runtime reflection,需手動為型別撰寫檢查。 |
Rust | ❌ | 無 runtime reflection,常以 derive/trait 自行實作。 |
def is_all_zero(obj, eps=1e-6):
if isinstance(obj, (int, float)): # 基本數值
return abs(obj) < eps
elif isinstance(obj, (list, tuple, set)): # 序列/集合
return all(is_all_zero(x, eps) for x in obj)
elif isinstance(obj, dict): # 字典
return all(is_all_zero(v, eps) for v in obj.values())
elif hasattr(obj, "__dict__"): # 一般物件
return all(is_all_zero(v, eps) for v in vars(obj).values())
else:
return False
# 測試
class Point:
def __init__(self, x=0, y=0): self.x, self.y = x, y
class Line:
def __init__(self, p1=None, p2=None):
self.p1 = p1 or Point()
self.p2 = p2 or Point()
print(is_all_zero([[0,0],[0,0]])) # True
print(is_all_zero(Line(Point(0,0),Point(0,0)))) # True
print(is_all_zero(Line(Point(1,0),Point(0,0)))) # False
function isAllZero(obj, eps = 1e-6) {
const isNumZero = n => Math.abs(n) < eps;
if (typeof obj === "number") return isNumZero(obj);
if (Array.isArray(obj)) return obj.every(v => isAllZero(v, eps));
if (obj && typeof obj === "object")
return Object.values(obj).every(v => isAllZero(v, eps));
return false;
}
console.log(isAllZero([[0,0],[0,0]])); // true
console.log(isAllZero({x:0, y:{z:0}})); // true
console.log(isAllZero({x:0.000001, y:0})); // 視 eps 而定
using System;
using System.Linq;
using System.Reflection;
public static class ZeroCheck {
public static bool IsAllZero(object obj, double eps = 1e-6) {
if (obj == null) return true;
switch (obj) {
case int i: return i == 0;
case long l: return l == 0;
case float f: return Math.Abs(f) < eps;
case double d: return Math.Abs(d) < eps;
}
var t = obj.GetType();
if (t.IsArray)
return ((Array)obj).Cast<object>().All(x => IsAllZero(x, eps));
// 掃描欄位與屬性
foreach (var f in t.GetFields(BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic))
if (!IsAllZero(f.GetValue(obj), eps)) return false;
foreach (var p in t.GetProperties(BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic))
if (p.CanRead && !IsAllZero(p.GetValue(obj, null), eps)) return false;
return true;
}
}
package main
import (
"math"
"reflect"
)
func IsAllZero(v interface{}, eps float64) bool {
val := reflect.ValueOf(v)
switch val.Kind() {
case reflect.Float32, reflect.Float64:
return math.Abs(val.Float()) < eps
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return val.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return val.Uint() == 0
case reflect.Slice, reflect.Array:
for i := 0; i < val.Len(); i++ {
if !IsAllZero(val.Index(i).Interface(), eps) { return false }
}
return true
case reflect.Map:
for _, k := range val.MapKeys() {
if !IsAllZero(val.MapIndex(k).Interface(), eps) { return false }
}
return true
case reflect.Struct:
for i := 0; i < val.NumField(); i++ {
if !IsAllZero(val.Field(i).Interface(), eps) { return false }
}
return true
case reflect.Pointer, reflect.Interface:
if val.IsNil() { return true }
return IsAllZero(val.Elem().Interface(), eps)
default:
return false
}
}
isAllZero
,或用 template/巨集輔助展開成員。derive
宏為型別自動生成實作(例如自訂 trait IsAllZero
)。eps
,避免精度問題導致誤判。isAllZero()
方法,避免昂貴的反射掃描。在 Bash 中,$? 代表上一個執行的命令的退出狀態碼(Exit Status)。這是一個整數值,通常用來判斷上一個命令是否成功執行。
#!/bin/bash
ls
echo "上一個命令的退出狀態碼是: $?"
在這個範例中,ls
命令會列出目錄內容,執行成功後,$?
的值將是 0。
#!/bin/bash
ls /nonexistent-directory
echo "上一個命令的退出狀態碼是: $?"
在這個範例中,ls
嘗試列出不存在的目錄,命令會失敗,$?
的值將是一個非 0 數字。
#!/bin/bash
cp file1.txt /some/directory/
if [ $? -eq 0 ]; then
echo "文件複製成功。"
else
echo "文件複製失敗。"
fi
此範例會根據命令的退出狀態來決定要顯示哪一條訊息。
if [ 條件 ]; then
指令
fi
要在 if 中同時滿足多個條件,可使用:
-a
(過時但可用)if [ "$a" -gt 0 -a "$b" -gt 0 ]; then
echo "a 和 b 都大於 0"
fi
[[ ]] &&
(推薦)if [[ "$a" -gt 0 && "$b" -gt 0 ]]; then
echo "a 和 b 都大於 0"
fi
&&
if [ "$a" -gt 0 ] && [ "$b" -gt 0 ]; then
echo "a 和 b 都大於 0"
fi
只要其中一個條件成立即可執行:
-o
(過時但可用)if [ "$a" -eq 0 -o "$b" -eq 0 ]; then
echo "a 或 b 為 0"
fi
[[ ]] ||
(推薦)if [[ "$a" -eq 0 || "$b" -eq 0 ]]; then
echo "a 或 b 為 0"
fi
||
if [ "$a" -eq 0 ] || [ "$b" -eq 0 ]; then
echo "a 或 b 為 0"
fi
可搭配括號來控制優先順序:
if [[ ( "$a" -gt 0 && "$b" -gt 0 ) || "$c" -eq 1 ]]; then
echo "a 和 b 都大於 0,或 c 等於 1"
fi
[[ ]]
時支援 &&
和 ||
[ ]
時建議搭配多個條件語句並用 &&
/ ||
在 Bash 中,可以使用條件判斷來檢查變數是否為空,以下提供幾種常見的方式:
-z
檢查變數是否為空#!/bin/bash
var=""
if [ -z "$var" ]; then
echo "變數是空的"
else
echo "變數不是空的"
fi
-z
用來檢查變數是否為空,如果變數為空,條件成立。
-n
檢查變數是否不為空#!/bin/bash
var="some value"
if [ -n "$var" ]; then
echo "變數不是空的"
else
echo "變數是空的"
fi
-n
用來檢查變數是否不為空,如果變數有值,條件成立。
#!/bin/bash
var=""
if [ "$var" == "" ]; then
echo "變數是空的"
else
echo "變數不是空的"
fi
這種方式直接將變數與空字串進行比較,來檢查變數是否為空。
Bash 支援兩種陣列:
fruits=("apple" "banana" "cherry")
echo "${fruits[0]}" # apple
echo "${fruits[1]}" # banana
echo "${fruits[@]}"
echo "${#fruits[@]}"
fruits+=("date")
for fruit in "${fruits[@]}"; do
echo "$fruit"
done
declare -A capital
capital["Taiwan"]="Taipei"
capital["Japan"]="Tokyo"
echo "${capital["Japan"]}" # Tokyo
for key in "${!capital[@]}"; do
echo "$key 的首都是 ${capital[$key]}"
done
declare -A
宣告Bash 陣列是儲存多筆資料的重要結構,適合用於參數清單、目錄集、鍵值對等資料處理。
combined=("${array1[@]}" "${array2[@]}")
這會將兩個陣列中的所有元素合併為一個新陣列 combined
。
array1=("apple" "banana")
array2=("cherry" "date")
combined=("${array1[@]}" "${array2[@]}")
echo "合併後的陣列:"
for item in "${combined[@]}"; do
echo "$item"
done
array1+=("${array2[@]}")
這會將 array2
的內容直接加到 array1
後方。
"${array[@]}"
形式,避免元素被錯誤展開str="hello world"
if [[ "$str" == hello* ]]; then
echo "字串以 hello 開頭"
fi
說明: 使用 [[ ]]
支援 shell 的模式比對,*
代表任意字元。
if [[ "$str" =~ ^hello ]]; then
echo "字串以 hello 開頭"
fi
說明: ^
表示開頭,[[ ]]
中的 =~
是正則運算。
case
敘述case "$str" in
hello*) echo "字串以 hello 開頭" ;;
*) echo "不是以 hello 開頭" ;;
esac
說明: case
是處理字串模式的好工具,簡潔且可讀性高。
expr
(舊式寫法)if expr "$str" : '^hello' &> /dev/null; then
echo "字串以 hello 開頭"
fi
說明: 使用 expr
的正則比對功能,適用於較舊版本的 shell。
[[ ]]
比 [ ]
功能更完整,推薦使用=~
時可使用正則表達式,但記得不要加引號-e
-e
用來檢查檔案或目錄是否存在(不分類型)。
FILE="/etc/passwd"
if [ -e "$FILE" ]; then
echo "$FILE 存在"
else
echo "$FILE 不存在"
fi
! -d
-d
用來檢查是否為目錄,!
是否定運算。
DIR="/tmp/myfolder"
if [ ! -d "$DIR" ]; then
echo "$DIR 不存在,正在建立..."
mkdir -p "$DIR"
else
echo "$DIR 已存在"
fi
-e
:檔案或目錄是否存在(不管是什麼類型)-f
:是否為「一般檔案」-d
:是否為「目錄」-L
:是否為「符號連結」PATH_TO_CHECK="/home/user/config"
if [ -e "$PATH_TO_CHECK" ]; then
echo "$PATH_TO_CHECK 已存在"
else
echo "$PATH_TO_CHECK 不存在,建立中..."
mkdir -p "$PATH_TO_CHECK"
fi
-e
-d
或 -f
target_dir="/path/to/your/dir"
subdirs=()
while IFS= read -r -d $'\0' dir; do
subdirs+=("$dir")
done < <(find "$target_dir" -mindepth 1 -maxdepth 1 -type d -print0)
find
:列出子目錄(不遞迴)-mindepth 1
:排除自身目錄-maxdepth 1
:只列出第一層-type d
:只列出目錄-print0
搭配 read -d $'\0'
:處理有空格的路徑for dir in "${subdirs[@]}"; do
echo "$dir"
done
subdirs=( "$target_dir"/*/ )
此寫法使用通配符匹配子目錄,但無法排除檔案或處理空格與特殊字元。
path="~/myfolder/file.txt"
realpath "$(eval echo "$path")"
path="~/myfolder/file.txt"
realpath "$(echo "$path" | sed "s|^~|$HOME|")"
path="~/myfolder/file.txt"
readlink -f "$(eval echo "$path")"
realpath "$(eval echo "$path")"
grep "關鍵字" 檔案名
grep -i "關鍵字" 檔案名
grep -n "關鍵字" 檔案名
grep -r "關鍵字" 目錄路徑
grep -o "關鍵字" 檔案名
grep -H "關鍵字" 檔案名
grep "關鍵字1" 檔案名 | grep "關鍵字2"
# 方法一:正規表示式
grep -E "關鍵字1|關鍵字2" 檔案名
# 方法二:多個 -e 參數
grep -e "關鍵字1" -e "關鍵字2" 檔案名
# 方法一:強制將檔案視為文字
grep -a "關鍵字" 檔案名
# 方法二:轉換檔案編碼為 UTF-8 再搜尋
iconv -f 原始編碼 -t UTF-8 檔案名 | grep "關鍵字"
# 範例 (從 BIG5 轉成 UTF-8)
iconv -f BIG5 -t UTF-8 檔案名 | grep "關鍵字"
grep -rin "關鍵字" 目錄路徑
在處理包含空格、特殊符號(如空白、引號、換行)的檔名或路徑時,傳統使用 find
配管線 xargs
或 read
可能產生誤判。
-print0
可讓 find
以 null(\0
)字元作為輸出分隔,而 read -d $'\0'
能精準讀取 null 分隔的內容,確保每個檔案/路徑準確分離。
subdirs=()
while IFS= read -r -d $'\0' dir; do
subdirs+=("$dir")
done < <(find . -type d -print0)
-print0
:將每筆結果用 null 字元結尾,不用換行-d $'\0'
:read
每次讀取一個 null 結尾的項目IFS=
:避免空格被當成欄位分隔-r
:避免處理時跳脫字元txt_files=()
while IFS= read -r -d $'\0' file; do
txt_files+=("$file")
done < <(find "$(pwd)" -type f -name "*.txt" -print0)
find . -type f -print0 | while IFS= read -r -d $'\0' file; do
echo "處理:$file"
# your_command "$file"
done
find . -type d -print0 | while IFS= read -r -d $'\0' dir; do
if [[ "$dir" == *" "* ]]; then
echo "含空格的目錄:$dir"
fi
done
傳統用法如:
find . -type f | while read file; do ...
但若檔名內含換行,會導致 read
錯誤解析,甚至多行誤判為多個檔案。
-print0
與 read -d $'\0'
是處理任何檔名最安全的方式-0
等場景以下寫法在某些 Bash 或 Cygwin 環境中,只讀取第一個項目:
while IFS= read -r -d $'\0' dir; do
subdirs+=("$dir")
done < <(find "$target_dir" -mindepth 1 -maxdepth 1 -type d -print0)
這通常是因為 `<(find ...)` 在某些系統(如 Cygwin)不是「真正的檔案描述符」,造成 `read` 無法持續讀取。
-print0
subdirs=()
find "$target_dir" -mindepth 1 -maxdepth 1 -type d -print0 | while IFS= read -r -d $'\0' dir; do
subdirs+=("$dir")
done
**注意**:若希望 `subdirs` 在主程式中可用,這方法不適合(因 `while ... |` 子 shell 無法回傳陣列)
mapfile -d '' -t subdirs < <(find "$target_dir" -mindepth 1 -maxdepth 1 -type d -print0)
mapfile
(或 readarray
)可正確讀入 null 分隔字串為陣列。
這是最可靠且能保留在主 shell 中的方式。
for dir in "${subdirs[@]}"; do
echo "找到目錄:$dir"
done
mapfile -d '' -t
是處理 -print0
最安全且最 Bash 原生的方式var="line1
line2
line3"
while IFS= read -r line; do
echo "讀到:$line"
done <<< "$var"
IFS=
避免 trim 空白-r
避免跳脫字元被解析<<<
是 here-string,將變數內容當成輸入給 whileread -r first_line <<< "$var"
echo "第一行為:$first_line"
readarray -t lines <<< "$var"
for line in "${lines[@]}"; do
echo "$line"
done
find . -mindepth 1 -maxdepth 1 -type d -name "abc*" -printf "%T@ %p\n" | sort -nr | cut -d' ' -f2-
-mindepth 1 -maxdepth 1
:只列出當前目錄下的子目錄-name "abc*"
:名稱以 abc 開頭-printf "%T@ %p\n"
:印出修改時間(timestamp)與目錄名稱sort -nr
:依時間數值由新到舊排序cut -d' ' -f2-
:去掉時間戳,只顯示路徑./abc_latest
./abc_old
./abc_2020
readarray -t abc_dirs << <(
find . -mindepth 1 -maxdepth 1 -type d -name "abc*" -printf "%T@ %p\n" |
sort -nr | cut -d' ' -f2-
)
for dir in "${abc_dirs[@]}"; do
echo "找到:$dir"
done
-printf
為 GNU find 專屬功能,在 macOS 預設 find
不支援,可用 gfind
替代%T@
代表「最後修改時間(秒)」,若需建立時間,需額外工具DIR="/mnt/usb"
if [ -w "$DIR" ]; then
echo "$DIR 可寫入"
else
echo "$DIR 不可寫入"
fi
-w
為檢查目錄是否有「寫入權限」。但若目錄本身可寫,但實際裝置唯讀(如 mount 成 readonly),這方法可能失效。
DIR="/mnt/usb"
TESTFILE="$DIR/.write_test"
if touch "$TESTFILE" 2>/dev/null; then
echo "$DIR 可寫入"
rm "$TESTFILE"
else
echo "$DIR 不可寫入"
fi
此方法最可靠,能檢測 mount 狀態或實體裝置是否真的可寫。
mount
指令檢查是否唯讀掛載MNT="/mnt/usb"
if mount | grep "$MNT" | grep -q "(ro,"; then
echo "$MNT 為唯讀掛載"
else
echo "$MNT 為可寫掛載"
fi
此方法需檢查裝置是否被以唯讀(ro
)方式掛載。
在 Shell 中,使用 `>` 可以將指令的標準輸出(stdout)重導向到一個檔案或設備。若檔案已存在,內容會被覆蓋。
echo "Hello" > output.txt
這行指令將 "Hello"
寫入 output.txt
檔案。
使用 `>>` 會將標準輸出附加到指定檔案的末尾,不會覆蓋原有內容。
echo "Hello again" >> output.txt
這行指令會將 "Hello again"
附加到 output.txt
的結尾。
在 Shell 中,`2>` 用於將標準錯誤(stderr)重導向到指定位置。例如:
ls non_existent_file 2> error.log
這行指令會將錯誤訊息寫入 error.log
檔案。
若不想覆蓋錯誤訊息檔案,可使用 `2>>` 將錯誤訊息附加到檔案末尾。
ls non_existent_file 2>> error.log
這行指令會將錯誤訊息附加到 error.log
。
使用 `&>` 可以同時將標準輸出與標準錯誤都重導向到同一個檔案或設備。
command &> all_output.log
這行指令會將 command
的所有輸出(標準輸出與錯誤)寫入 all_output.log
。
`2>&1` 將標準錯誤合併到標準輸出,便於統一管理。例如:
command > output.log 2>&1
這行指令會將標準輸出和錯誤都寫入 output.log
。
若不想顯示任何輸出,可將所有輸出導向到 /dev/null
,如:
command >/dev/null 2>&1
這行指令會將 command
的所有輸出丟棄。
在使用 tee
命令將輸出追加到文件時,可以通過 iconv
將輸出轉換為 UTF-8 編碼,確保文件內容以 UTF-8 保存。以下是具體的指令和範例。
以下是將輸出保存為 UTF-8 編碼的 tee
指令格式:
command | iconv -t utf-8 | tee -a output.txt
-t
代表「目標編碼」。output.txt
文件。以下範例演示如何將 ls
命令的輸出以 UTF-8 編碼寫入到 output.txt
:
ls | iconv -t utf-8 | tee -a output.txt
執行該指令後,output.txt
的內容將以 UTF-8 編碼保存,避免出現編碼錯誤。
Win + R
輸入 cmd
後按下 Entercmd
可直接於該路徑開啟dir
:顯示目前目錄下的檔案與資料夾cd
:切換目錄,例如 cd C:\Users
cls
:清除螢幕內容copy
:複製檔案,例如 copy a.txt b.txt
del
:刪除檔案mkdir
:建立新資料夾rmdir
:刪除資料夾echo
:輸出文字,例如 echo Hello
exit
:關閉命令提示字元dir > file.txt
將結果輸出至檔案dir | find "txt"
篩選包含 txt
的結果.bat
檔案一次執行多個指令set
查看與設定變數ipconfig /flushdns
或 sfc /scannow
等系統維護指令可以使用 &
、&&
或 ||
來接續指令:
cmd /k "第一個指令 & 第二個指令"
&
:無論第一個成功或失敗都會執行下一個&&
:只有第一個成功時才執行下一個||
:只有第一個失敗時才執行下一個在第一個指令後加上 call
,即可接續執行另一個批次檔:
cmd /k "第一個指令 & call second.bat"
若不需要保持視窗開啟,可以用 /c
:
cmd /c "第一個指令 & 第二個指令"
/c
會執行完所有指令後自動關閉視窗,而 /k
則會保留視窗。
@echo off
:: 偵測 PowerShell
if defined PSModulePath (
echo 目前在 PowerShell 執行
goto :eof
)
:: 偵測 Cygwin
if defined CYGWIN (
echo 目前在 Cygwin 執行
goto :eof
)
if defined SHELL (
echo 目前在 Cygwin 執行 (SHELL=%SHELL%)
goto :eof
)
:: 預設為 CMD
echo 目前在 CMD 執行
PSModulePath
會存在CYGWIN
變數,或 SHELL=/bin/bash
Win + X
選擇「Windows PowerShell」或「Windows Terminal」powershell
可直接於該路徑開啟Get-ChildItem
:列出目前目錄檔案與資料夾 (類似 dir
)Set-Location
:切換目錄 (縮寫 cd
)Clear-Host
:清除螢幕內容 (縮寫 cls
)Copy-Item
:複製檔案或資料夾Remove-Item
:刪除檔案或資料夾New-Item
:建立新檔案或資料夾Write-Output
:輸出文字 (縮寫 echo
)Exit
:關閉 PowerShellGet-Process | Where-Object {$_.CPU -gt 100}
Get-ChildItem > file.txt
ls
、cp
、rm
可直接使用$var = "Hello"
,輸出 $var
.ps1
檔案執行多個指令Get-EventLog
、Set-ExecutionPolicy
等系統維護指令Get-Variable
:顯示目前 PowerShell 工作階段中的所有變數Get-ChildItem variable:
:透過變數驅動器檢視所有變數# 列出所有變數
Get-Variable
# 只顯示變數名稱
Get-Variable | Select-Object Name
# 顯示特定變數的值
Get-Variable PATH | Format-List *
# 使用變數驅動器方式
Get-ChildItem variable:
variable:
可當作路徑來操作變數,例如 Get-Content variable:PATH
Where-Object
,例如:Get-Variable | Where-Object { $_.Name -like "P*" }
只顯示以 P 開頭的變數在 C++ 中,可以使用 std::array
初始化多維陣列為 0:
#include <iostream>
#include <array>
using namespace std;
int main() {
array<array<int, 4>, 3> arr = {0}; // 初始化所有元素為 0
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
cout << arr[i][j] << " ";
}
cout << endl;
}
return 0;
}
std::array
是 C++ 標準庫中的容器類別,適合用於大小在編譯期固定的情況。std::array
提供更多的功能和安全性。std::array<std::array<std::array<float, 2>, 2>, 2> twoLines;
std::array<std::array<std::array<float, 2>, 2>, 2> twoLinesCopy;
std::array
支援淺層到深層的完整複製,因此可以直接使用 =
:
twoLinesCopy = twoLines;
std::copy(twoLines.begin(), twoLines.end(), twoLinesCopy.begin());
std::memcpy(&twoLinesCopy, &twoLines, sizeof(twoLines));
twoLinesCopy = twoLines;
std::copy
適用於需要 STL 泛型方法。memcpy
僅適用於 POD(Plain Old Data),且無建構子/解構子邏輯。
#include <cmath>
#include <iostream>
using namespace System;
using namespace System::Collections::Generic;
int main()
{
// 建立測試資料
Dictionary<int, List<float>^>^ data = gcnew Dictionary<int, List<float>^>();
data->Add(1, gcnew List<float>({ 1.2f, 2.3f, 3.4f }));
data->Add(2, gcnew List<float>({ 4.5f, 5.5f, 6.5f, 7.5f }));
data->Add(3, gcnew List<float>()); // 測試空清單
// 計算每個 key 的標準差
for each (KeyValuePair<int, List<float>^> entry in data)
{
int key = entry.Key;
List<float>^ values = entry.Value;
if (values == nullptr || values->Count == 0)
{
Console::WriteLine("Key {0}: 無資料", key);
continue;
}
// 計算平均值
double sum = 0.0;
for each (float v in values)
{
sum += v;
}
double mean = sum / values->Count;
// 計算變異數(母體變異數)
double variance = 0.0;
for each (float v in values)
{
double diff = v - mean;
variance += diff * diff;
}
variance /= values->Count; // 若要樣本變異數,改為 (values->Count - 1)
// 標準差
double stddev = Math::Sqrt(variance);
Console::WriteLine("Key {0}: 標準差 = {1:F4}", key, stddev);
}
return 0;
}
values->Count - 1
。Key 1: 標準差 = 0.8981 Key 2: 標準差 = 1.1180 Key 3: 無資料
在 C++ 中,模板 (Template) 是一種泛型編程的工具,允許我們在編寫函數或類別時,不指定具體的資料型別,從而使程式碼更具重用性。模板有助於在單一程式碼中處理不同型別的資料,避免重複的函數或類別定義。
函數模板允許我們撰寫可以處理不同型別的函數。函數模板的語法如下:
template <typename T>
T add(T a, T b) {
return a + b;
}
在此例中,add
函數可以處理任何支持加法操作的型別,如 int
、float
或 double
。
使用時,可以這樣呼叫:
int result = add(3, 4); // 使用 int 型別
double result2 = add(3.5, 2.7); // 使用 double 型別
類別模板允許我們建立可以適用於多種型別的類別。類別模板的語法如下:
template <typename T>
class MyClass {
private:
T data;
public:
MyClass(T data) : data(data) {}
T getData() { return data; }
};
此例中的 MyClass
類別可以使用任何型別的資料作為 data
。
使用時可以這樣:
MyClass<int> obj1(5); // int 型別
MyClass<double> obj2(3.14); // double 型別
模板可以接受多個參數,例如:
template <typename T, typename U>
class Pair {
private:
T first;
U second;
public:
Pair(T first, U second) : first(first), second(second) {}
T getFirst() { return first; }
U getSecond() { return second; }
};
這樣的模板類別可以存儲兩種不同型別的資料:
Pair<int, double> pair1(1, 3.14);
模板特化允許我們對特定型別的模板進行特別定義。例如:
template <>
class MyClass<int> {
public:
MyClass(int data) { /* 特化行為 */ }
};
這段程式碼特化了 MyClass
對 int
型別的行為,使其與其他型別不同。
模板還可以接受非型別的參數,例如常數值:
template <typename T, int Size>
class Array {
private:
T data[Size];
public:
int getSize() const { return Size; }
};
在這裡,Size
是一個非型別參數,表示陣列的大小。
使用範例:
Array<int, 10> arr; // 建立大小為 10 的 int 型別陣列
C++ 模板功能強大,使得程式碼可以更具泛用性並減少重複。了解如何利用函數模板、類別模板及模板特化等技術,將大大提升程式設計的彈性與效能。
在 C++ 中,當兩個類別相互依賴並需要同時引用對方的成員時,直接在各自的 .h
檔案中 #include
對方的定義會造成循環引用問題,導致無法編譯。解決方法是使用「前向宣告」(forward declaration)來避免循環引用。
.h
檔案中只宣告對方類別的存在,不直接包含對方的頭文件。.cpp
檔案中包含對方的頭文件,讓編譯器取得完整的類別定義。
// ClassA.h
#ifndef CLASSA_H
#define CLASSA_H
// 前向宣告 ClassB
class ClassB;
class ClassA {
public:
ClassA();
void setB(ClassB* b); // 設定指向 ClassB 的指標
void showBData(); // 顯示 ClassB 的數據
private:
ClassB* b; // 指向 ClassB 的指標
};
#endif
// ClassB.h
#ifndef CLASSB_H
#define CLASSB_H
// 前向宣告 ClassA
class ClassA;
class ClassB {
public:
ClassB(int data);
int getData(); // 取得數據
void setA(ClassA* a); // 設定指向 ClassA 的指標
void showAInfo(); // 顯示 ClassA 的資訊
private:
int data;
ClassA* a; // 指向 ClassA 的指標
};
#endif
#include "ClassA.h"
#include "ClassB.h"
#include <iostream>
ClassA::ClassA() : b(nullptr) {}
void ClassA::setB(ClassB* b) {
this->b = b;
}
void ClassA::showBData() {
if (b != nullptr) {
std::cout << "ClassB data: " << b->getData() << std::endl;
}
}
#include "ClassB.h"
#include "ClassA.h"
#include <iostream>
ClassB::ClassB(int data) : data(data), a(nullptr) {}
int ClassB::getData() {
return data;
}
void ClassB::setA(ClassA* a) {
this->a = a;
}
void ClassB::showAInfo() {
if (a != nullptr) {
a->showBData();
}
}
ClassA.h
和 ClassB.h
中,我們僅使用前向宣告來指出對方類別的存在,這樣可以避免循環引用。
.cpp
檔案中包含對方的頭文件,保證在使用指標時獲得對方的完整類別定義。在 C++ 中,friend 關鍵字可以用於函數或類別,以允許其他函數或類別訪問類別的私有成員(private)和保護成員(protected)。這樣的設計可讓外部函數或類別進行操作,而不違反封裝原則。
Friend 函數是一種被授權訪問另一類別的私有成員和保護成員的外部函數。在類別內部聲明時,以 friend
關鍵字修飾即可。
範例如下:
#include <iostream> using namespace std; class Box { private: double width; public: Box(double w) : width(w) {} // 聲明 friend 函數 friend void showWidth(Box &b); }; // friend 函數定義,能訪問 Box 類別的私有成員 void showWidth(Box &b) { cout << "Box 寬度: " << b.width << endl; } int main() { Box box(10.5); showWidth(box); // 訪問私有成員 width return 0; }
在此範例中,showWidth
函數雖然是 Box 類別外的普通函數,但因為被聲明為 friend 函數,仍然能夠訪問 Box 類別的私有成員 width
。
Friend 類別允許某一類別訪問另一類別的所有成員。這樣的設置在類別需要緊密協作時非常有用,但使用時應該謹慎,以避免過多暴露內部細節。
範例如下:
#include <iostream> using namespace std; class Square; // 前置聲明 class Rectangle { private: double width, height; public: Rectangle(double w, double h) : width(w), height(h) {} // 聲明 Square 類別為 friend friend class Square; }; class Square { public: double areaOfRectangle(Rectangle &rect) { return rect.width * rect.height; } }; int main() { Rectangle rect(5.0, 3.0); Square square; cout << "矩形面積: " << square.areaOfRectangle(rect) << endl; return 0; }
在此範例中,Square 類別被宣告為 Rectangle 類別的 friend 類別,因此 Square 類別中的成員函數可以直接訪問 Rectangle 類別的私有成員 width
和
height
。
在 C++ 中使用 friend 函數和 friend 類別需謹慎,過多使用會破壞類別的封裝性。因此,friend 關鍵字通常只在設計需要密切配合的類別或函數時使用。
週邊控制程式是用來與電腦或主機連接的外部硬體裝置(例如:印表機、掃描器、馬達、PLC、感測器等)進行通訊與操作的應用程式。
語言 | 適用情境 | 備註 |
---|---|---|
Python | 快速開發、測試自動化 | 適用 Serial、USB HID |
C/C++ | 嵌入式控制、驅動開發 | 可存取低階記憶體與硬體 |
C# | Windows UI + 控制裝置 | 適用於 COM Port、USB 通訊 |
Java | 跨平台控制 | 較少用於低階裝置 |
import serial
ser = serial.Serial('COM3', 9600, timeout=1)
ser.write(b'ON\n') # 傳送控制命令
response = ser.readline()
print("裝置回應:", response.decode())
ser.close()
SerialPort port = new SerialPort("COM4", 9600);
port.Open();
port.WriteLine("MOVE 100");
string response = port.ReadLine();
Console.WriteLine("回應:" + response);
port.Close();
libusb
(C)、pyusb
(Python)、HidSharp
(C#)等函式庫import socket
s = socket.socket()
s.connect(('192.168.1.100', 5000))
s.sendall(b'START\n')
data = s.recv(1024)
print("回應:", data.decode())
s.close()
週邊控制程式開發需依據裝置的介面與協定選擇適合的語言與函式庫,結合串列、USB、網路等通訊方式,可廣泛應用於各種自動化與工控領域。
// build.gradle
implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
// Java 呼叫掃描畫面
IntentIntegrator integrator = new IntentIntegrator(this);
integrator.setPrompt("請掃描條碼");
integrator.setBeepEnabled(true);
integrator.setOrientationLocked(false);
integrator.initiateScan();
// onActivityResult 接收結果
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
if(result != null) {
if(result.getContents() != null) {
String barcode = result.getContents();
Log.d("條碼內容", barcode);
}
}
}
<script src="https://unpkg.com/[email protected]/dist/quagga.min.js"></script>
<script>
Quagga.init({
inputStream: {
name: "Live",
type: "LiveStream",
target: document.querySelector('#scanner')
},
decoder: {
readers: ["code_128_reader", "ean_reader", "upc_reader"]
}
}, function(err) {
if (!err) {
Quagga.start();
}
});
Quagga.onDetected(function(result) {
console.log("條碼內容: ", result.codeResult.code);
});
</script>
平台 | 建議套件 | 是否免費 |
---|---|---|
Android | ZXing / ML Kit | 是 |
Web | QuaggaJS / jsQR | 是 |
Windows | Dynamsoft / ZXing.NET | Dynamsoft 為商用 |
Python | ZBar / pyzbar | 是 |
開發 Barcode Reader 可根據平台選擇合適的開源套件,ZXing 是最廣泛支援的選擇,Web 可用 QuaggaJS,若需商業級支援則可考慮 Dynamsoft Barcode SDK。
條碼讀取器本質上是模擬鍵盤輸入的裝置。當掃描到條碼時,它會將條碼中的內容「輸出」成字元流,就像是鍵盤輸入一樣。
因此,條碼中是可以包含控制碼(Control Code),但需要條碼讀取器與條碼格式支援才行。
例如以下格式可編碼控制碼:
部分條碼產生器允許嵌入特殊字元,例如:
\x0D
表示 Enter\x03
表示 Ctrl-C^C
、[CTRL+C]
等特殊語法依工具而定多數專業條碼機(例如 Zebra、Honeywell)在出廠時預設不啟用控制碼輸出,需透過掃描器提供的「設定條碼」啟用:
例如在 Windows 應用程式中:
keydown
與 Ctrl
配合的快捷鍵處理。用 Code128 編碼 ASCII 3:
輸入:\x03Hello World
條碼掃描後會觸發 Ctrl+C 再輸出 "Hello World"。
sudo apt update && sudo apt upgrade -y
sudo apt install -y wget curl unzip zip git ca-certificates
sudo apt install -y openjdk-17-jdk
sudo dpkg -i ~/Downloads/android-studio-*.deb || sudo apt -f install -y
android-studio
或解壓縮 .tar.gz
:
tar -xzf ~/Downloads/android-studio-*.tar.gz -C ~
~/android-studio/bin/studio.sh
sudo apt install -y android-sdk-platform-tools
adb kill-server
adb start-server
adb devices
sudo dpkg -i ~/Downloads/code_*.deb || sudo apt -f install -y
sudo apt update && sudo apt install -y openjdk-17-jdk unzip git
wget https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip
unzip commandlinetools-linux-*_latest.zip -d ~/android-sdk
~/.bashrc
或 ~/.zshrc
):
export ANDROID_SDK_ROOT=$HOME/android-sdk
export PATH=$ANDROID_SDK_ROOT/cmdline-tools/bin:$ANDROID_SDK_ROOT/platform-tools:$PATH
sdkmanager "platform-tools" "platforms;android-34" "build-tools;34.0.0"
adb devices
看到 device
即可部署測試。
adb install
或框架 CLI(如 flutter run
)將 App 部署至手機。在 Android Studio 中建立一個新的專案,選擇 "Empty Activity" 模板,然後設定專案名稱及其他基本資訊。
在 res/layout/activity_main.xml
檔案中設計簡單的使用者介面,例如包含一個按鈕和一個文字顯示區域:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="點我" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, World!"
android:layout_marginTop="20dp" />
</LinearLayout>
在 MainActivity.java
中,設定按鈕的點擊事件,使其更改文字顯示區域的內容:
package com.example.simpleapp;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
TextView textView = findViewById(R.id.textView);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
textView.setText("你點了按鈕!");
}
});
}
}
在 Android Studio 中點擊執行按鈕,即可在模擬器或連接的實體裝置上測試應用程式。點擊按鈕後,文字將更改為 "你點了按鈕!"。
在 AndroidManifest.xml
中加入以下權限:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
}
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
LocationListener locationListener = new LocationListener() {
@Override
public void onLocationChanged(@NonNull Location location) {
double latitude = location.getLatitude();
double longitude = location.getLongitude();
Log.d("GPS", "Latitude: " + latitude + ", Longitude: " + longitude);
}
};
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
1000, // 毫秒間隔
1, // 最小距離(公尺)
locationListener);
}
FusedLocationProviderClient fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
fusedLocationClient.getLastLocation()
.addOnSuccessListener(this, location -> {
if (location != null) {
double lat = location.getLatitude();
double lng = location.getLongitude();
Log.d("GPS", "Lat: " + lat + ", Lng: " + lng);
}
});
}
要實作一個類似 Siri 或 Hey Google 的語音助理功能,你需要結合以下元件:
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.INTERNET"/>
SpeechRecognizer recognizer = SpeechRecognizer.createSpeechRecognizer(this);
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault());
recognizer.setRecognitionListener(new RecognitionListener() {
@Override
public void onResults(Bundle results) {
ArrayList<String> matches = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
if (matches != null && !matches.isEmpty()) {
String command = matches.get(0).toLowerCase();
if (command.contains("打開相機")) {
// 執行操作
}
}
}
// 其他必要的覆寫方法略
});
recognizer.startListening(intent);
TextToSpeech tts = new TextToSpeech(this, status -> {
if (status == TextToSpeech.SUCCESS) {
tts.setLanguage(Locale.TAIWAN);
tts.speak("你好,我在這裡。", TextToSpeech.QUEUE_FLUSH, null, null);
}
});
如需常駐背景並語音喚醒,需使用:
RECORD_AUDIO
權限。常駐背景監聽的目標,是讓 App 即使在未開啟畫面時也能偵測語音喚醒詞(如「Hey 助理」),並啟動對應功能。
SpeechRecognizer
長時間背景運行。SpeechRecognizer
辨識完整語音命令。public class VoiceService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Notification notification = new NotificationCompat.Builder(this, "voice_channel")
.setContentTitle("語音助手運作中")
.setSmallIcon(R.drawable.ic_mic)
.build();
startForeground(1, notification);
// 初始化 Hotword 檢測
startHotwordDetection();
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
NotificationChannel channel = new NotificationChannel("voice_channel",
"Voice Assistant", NotificationManager.IMPORTANCE_LOW);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
Porcupine 提供 Android SDK,可辨識自訂關鍵詞並在完全離線情況下運作。
PorcupineManager porcupineManager = new PorcupineManager.Builder()
.setAccessKey("你的金鑰")
.setKeywordPath("hey_assistant.ppn")
.setSensitivity(0.7f)
.build((keywordIndex) -> {
// 被喚醒時呼叫 SpeechRecognizer 進行語音辨識
startSpeechRecognition();
});
porcupineManager.start();
Intent serviceIntent = new Intent(this, VoiceService.class);
ContextCompat.startForegroundService(this, serviceIntent);
RECORD_AUDIO
權限。1. 下載並安裝最新版本的 Xcode。
2. 開啟 Xcode 並前往 Preferences > Accounts
,登入 Apple ID 以啟用開發者功能。
3. 透過 Xcode 安裝 Command Line Tools 以使用 Swift 命令列工具。
1. 開啟 Xcode,選擇「Create a new Xcode project」。
2. 選擇適合的應用程式模板,例如 App (iOS/macOS)。
3. 設定專案名稱、識別碼 (Bundle Identifier) 及語言 (Swift 或 Objective-C)。
4. 選擇 UI 框架 (SwiftUI 或 UIKit)。
1. 註冊 Apple Developer Program (需年費 USD $99)。
2. 透過 Xcode 設定 App Store Connect 並提交 App。
3. 遵循 Apple 的 App Store Review Guidelines 確保應用程式符合上架規範。
iOS 開發主要使用 Xcode,這是 Apple 提供的官方整合開發環境 (IDE)。
學習 iOS 開發需要掌握以下基礎:
以下是一些實用的學習與開發資源:
Xcode 是 Apple 提供的整合開發環境 (IDE),用於 macOS、iOS、watchOS 和 tvOS 應用程式的開發。
可從 Mac App Store 或 Apple 開發者官網下載最新版本的 Xcode。
提升開發效率的實用技巧:
相關學習與參考資源:
Swift 是 Apple 推出的現代化程式語言,用於開發 iOS、macOS、watchOS 和 tvOS 應用程式。
var
和 let
?
和 !
處理值的存在與否if
、switch
、for
、while
func
定義,支援參數標籤與多返回值class
和 struct
Swift 不僅適用於 Apple 生態系統,還可以用於伺服器端開發與跨平台工具。
相關學習與參考資源:
Objective-C 是一種以 C 為基礎的物件導向程式語言,最初由 NeXT 公司開發,後來被 Apple 廣泛用於 macOS 和 iOS 應用程式開發。
Objective-C 的語法結合了 C 和 Smalltalk 的特性,使用 @ 符號來標示語言擴展。
@interface
和 @implementation
[object method]
@property
和 @synthesize
Objective-C 的開發主要使用 Apple 的 Xcode。
以下是一些學習與參考的資源:
GNews是一個由Google開發的新聞聚合平台,旨在幫助用戶獲取最新的全球新聞資訊。它整合了來自各種新聞來源的內容,使用人工智慧技術來個性化推薦用戶感興趣的新聞。
用戶可以通過訪問GNews的網站或下載其應用程式來使用此平台。在平台上,用戶可以選擇感興趣的主題、追蹤特定的新聞來源,並根據自己的需求自訂新聞推送。
GNews是一個強大的新聞聚合工具,透過人工智慧技術,為用戶提供個性化的新聞體驗。隨著新聞資訊的快速變化,GNews幫助用戶快速跟上世界動態,獲取所需的資訊。
工具名稱 | 主要特色 | 適用場景 | 價格 |
---|---|---|---|
n8n | 開源的自動化工作流工具,提供高度可定制的流程設計,支持自建節點。 | 適用於企業內部流程自動化、大型自動化系統、API集成等。 | 免費開源,提供付費雲端版本。 |
Make | 提供視覺化工作流建構,支持多種第三方應用和服務的集成,強調簡單易用。 | 適用於小型到中型企業的工作流程自動化,快速集成各種服務。 | 提供免費計劃,付費計劃根據用戶需求提供更高級功能。 |
Zapier | 支持大多數應用程序的集成,簡單易用,能夠創建觸發器和自動化工作流程。 | 適用於各種業務領域的自動化,特別是小型企業和初創企業。 | 提供免費計劃,付費計劃依據用戶需求提供更多功能和運行次數。 |
Bolt 是一個快速、輕量的 AI 開發框架,專注於提供開發者簡單且高效的工具來建構應用程式。特點包括:
Cursor 是專為 AI 程式開發設計的編輯器工具,提供智能輔助程式碼撰寫與調試功能。特點包括:
v0 是一個基於視覺化開發的 AI 平台,允許使用者透過拖放介面構建模型與應用。特點包括:
Codeium 是一款結合 AI 智能輔助的程式編輯器,專注於提升程式開發效率與準確性。特點包括:
軟體工程是一門系統性、規劃性地開發、操作與維護軟體的工程學科,目標是建構高品質、可維護、可靠且符合需求的軟體系統。
請填入本專案的正式名稱。
說明此專案的緣由、背景問題以及欲解決的核心問題,並定義明確的專案目標。
概述系統功能與整體架構,可搭配系統架構圖。
可附上主要畫面草圖或線框圖,描述各畫面的元素與互動流程。
列出主要資料表結構、欄位、關聯性等。
列出需整合的外部系統、API或其他軟體元件。
列出潛在風險、限制條件(如預算、人力、技術等)。
相關文件連結、參考資料、名詞定義等。
設計模式(Design Patterns)是一組經過實踐驗證的軟體設計解決方案,主要應用於物件導向程式設計中,用來解決在特定情境下反覆出現的設計問題。
版本控制(Version Control)是一種管理檔案變更歷史的系統,廣泛應用於軟體開發中,讓多位開發者能同時協作,並保留每次變更的完整記錄。
git init
:初始化版本庫git clone [網址]
:複製遠端專案git add [檔案]
:加入變更至暫存區git commit -m "訊息"
:提交變更git push
:將變更推送至遠端儲存庫git pull
:取得並合併遠端變更git branch
:查看或建立分支git merge
:合併分支GitHub 是一個基於雲端的版本控制和協作平台,主要用於軟體開發。它使用 Git 進行版本控制,讓開發者能夠管理專案的源代碼、跟蹤變更以及與他人協作。
GitHub 適合各類開發者,無論是單獨開發者、開源社區還是企業團隊。它能夠滿足小型專案到大型軟體專案的版本控制和協作需求。
git pull --rebase
git pull --rebase
是從遠端分支拉取最新變更,並將本地的更改重新應用到最新的遠端提交之上,而不是使用傳統的合併。
git pull --rebase
--rebase
?--rebase
可以讓提交歷史保持簡潔,沒有多餘的合併提交。假設你在本地提交了一些更改,而遠端也有新的提交,使用 git pull --rebase
會:
如果在重放本地提交時發生衝突,Git 會要求手動解決衝突:
git add
暫存已解決的文件。git rebase --continue
繼續重放提交。git rebase --abort
回到原狀。git pull --rebase
是保持 Git 提交歷史整潔的重要工具,特別適合多人協作時,能夠避免產生冗餘的合併提交,並保持代碼庫的提交記錄線性。
程式部署(Application Deployment)是將開發完成的應用程式從開發環境移轉至測試或生產環境,讓使用者可以實際使用的過程。它確保系統能在不同環境中穩定、可重現地執行。
# 使用 GitHub Actions 部署範例
name: Deploy Web App
on:
push:
branches: [ "main" ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 建立 Docker 映像
run: docker build -t myapp .
- name: 部署至伺服器
run: docker run -d -p 80:80 myapp
Docker(容器技術)是一個開放原始碼的平台,用於自動化應用程式的部署、擴展與管理。它利用「容器」(Container)將應用與其依賴封裝在一起,確保在不同環境中能一致執行。
docker pull [映像名稱]
:下載映像檔docker run [映像名稱]
:執行容器docker ps
:查看執行中的容器docker stop [容器ID]
:停止容器docker rm [容器ID]
:刪除容器docker build -t [名稱] .
:依據 Dockerfile 建立映像檔docker images
:查看映像檔清單# 使用 Python 映像檔
FROM python:3.10
# 設定工作目錄
WORKDIR /app
# 複製程式檔案
COPY . /app
# 安裝依賴套件
RUN pip install -r requirements.txt
# 指定容器啟動時執行的指令
CMD ["python", "app.py"]
Docker Compose 是一個可定義多個容器應用的工具,使用 docker-compose.yml
檔案定義各服務。
version: '3'
services:
web:
build: .
ports:
- "8000:8000"
db:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: example
軟體程式碼、設計文件、使用手冊等均屬著作權保護範疇。著作權保護自創作完成時自動成立,無需註冊,禁止未經授權的複製、修改、散布或商業利用。
若軟體涉及技術創新或獨特演算法,可申請專利保護。專利需經審查批准,保障發明人對技術方案的專有權利,但純粹的程式邏輯通常不受專利保護。
軟體名稱、圖示(Logo)、品牌識別可註冊為商標,用以保護品牌形象與市場區隔,防止他人冒用造成混淆。
未公開的程式設計細節、演算法、資料庫結構等屬營業秘密。公司應透過保密協議、資訊管理制度等方式保護。
如個資法、資通安全管理法等,要求軟體開發過程中妥善處理使用者個人資料、保障資訊安全,違規可能面臨行政或刑事責任。
軟體開發常涉及承攬契約、合作開發契約、軟體授權條款等,明確雙方權利義務、程式碼所有權、維護責任、保密義務等,避免法律爭議。
軟體上線運營涉及網路服務、電子支付、智慧財產權線上保護等,需遵守電子簽章法、電子交易法等規範。
軟體的程式碼屬於著作權保護的範疇,開發者自動擁有著作權,無需額外登記。著作權保護涵蓋原始碼、設計文件、使用手冊等。
若軟體涉及新穎性與技術創新,可能可申請專利保護。例如獨特的演算法或解決技術問題的方法,但純粹的程式碼邏輯通常不在專利保護範圍。
軟體名稱、圖示(Logo)可註冊為商標,用於區分不同來源的產品,避免市場混淆。
未公開的程式設計細節、資料庫結構、演算法可透過營業秘密方式保護。需依靠企業內部保密協議與管理措施維護。
軟體可透過不同授權模式發行,例如專有軟體、自由軟體、開源授權(如GPL、MIT)。不同授權模式影響使用者的修改與再散布權利。
未經授權使用他人程式碼、圖像或演算法可能構成侵權。開發過程應確保程式碼來源合法,避免法律糾紛。
智慧財產權具地域性,但可透過國際條約如伯恩公約、TRIPS協議在多國獲得一定程度的保護。
email: [email protected]