命令列、GitHub
與正規式

Lab 4 · 9/30(三)Lab 5 · 10/7(三)Windows PowerShell/macOS Terminal

下載練習資料夾 week04-lab.zip 請先在右上角選擇作業系統。

動手做在自己的電腦上完成的練習。頁面練習在本頁的模擬工具中練習,不會影響電腦上的檔案。

終端機

開啟終端機

終端機(terminal)是以文字指令操作電腦的程式。與以滑鼠操作的圖形介面(GUI)相比,命令列有下列優點:

  • 可以重現:指令可以記錄下來,之後或在另一台電腦上重新執行,得到相同的結果;滑鼠操作則難以準確描述與重複。
  • 可以大量處理:一行指令就能處理許多檔案,例如將資料夾中所有 .csv 檔一次複製到別處。
  • 可以自動執行:多行指令可以寫成腳本(script),交由電腦依序執行。
  • 許多工具只有命令列介面:Git、Python 套件的安裝,以及連線到遠端伺服器(例如實驗室的 GPU 主機)時,通常只能透過終端機操作。

點選「開始」,搜尋 PowerShell,開啟「Windows PowerShell」。以下所有指令都在這個視窗中輸入。

按 ⌘ + 空白鍵,輸入 Terminal(終端機)並按 Enter。以下所有指令都在這個視窗中輸入。

開始安裝 Git、GitHub CLI 與 VS Code

這些工具要到「Git 與 GitHub 儲存庫」才會用到,各自的用途也在該節說明。由於下載需要一段時間,先在這裡開始安裝,安裝期間繼續進行下一節。

完成後,電腦上應有下列軟體:

軟體用途取得方式
終端機以文字指令操作電腦Windows PowerShell,系統內建Terminal,系統內建
Git記錄檔案的修改歷史下方安裝
GitHub CLI(gh)在終端機中登入 GitHub下方安裝
Visual Studio Code編輯文字檔與程式;之後的課程也會使用下方安裝
瀏覽器使用 GitHub 與 RegExr任一種皆可

逐行貼上下列指令並按 Enter。終端機中出現 [Y] Yes [N] No 時,輸入 Y 再按 Enter;跳出權限詢問視窗時,選擇「是」。每一行都顯示 Successfully installed 即完成。

winget install --id Git.Git -e --source winget
winget install --id GitHub.cli -e --source winget
winget install --id Microsoft.VisualStudioCode -e --source winget

winget 執行期間,這個視窗無法輸入其他指令,可以從「開始」再開一個 PowerShell 視窗繼續下一節。安裝完成後,須關閉並重新開啟 PowerShell,新指令才能使用。

無法使用 winget 時

改為下載安裝檔:Git 至 git-scm.com,GitHub CLI 至 cli.github.com 下載 .msi,VS Code 至 code.visualstudio.com 下載。安裝選項皆使用預設值即可。

先安裝 Git。貼上下列指令並按 Enter,在跳出的視窗中點選「安裝」:

xcode-select --install

若顯示 xcode-select: error: command line tools are already installed,表示已經安裝(訊息中雖有 error,但並非錯誤),可直接進行下一步。安裝會在另一個視窗進行,終端機可以繼續使用。

接著安裝 GitHub CLI:開啟 GitHub CLI 下載頁,在頁面下方的 Assets 清單中,點選檔名結尾為 _macOS_universal.pkg 的檔案,下載後點兩下安裝。universal 版同時支援 Intel 與 Apple 晶片(M 系列)的 Mac,不需另外區分。已安裝 Homebrew 者也可以執行 brew install gh。

最後安裝 VS Code:至 code.visualstudio.com 下載 macOS 的 Universal 版本(同時支援 Intel 與 Apple 晶片),開啟下載的檔案後,將 Visual Studio Code 拖曳到「應用程式」資料夾。開啟 VS Code,按 ⇧⌘P,輸入並執行 Shell Command: Install 'code' command in PATH,終端機才能使用 code 指令。

檔案與資料夾

電腦中的檔案存放在資料夾裡。資料夾可以包含檔案,也可以包含其他資料夾,因此整體呈樹狀結構。下載 week04-lab.zip 後,將它解壓縮到「下載」資料夾:

完成後,「下載」資料夾中應有一個 week04-lab 資料夾。若下載了兩次,資料夾名稱可能變成 week04-lab (1),請刪除多餘的一個,或將它改名為 week04-lab。資料夾的結構如下:

week04-lab/
├── README.txt
├── data/
│   ├── results_2025.csv
│   └── results_2026.csv
└── notes/
    └── week1.txt

圖中每縮排一層,表示該項目位於上一層資料夾之內。例如 data 與 notes 位於 week04-lab 之內,results_2025.csv 位於 data 之內。名稱以 / 結尾的是資料夾,其餘是檔案。

在資料夾之間移動

終端機在任何時刻都位於某一個資料夾中,這個資料夾稱為工作目錄(working directory)。pwd 顯示目前的工作目錄,ls 列出其中的內容,cd 切換到另一個資料夾。

進入練習資料夾

cd ~\Downloads\week04-lab
cd ~/Downloads/week04-lab

~ 代表使用者的家目錄(home directory),也就是以帳號命名的資料夾:C:\Users\帳號名稱/Users/帳號名稱。因此不論帳號名稱為何,這行指令都適用。

執行成功時,提示字元的結尾會變成 week04-lab>week04-lab %。接著執行 ls,應列出 README.txt、data、notes 三項。若出現 Cannot find pathno such file or directory,表示資料夾不在「下載」中或名稱不同,請回到「檔案與資料夾」檢查解壓縮的結果。

第一次在「下載」資料夾中執行指令時,macOS 可能詢問「終端機」是否可取用「下載項目」檔案夾,請選擇「好」。若已選擇不允許,執行指令時會出現 Operation not permitted;可到「系統設定 → 隱私權與安全性 → 檔案與檔案夾 → 終端機」開啟「下載項目」。

若 week04-lab 不在「下載」資料夾中,可先輸入 cd 與一個空白,再將 week04-lab 資料夾從檔案總管 Finder 拖曳到終端機視窗,路徑會自動填入,然後按 Enter。

絕對路徑與相對路徑

路徑(path)是描述檔案或資料夾位置的寫法,分為兩種:

  • 絕對路徑(absolute path)從最上層開始寫起,不論目前的工作目錄在哪裡,都指向同一個位置,例如 C:\Users\帳號名稱\Downloads\week04-lab\data。Windows 的絕對路徑以磁碟代號(如 C:\)開頭/Users/帳號名稱/Downloads/week04-lab/data。macOS 與 Linux 的絕對路徑以 / 開頭。pwd 輸出的就是絕對路徑。
  • 相對路徑(relative path)從目前的工作目錄開始寫起,因此同一個相對路徑在不同的位置會指向不同的地方。data、practice/ 與 ../notes 都是相對路徑;. 代表目前的資料夾,.. 代表上一層資料夾。

以 ~ 開頭的路徑以家目錄為起點,因此與絕對路徑相同,在任何位置都指向同一處。下表比較同一個指令在不同位置執行的結果:

目前位置指令結果
week04-labcd data進入 week04-lab 中的 data
notescd data錯誤:notes 中沒有 data
notescd ../data先回到 week04-lab,再進入 data
任何位置cd ~\Downloads\week04-lab\datacd ~/Downloads/week04-lab/data進入 data

下方的練習用終端機也接受絕對路徑(其中的帳號名稱固定為 you;在自己的電腦上請換成 pwd 顯示的帳號名稱),例如 cd C:\Users\you\Downloads\week04-lab\notescd /Users/you/Downloads/week04-lab/notes。

練習:逐步移動 頁面練習

點選一個步驟,先預測結果,再按「執行」。藍底列為目前的工作目錄。也可以直接在下方的終端機輸入指令;點選步驟時,練習用終端機會重設。

    
              
    輸入 help 可列出可用指令。按 Tab 可自動補全名稱,按 ↑ 可叫出上一個指令。

    在自己的終端機執行 動手做

    在自己的終端機中,從 week04-lab 開始依序執行下列指令,並對照上方的結果。之後的「練習:自行移動」會用到這裡建立的 practice。

    pwd
    ls
    cd data
    cd ..
    mkdir practice
    cp README.txt practice/
    cd practice
    cat README.txt
    mv README.txt README-copy.txt
    cd ..

    常用指令

    下表列出本次使用的指令,範例皆以 week04-lab 為工作目錄。每一個指令都可以在上方的練習用終端機中嘗試。

    指令作用範例說明
    pwd顯示目前的工作目錄pwd輸出絕對路徑;PowerShell 會先輸出 Path 標題。
    ls列出資料夾的內容ls data不加名稱時列出目前的資料夾;也可以使用 dir。
    cd切換工作目錄cd ../notescd .. 回到上一層。
    mkdir建立資料夾mkdir practice名稱已存在時會出現錯誤。
    cp複製檔案cp README.txt practice/原檔保留不變。
    mv移動或重新命名mv README.txt README-copy.txt目的地是資料夾時為移動,否則為重新命名。
    cat顯示檔案內容cat README.txt適用於文字檔。
    rm刪除檔案rm README-copy.txt刪除的檔案不會進入資源回收筒或垃圾桶,無法復原。

    練習:自行移動 動手做

    從練習用終端機左側的樹狀圖中任選一個資料夾,例如 notes 或 practice,在終端機中移動到該處,並以 pwd 與 ls 確認位置。接著嘗試只用一個 cd 指令抵達,例如由 data 執行 cd ../notes。

    檢核:能說明分別從 data 與 practice 執行 cd .. 之後所在的位置。

    Git 與 GitHub 儲存庫

    Git 是一種版本控制系統(version control system)。它把資料夾中的修改記錄為一個個 commit(提交,即保存下來的版本),因此可以查看檔案的修改歷史、比較不同版本、回復先前的狀態,也能讓多人分別修改同一組檔案之後再加以合併。以「報告_最終版_v3.docx」這類檔名保存多個副本的做法,難以得知各版本之間的差異;Git 只保留一份檔案,修改的歷史則另外記錄。

    1. b27a9c1Add exit task你的名字 · 10/7 12:58HEAD → main
    2. a90c6e8Resolve conflict in about-me.txt你的名字 · 10/7 12:31
    3. 5f4e3d2Update about-me.txt你的名字(在 GitHub 網頁上)· 10/7 12:20
    4. 8e5b0f7Update about-me你的名字 · 10/7 12:05
    5. 3c1d2a4Add about-me file你的名字 · 10/7 11:48
    6. 9f8e7d6Initial commit你的名字 · 10/7 11:30
    一個儲存庫的 commit 紀錄,最新的在最上方。每一個 commit 都有一組編號(hash)、一句說明,以及作者與時間,之後可以隨時比較或回到其中任何一個版本。這是本次實作結束時 command-line-lab 大致的歷史;git log 會列出同樣的內容。

    由 Git 管理的資料夾稱為儲存庫(repository,簡稱 repo)。Git 在本機運作,不需要網路。GitHub 則是存放儲存庫的網路服務,在遠端保留一份副本,用於備份、分享與協作;許多研究專案與開放原始碼軟體都在 GitHub 上公開程式與資料。本節在 GitHub 上建立一個儲存庫,並讓本機的 Git 取得存取權限。

    1. 確認安裝

    Git 與 GitHub CLI 已在「終端機」一節開始安裝。GitHub CLI(gh)用來在終端機中登入 GitHub;Visual Studio Code(簡稱 VS Code)是文字與程式編輯器,之後的課程也會使用。確認三者都已安裝完成:

    git --version
    gh --version
    code --version

    三行都顯示版本號即完成。若出現 is not recognizedcommand not found,表示尚未安裝完成,或終端機尚未重新開啟。

    2. 以 VS Code 編輯檔案

    本次實作一律從終端機開啟檔案,例如 code about-me.txt。若檔案不存在,VS Code 會開啟一個空白的分頁,按 Ctrl⌘ + S 存檔時,檔案就會建立在目前的工作目錄中,不必在存檔視窗中尋找位置。第一次開啟資料夾時,VS Code 可能詢問是否信任該資料夾的作者,選擇信任即可。

    若 code 指令無法使用,可改用系統內建的編輯器:Windows 執行 notepad about-me.txt,記事本詢問是否建立新檔案時選擇「是」。macOS 先執行 touch about-me.txt 建立空白檔案,再以 open -e about-me.txt 用「文字編輯」開啟。

    檔案總管預設隱藏副檔名,可在「檢視 → 顯示 → 副檔名」開啟顯示。

    3. 註冊 GitHub 帳號

    至 github.com/signup 註冊帳號,並開啟驗證信完成電子郵件驗證。未驗證的帳號可能無法建立儲存庫。

    4. 建立儲存庫

    1. 登入 github.com,點選右上角的 + → New repository。
    2. 在 Repository name 輸入 command-line-lab。
    3. 選擇 Public,並開啟 Add README(部分版面為勾選 Add a README file)。
    4. 點選 Create repository。

    5. 以 GitHub CLI 登入

    gh auth login

    執行後,程式會依序提出四個問題(不同版本的用字可能略有差異)。第一、二、四題以方向鍵選擇,按 Enter 確認;第三題輸入 Y 再按 Enter:

    問題選項
    Where do you use GitHub?GitHub.com
    What is your preferred protocol for Git operations on this host?HTTPS
    Authenticate Git with your GitHub credentials?輸入 Y
    How would you like to authenticate GitHub CLI?Login with a web browser

    接著畫面會顯示一組一次性驗證碼(First copy your one-time code: XXXX-XXXX)。按 Enter 開啟瀏覽器,在頁面上輸入這組驗證碼並點選 Authorize;若瀏覽器沒有自動開啟,請手動前往 github.com/login/device。終端機顯示 Logged in as 帳號名稱,即表示登入完成。

    gh auth status可隨時以此指令確認登入狀態。

    6. 設定使用者名稱與電子郵件

    Git 會把這兩項資訊記錄在每一次 commit 中。每台電腦只需設定一次。將引號內的你的名字、你的信箱換成自己的資料後再執行:

    git config --global user.name "你的名字"
    git config --global user.email "你的信箱"

    由於儲存庫是公開的,這個電子郵件會出現在 commit 紀錄中。若不希望公開,可到 github.com/settings/emails 取得 GitHub 提供的代用信箱(結尾為 users.noreply.github.com)。

    提交與上傳修改

    本節將 GitHub 上的儲存庫複製到本機,新增一個檔案,再讓這個修改依序經過暫存區與本機歷史,最後上傳到 GitHub。

    1. 將儲存庫複製到本機(clone)

    在儲存庫頁面點選綠色的 Code 按鈕,選擇 HTTPS,並點選右側的複製圖示。網址的格式為 https://github.com/帳號名稱/command-line-lab.git。

    GitHub 儲存庫頁面的綠色 Code 按鈕展開,Clone 底下選了 HTTPS,框中是 .git 結尾的網址,右側有複製圖示。
    Code 按鈕展開後的選單。圖中為其他儲存庫的範例,實際的網址結尾應為 command-line-lab.git。

    git clone 會把 GitHub 上的儲存庫完整複製一份到本機,包含所有的修改紀錄。先回到「下載」資料夾,再輸入 git clone 與一個空白,然後貼上剛才複製的網址(在 PowerShell 中按 Ctrl + V 或按右鍵按 ⌘ + V):

    cd ~\Downloads
    cd ~/Downloads
    git clone https://github.com/帳號名稱/command-line-lab.git
    cd command-line-lab
    git status預期輸出包含 On branch main 與 nothing to commit, working tree clean。

    此時「下載」資料夾中有兩個資料夾:

    Downloads/
    ├── week04-lab/          練習移動用的普通資料夾
    └── command-line-lab/    剛才 clone 的儲存庫

    week04-lab 不是儲存庫;以下所有 Git 操作都在 command-line-lab 中進行。

    為什麼分成四個區域

    Git 把一次修改從編輯到分享分成三個步驟,每一步都由使用者決定:

    • git add:選擇哪些修改要納入下一次 commit。例如修改了三個檔案,但只有其中兩個屬於同一件事,就只 add 這兩個,第三個留到下一次。
    • git commit:將暫存區的內容記錄為一個版本,之後可以隨時比較或回復。commit 只存在本機,不需要網路,因此可以先累積幾次 commit 再分享。
    • git push:將本機的 commit 上傳到 GitHub,供他人檢視,也作為備份。上傳會影響共用同一個儲存庫的人,所以與 commit 分開進行。

    另外兩個指令不會移動任何內容,只用來查看:git status 顯示每個檔案目前位於哪一個區域,是最常使用的指令;git log --oneline 列出本機歷史中的 commit。

    四個區域因此分別對應:尚未決定的修改(工作資料夾)、準備記錄的修改(暫存區)、已經記錄的版本(本機歷史),以及已經分享的版本(GitHub)。

    Git 的四個區域 頁面練習

    一次修改在上傳到 GitHub 之前,會依序經過四個區域。以下是模擬,按下按鈕觀察 about-me.txt 的各個版本記錄在哪些區域;實際的指令在下一步執行。add、commit 與 push 會把目前的版本複製到下一個區域,原區域的內容仍然保留。

    工作資料夾

    儲存庫資料夾中實際的檔案,以編輯器修改的就是這些檔案

    暫存區

    預定納入下一次 commit 的內容

    本機歷史

    儲存在本機的 commit 紀錄

    GitHub

    遠端的儲存庫,其他人可以檢視

    
          

    各指令的動畫示範(英文,點選 Show Visualization):git add、git commit、git push(Gitualize)

    2. 新增 about-me.txt

    在 command-line-lab 中,從終端機建立並開啟 about-me.txt:

    code about-me.txt

    寫入一句話說明想研究的題目,存檔後關閉分頁。回到終端機執行 ls,應可看到 README.md 與 about-me.txt。

    執行 code . 則會以 VS Code 開啟整個資料夾,左側會列出其中的檔案。

    3. 暫存、提交與上傳

    逐行執行下列指令。每一步的預期結果列在指令下方,可對照上方「Git 的四個區域」的模擬。

    git statusabout-me.txt 列在 Untracked files 之下:修改位於工作資料夾。
    git add about-me.txt沒有輸出。
    git status改列在 Changes to be committed 之下:修改位於暫存區。
    git commit -m "Add about-me file"顯示類似 [main 3c1d2a4] Add about-me file 的訊息:修改已寫入本機歷史。
    git log --oneline最上方一行是剛才的 commit。
    git push最後一行為 main -> main:修改已上傳到 GitHub。

    重新整理 GitHub 上的儲存庫頁面,應可看到 about-me.txt。

    4. 第二次修改 動手做

    以 code about-me.txt 開啟檔案,修改內容並存檔,執行 git status 與 git diff 觀察輸出。git diff 會顯示尚未 add 的修改,例如:

    @@ -1 +1 @@-我想研究台語的聲調。+我想研究台語和客語的聲調。

    接著不參照上方的指令,依照四個區域的順序自行完成 add、commit 與 push。commit 訊息可以自訂,例如 "Update about-me"。完成後重新整理 GitHub 頁面,確認內容已更新。

    5. 衝突 動手做

    同一個檔案的同一處若在兩個地方分別被修改,Git 無法判斷應保留哪一個版本,這種情形稱為衝突(conflict)。多人協作時,衝突通常發生在兩人修改了同一段文字;一個人使用時,也可能因為在 GitHub 網頁與本機分別修改而發生。以下刻意製造一次衝突,再加以解決。

    1. 在 GitHub 的儲存庫頁面點選 about-me.txt,再點選右上角的鉛筆圖示(Edit this file)。修改句子後,點選 Commit changes,並在跳出的對話框中再按一次 Commit changes。頁面上顯示修改後的句子即完成。

    2. 不執行 git pull,直接在本機以編輯器把同一行改成另一個句子,存檔後執行:

      git add about-me.txt
      git commit -m "Update about-me again"
      git push

      GitHub 上有一個本機沒有的 commit,因此 push 會被拒絕:

       ! [rejected]        main -> main (fetch first)
      error: failed to push some refs to 'https://github.com/帳號名稱/command-line-lab.git'
      hint: Updates were rejected because the remote contains work that you do not
      hint: have locally. ...
    3. 取得 GitHub 上的修改,並與本機的修改合併:

      git pull --no-rebase

      --no-rebase 指定以合併(merge)的方式整合兩邊的修改:保留兩條歷史,再以一個合併的 commit 連接。另一種方式是 rebase,它把本機新增的 commit 暫時移開,接在 GitHub 最新的 commit 之後重新套用一次,歷史因此成為一直線;本次實作不使用 rebase。若只執行 git pull,新版 Git 會先要求選擇整合方式,並顯示 fatal: Need to specify how to reconcile divergent branches. 後停止。

      由於兩邊修改了同一行,合併無法自動完成:

      Auto-merging about-me.txt
      CONFLICT (content): Merge conflict in about-me.txt
      Automatic merge failed; fix conflicts and then commit the result.
    4. 以 code about-me.txt 開啟檔案,可以看到 Git 加入的衝突標記。VS Code 會以顏色區分兩個版本,並在上方提供「Accept Current Change」(保留本機)、「Accept Incoming Change」(保留 GitHub)與「Accept Both Changes」(兩者都保留)等選項;也可以直接手動編輯。若使用其他編輯器且檔案原本已開啟,請先關閉再重新開啟。

      <<<<<<< HEAD
      我想研究台語的語法。
      =======
      我想研究客語的聲調。
      >>>>>>> 2b55f62e69575191df2490cf5129a012b2badb75

      <<<<<<< HEAD 與 ======= 之間是本機的版本,======= 與 >>>>>>> 之間是 GitHub 上的版本。HEAD 指本機目前的版本;最後一行的長字串是 GitHub 上那個 commit 的編號。將內容改成最後要保留的文字,刪除三行標記,然後存檔。存檔後,檔案中不應再有 <<<<<<<、======= 或 >>>>>>>。

      整理的方式有幾種,例如兩邊都保留(即「Accept Both Changes」的結果):

      我想研究台語的語法。
      我想研究客語的聲調。

      或把兩邊合併成一句:

      我想研究台語的語法與客語的聲調。

      只保留其中一邊也可以。重點是三行標記都已刪除,檔案中只剩最後要保留的文字。

    5. 完成合併並上傳:

      git add about-me.txt
      git commit -m "Resolve conflict in about-me.txt"
      git push

      此處執行 git add,是告知 Git 衝突已經解決。執行 git log --oneline --graph,可以看到兩條分開的歷史在合併的 commit 匯合。

    6. commit 的圖形表示

    Learn Git Branching 以圖形呈現 commit 歷史。開啟後會直接進入「基礎篇」第一關〈介紹 git commit〉。閱讀說明並關閉視窗後,依指示執行兩次 git commit,圖中會新增兩個節點。

    圖中的每一個節點,對應 git log --oneline 輸出中的一行。第 5 步的 git pull 已經產生過一次合併;建立分支與 rebase 的操作則不在本次實作範圍內。

    Git 的使用情境

    凡是會反覆修改、需要保留修改過程的檔案,都適合以 Git 管理。常見的情境如下:

    個人寫作
    以 LaTeX 或 Markdown 撰寫論文、講義時,每完成一段就 commit。git diff 可以逐行顯示兩個版本之間的差異,比人工對照兩份文件清楚得多(圖一)。
    撰寫程式
    在程式可以正常執行時 commit。之後若程式出錯,可以查看改了哪些地方,或回到先前可以執行的版本。
    重大修改之前
    先建立分支(branch),在分支上嘗試新的寫法。確定可行後再合併(merge)回主線;不可行就捨棄分支,主線不受影響。重要的版本可以加上標籤(tag),例如投稿版或 v1.0,日後可以直接回到該版本(圖二、圖五)。
    與他人協作
    每個人在各自的分支上工作,透過 GitHub 的 pull request 檢視彼此的修改,再決定是否合併(圖三)。
    與 AI 代理人協作
    讓代理人在獨立的分支上修改,先以 git diff 檢視它改了什麼,再決定是否合併;結果不理想時,捨棄該分支即可。多個代理人同時開發不同的功能時,各自使用一個分支(或以 git worktree 建立各自的工作資料夾),彼此不會覆蓋對方的修改。代理人完成的 commit 也會記錄在歷史中(圖四)。

    git diff:以行為單位,有改動的整行標為刪除(-)與新增(+)

    diff --git a/paper.tex b/paper.tex--- a/paper.tex+++ b/paper.tex@@ -1,3 +1,3 @@11 \section{研究方法}2-本研究使用 BPE 切分台語語料。2+本研究比較 BPE 與 Unigram LM 兩種方法切分台語語料。33 語料來源為教育部臺灣台語常用詞辭典。

    git diff --word-diff:同一個修改,只標出改動的字詞

    1\section{研究方法}2[-本研究使用-]{+本研究比較+} BPE [-切分台語語料。-]{+與 Unigram LM 兩種方法切分台語語料。+}3語料來源為教育部臺灣台語常用詞辭典。

    將滑鼠移到上方任一行,或點選該行,這裡會顯示該行的意義。

    圖一 同一個修改的兩種顯示方式。上方逐行標示:- 開頭的是刪除的行,+ 開頭的是新增的行;左側兩欄是行號,左欄為修改前、右欄為修改後,刪除的行只有左欄、新增的行只有右欄。下方為加上 --word-diff 的結果,以字詞為單位標示。
    三條分支的 commit 圖:main 主線;experiment 分支合併回 main;agent-idea 分支被捨棄;v1.0 標籤位於合併之後的 commit。 main experiment agent-idea(捨棄) 合併 建立分支 v1.0
    圖二 分支與標籤。experiment 分支確定可行後合併回 main;agent-idea 分支的結果不理想,因此捨棄,main 不受影響;v1.0 標籤標記投稿的版本。

    以下三張圖取自 Hugging Face 的 transformers 儲存庫。這是許多語言模型程式使用的開放原始碼套件,自 2018 年起以 Git 共同維護。截至 2026 年 9 月 30 日,它的規模如下:

    commit
    約 24,000
    貢獻者
    約 4,100
    已合併的 pull request
    約 20,600
    檢視中的 pull request
    約 1,600
    分支
    約 1,600
    版本標籤
    289

    若沒有版本控制,數千人只能輪流傳遞同一份檔案:一次只有一個人可以修改,其他人都必須等待,就像一次只允許一個人通過的號誌。Git 讓每個人在自己的分支上同時工作,修改完成後再經過檢視與合併,因此上千個修改可以同時進行。

    transformers 的 pull request #49075:Cyrilvallez 將 fix-assisted 分支的 2 個 commit 合併到 main;Files changed 分頁顯示 src/transformers/generation/utils.py 刪除一行、新增兩行。
    圖三 一個 pull request。貢獻者在 fix-assisted 分支上完成修改,檢視通過後將 2 個 commit 合併到 main。Files changed 分頁以紅色與綠色標示刪除與新增的行,與圖一的 git diff 相同。
    transformers 中 README.md 的修改歷史:每一筆 commit 都有說明、作者、日期與編號;其中一筆的作者為 ydshieh 與 claude。
    圖四 README.md 的修改歷史。每一筆 commit 都有說明、作者、日期與編號(如 64f3045),點選即可查看當次的 diff。第二筆的作者為 ydshieh and claude,是人與 AI 代理人共同完成的 commit。
    transformers 的 Tags 頁面:v5.17.0、v5.16.1、v5.16.0 等版本標籤,各自對應一個 commit 編號,並提供 zip 與 tar.gz 下載。
    圖五 版本標籤。每一個發行版本(如 v5.17.0)都是一個 tag,指向特定的 commit,任何人都可以下載當時的程式碼。

    相關指令:git switch -c experiment 建立並切換到新分支,git switch main 回到主線,git merge experiment 將分支合併回目前的分支,git tag v1.0 為目前的 commit 加上標籤,git log --oneline --graph --all 顯示所有分支的圖。本次實作只使用 main 分支;分支的練習見「延伸閱讀」中的 Learn Git Branching。

    常見錯誤
    訊息原因與處理
    Author identity unknown尚未設定使用者名稱與電子郵件。請完成「Git 與 GitHub 儲存庫」的第 6 步。
    git push 要求輸入 Username、Password按 Ctrl + C 取消,執行 gh auth setup-git 後再 push 一次。
    fatal: not a git repository目前不在 command-line-lab 中。以 pwd 確認位置後,執行 cd ~\Downloads\command-line-labcd ~/Downloads/command-line-lab。
    pathspec 'about-me.txt' did not match any files檔案不在這個資料夾中、尚未存檔,或檔名實際上是 about-me.txt.txt、about-me.rtf。以 ls 查看實際檔名。
    執行 git commit 後出現陌生的編輯畫面未加 -m,Git 因此開啟了 Vim 編輯器。按 Esc,輸入 :q! 再按 Enter 離開,然後加上 -m 重新執行。
    fatal: Need to specify how to reconcile divergent branches.改用 git pull --no-rebase。
    ! [rejected] ... (fetch first)GitHub 上有本機沒有的 commit。先執行 git pull --no-rebase,再 push。
    LF will be replaced by CRLF換行格式的提示,不影響操作。
    帳號驗證或登入無法完成請找助教協助,並先完成 commit 與正規式的部分,push 稍後再補。

    正規式

    正規式(regular expression,簡稱 regex)是描述文字樣式的表示法。一般的搜尋只能尋找固定的字串,例如「台灣」;正規式則可以描述一類字串,例如「以 09 開頭的十位數字」或「位於行首的學號」。

    正規式常用於下列工作:在大量文字中找出符合某種樣式的字串,例如在語料庫中找出所有疊字;檢查資料的格式,例如電子郵件或電話號碼;從文字中擷取特定欄位;清理雜亂的文字,例如刪除網頁擷取時殘留的 HTML 標籤、把連續的空白合併成一個、統一全形與半形的標點;以及把文字切分成較小的單位。語言模型的 tokenizer 也在兩個階段使用正規式:先以正規式清理與統一文字(normalization),再以正規式把文字切成小段(pre-tokenization,見本節第 8 小節)。多數程式語言、文字編輯器與命令列工具都支援正規式。

    以清理文字為例。下表把一行從網頁擷取的文字依序處理,每一步以一條正規式找出要處理的部分,再換成指定的內容。前三步屬於 normalization,最後一步屬於 pre-tokenization。表中各符號的意義在本節後半說明,此處只需比較每一步前後的差異。

    步驟正規式取代為處理後
    原始文字<p>我想研究□台語的聲調␣,↵也想研究客語␣。</p>
    刪除 HTML 標籤<[^>]+>(刪除)我想研究□台語的聲調␣,↵也想研究客語␣。
    刪除空白與換行\s+(刪除)我想研究台語的聲調,也想研究客語。
    半形逗號改為全形,,我想研究台語的聲調,也想研究客語。
    切分詞元(挑出)\p{Script=Han}|\w+|[^\w\s]
    我想研究台語的聲調,也想研究客語。

    將滑鼠移到表中正規式的任一部分,或點選該部分,這裡會顯示其意義。

    表中 ␣ 代表半形空白,□ 代表全形空白,↵ 代表換行。\s 同時符合這三種字元。中文不以空白分詞,所以第二步直接刪除空白;處理英文時則應改為取代成一個半形空白。最後一步的規則即第 8 小節的「漢字逐字」。

    本節使用 RegExr 撰寫正規式,符合的部分會即時標示出來。

    1. 準備 RegExr

    開啟本課準備的 RegExr 練習頁。畫面中央標示 Text 的大框中已有下列四行聯絡資料:

    Alice Chen alice@example.com 0912-345-678 Bob Wang bob.wang@ntu.edu.tw 0928-123-456 Carol Lin carol99@gmail.com 0988-765-432 Office office@example.com 02-2345-6789

    上方 Expression 區的兩條斜線之間有一條範例正規式 [A-Z],先將它刪除,之後的正規式都寫在這兩條斜線之間。若練習頁無法開啟,可改開 RegExr 首頁,把 Text 框中原有的文字換成上面四行。

    RegExr 畫面:上方 Expression 區是 /Lin/g,下方 Text 區貼了四行聯絡資料,Carol Lin 的 Lin 以藍色標示。
    貼上文字後的畫面。此處的 Expression 以 Lin 為例,符合的部分以藍色標示。

    2. 基本符號

    正規式中的大多數字元代表字元本身,例如 Lin 就是比對 Lin 這三個字母。下列符號則有特殊意義,更完整的說明見正規式 ELI5:

    符號意義範例
    \d任一數字\d 比對 7
    +前一個元素出現一次以上\d+ 比對 2026
    {n}前一個元素恰好出現 n 次\d{3} 比對 345
    |或cat|dog 比對 cat 或 dog
    ^一行的開頭^S 比對行首的 S

    3. 練習 動手做

    1. 使每一個數字各自成為一個比對結果。

      提示

      表示「任一數字」的寫法以反斜線開頭,共兩個字元。

    2. 使連續的數字成為一個比對結果。

      提示

      在上一題的寫法之後,加上表示「一個以上」的符號,並觀察 0912-345-678 被分成幾段及其原因。

    3. 只比對三組手機號碼,不包含辦公室號碼 02-2345-6789。

      提示 1

      手機號碼都以 09 開頭,可直接照字面寫出。

      提示 2

      {3} 表示恰好三個。連字號 - 也照字面寫出。

    4. 比對 Alice 或 Bob。

      提示

      「或」以直線 | 表示。完成後,觀察信箱中的 alice、bob 是否被比對,並思考原因。

    4. 行首 動手做

    將 Text 替換為下列內容:

    S001 Alice Chen S002 Bob Wang X003 Carol Lin

    先點選 Expression 右上角的 Flags,勾選 multiline。預設情況下,^ 只比對整段文字的開頭;啟用 multiline 後,^ 才會比對每一行的開頭。

    RegExr 的 Flags 選單展開,列出 global、case insensitive、multiline、single line、unicode、sticky,目前只勾選 global。
    Flags 選單位於 Expression 區右上角。勾選 multiline 後,Expression 的結尾由 /g 變為 /gm。

    練習:比對 S001 與 S002,但不包含 X003。完成後取消 multiline,觀察哪一行不再被比對。

    提示

    ^ 表示一行的開頭。

    5. 更多符號 動手做

    下列符號與第 2 小節的符號可以任意組合:

    符號意義範例
    .任一字元(換行除外)b.b 比對 bob
    *前一個元素出現零次以上ab* 比對 a、ab、abb
    ?前一個元素可有可無colou?r 比對 color 與 colour
    [abc]方括號中的任一字元[aeiou] 比對任一母音字母
    [a-z]範圍內的任一字元[A-Z] 比對任一大寫字母
    [^abc]方括號中以外的任一字元[^\d] 比對任一非數字的字元
    \w英文字母、數字或底線\w+ 比對 carol99
    \s空白字元,包括空格、Tab 與換行\s+ 比對一串空白
    \b詞的邊界,本身不佔字元\bLin\b 不比對 Linda 中的 Lin
    $一行的結尾(須啟用 multiline)\d+$ 比對行尾的數字
    \.句點本身。其他特殊符號也以反斜線表示其本身,例如 \+、\?\.tw 比對 .tw

    將 Text 換回第 1 小節的四行聯絡資料(重新開啟練習頁亦可),並保持 multiline 為勾選狀態。

    1. 比對每一行的第一個單字。

      提示

      行首加上表示「一個以上的英文字母或數字」的寫法。

    2. 比對每一行最後一組數字。

      提示

      以 $ 表示行尾。

    3. 比對全部四個電子郵件地址。

      提示

      信箱由英文字母、數字與句點組成,中間有一個 @。[\w.] 表示「\w 或句點」;在方括號中,句點只代表句點本身。

    4. 只比對以 .com 結尾的電子郵件地址。

      提示

      在方括號以外,句點代表任一字元,因此 .com 的句點須寫成 \.。

    6. 群組與反向參照 動手做

    以圓括號 ( ) 括起的部分稱為群組(group)。群組有兩種用途:一是把多個字元視為一個整體,例如 (ha)+ 比對 ha、haha、hahaha;二是記住該部分實際符合的內容,稱為擷取(capture)。由左至右,第一個群組擷取的內容以 \1 表示,第二個以 \2 表示,依此類推。在正規式中寫 \1 稱為反向參照(backreference),表示「與第一個群組擷取的內容相同」。只需要整體、不需要擷取時,可以寫成 (?:ha)+。

    (.)\1 的比對過程 頁面練習

    正規式由左至右逐一嘗試每個位置。按「下一步」觀察每個位置發生了什麼。

    (.) 擷取的字 \1 與擷取的字比較   已符合

    取代。擷取的內容也可以用於取代。點選 RegExr 畫面下方 Tools 區的 Replace,在其中輸入取代後的格式,$1 代表第一個群組擷取的內容,$2 代表第二個。以四行聯絡資料為例:

    1. 以 Expression ^(\w+) (\w+) 與 Replace $2, $1,把 Alice Chen 改寫成 Chen, Alice。觀察 Office 那一行為何沒有改變。

      提示

      比較 Office 之後與 Alice 之後的字元。

    疊字。將 Text 換成下列內容:

    他高高興興地走了。 這件事我們研究研究再說。 天天下雨,路上慢慢走。 價格是 120 元,運費 60 元。
    1. 比對所有連續兩個相同的字,例如 天天。

      提示

      以 (.) 擷取任一個字,再以 \1 要求下一個字與它相同。

    2. 比對 AABB 式的疊字,例如 高高興興。

      提示

      需要兩個群組,分別以 \1 與 \2 參照。

    3. 比對 ABAB 式的疊字,例如 研究研究。

      提示

      先擷取兩個字,再依相同的順序重複一次。

    7. 萬用字元與正規式

    終端機中的檔名也可以用樣式表示,這種樣式稱為萬用字元(wildcard,又稱 glob)。例如 ls data/*.csv 會列出 data 中所有以 .csv 結尾的檔案。在 macOS 的終端機中,萬用字元由 shell 在執行指令之前展開成符合的檔名清單。在 PowerShell 中,萬用字元由各個指令自行處理,結果相同。

    萬用字元與正規式使用部分相同的符號,但意義不同:

    符號萬用字元正規式
    *任意長度的任意字元前一個元素重複零次以上;「任意長度的任意字元」須寫成 .*
    ?任意一個字元前一個元素可有可無;「任意一個字元」須寫成 .
    [0-9]一個數字一個數字

    兩者是不同的表示法,萬用字元並不是正規式的一部分:

    • 萬用字元必須符合整個檔名,相當於正規式前後加上 ^ 與 $;正規式則會在文字中的任何位置尋找符合的部分。
    • 萬用字元中的 . 只代表句點;正規式中的 . 代表任意一個字元。
    • 萬用字元沒有 +、{3}、|、\d 等寫法,能描述的樣式少得多。

    下列指令須在 week04-lab 中執行,可使用「在資料夾之間移動」的練習用終端機,或在自己的終端機中先執行 cd ~\Downloads\week04-labcd ~/Downloads/week04-lab。第三行須先建立 practice。

    ls data/*.csv
    ls data/results_202?.csv
    cp data/*.csv practice/

    8. 以正規式切分詞元 頁面練習

    語言模型處理文字之前,先由 tokenizer 把文字切成一個個詞元(token),模型所認得的全部詞元稱為詞彙表(vocabulary)。tokenizer 先清理與統一文字(normalization,見本節開頭的表),接著進行 pre-tokenization,通常以一條正規式把文字切成小段(詳見 Lecture 03)。正規式在此有兩種用法:「切開」以符合的部分作為分界,丟掉分界,保留分界之間的文字;「挑出」只保留符合的部分,丟掉其餘的文字。規則描述的是分界時用「切開」,描述的是詞元本身時用「挑出」。以 I don't know. 為例:

    • 以 \s+ 切開:得到 I、don't、know.。空白是分界,標點仍連在字上。
    • 以 \w+ 挑出:得到 I、don、t、know。撇號與句點不符合 \w,因此被丟掉。

    「切開」與「挑出」分別相當於 Python 的 re.split 與 re.findall。GPT-2 的規則與本節開頭表格的最後一步都屬於「挑出」。

    用法

    符合之處(「切開」時標示的是被丟掉的分界;規則以 | 分成幾段時,各段以不同顏色標示)
    切出的詞元(空白以 ␣ 表示)

    討論

    1. 以 \s+ 切開英文句子時,don't 與 words; 的結果為何?
    2. 以同一條規則切開中文句子,會得到幾個詞元?原因為何?
    3. 選擇「\w+ 挑中文」範例:為什麼 \w+ 無法比對任何中文字?
    4. GPT-2 的規則把空白歸到哪一側?這對詞彙表有什麼影響?

    pre-tokenization 的詳細說明見 pre-tokenization ELI5。Hugging Face 的 tokenizers 文件說明完整的流程:normalization、pre-tokenization、模型切分與後處理(post-processing),其中前兩個步驟都可以用正規式設定(normalizers.Replace 與 pre_tokenizers.Split)。

    9. LLM 的 tokenizer

    本頁的工具只做到 pre-tokenization,也就是以一條規則把文字切成小段。實際的 LLM tokenizer 還會再把每一小段切成詞彙表中的單位,並把每個詞元換成一個編號(ID),模型處理的就是這串編號。與正規式相比,主要有三點不同:

    • 切分方式由資料決定。詞彙表由 BPE 等演算法從大量語料中統計常見的字元組合而得,而不是由人撰寫規則。常見的詞成為一個詞元,少見的詞則拆成幾段,例如 GPT-2 把 unbelievably 切成 un、bel、iev、ably(詳見 BPE ELI5)。
    • 以位元組為最小單位。GPT 系列的 tokenizer 先把文字轉為 UTF-8 位元組,因此任何文字都能表示。詞彙表中沒有的字會拆成位元組:「詞」的 UTF-8 編碼是 e8 a9 9e 三個位元組,在 GPT-2 中就成為三個詞元。
    • 不同模型的切法不同。詞彙表的大小與內容依模型而異。下表比較 GPT-2(詞彙表 50,257 個詞元)與 GPT-4o(約 200,000 個)對同樣兩句話的切分,灰色方塊是無法單獨顯示為文字的位元組:
    模型切分結果詞元數
    GPT-2
    Tokenizers␣don't␣see␣words;␣they␣see␣strings.
    11
    GPT-4o
    Tokenizers␣don't␣see␣words;␣they␣see␣strings.
    10
    GPT-2
    e8a99ee5 8583不是e8a99eefbc8ce4 b99f不是e8 aa9ee7b4a0。
    23
    GPT-4o
    詞元不是詞,也不是語素。
    9
    上表第三列開頭「詞元不是」的放大。每個中文字是三個位元組。GPT-2 的詞彙表中有「不」與「是」,沒有「詞」與「元」,因此「詞」拆成三個位元組,「元」則在字的中間被切開。GPT-4o 的詞彙表較大,「不是」兩個字合成一個詞元。詞元的邊界不一定與字的邊界相同。

    同一句中文,GPT-2 需要 23 個詞元,GPT-4o 只需要 9 個。詞元數決定了模型能讀入的文字長度與使用 API 的費用,因此詞彙表涵蓋哪些語言,會直接影響該語言的使用成本。各模型的切分結果可在 Tiktokenizer 中比較。

    BPE 如何學出詞彙表 頁面練習

    左側是一份小型語料,已先以 pre-tokenization 切成詞,右邊數字是出現次數。BPE 從單一的字開始,每一步找出語料中最常相鄰出現的一對單位,把它合併成新的詞元,加入詞彙表。實際的 GPT tokenizer 從位元組開始,此處為了易讀,從字開始。

    語料
    相鄰的組合與次數
      已學到的合併規則(依學到的順序)
        以目前的規則切分新的句子「研究生命起源」

        10. 正規式的限制

        正規式適合描述局部、固定的字串樣式,但有下列限制:

        • 只處理形式,不處理意義。(.)\1 能找出 天天、慢慢,(.)(.)\1\2 能找出 研究研究,但無法判斷這些疊字分別屬於名詞、形容詞或動詞,也無法判斷多音字「行」在句中讀作 xíng 或 háng。
        • 無法切分中文詞。「研究生命起源」可以切成「研究/生命/起源」,也可以切成「研究生/命/起源」。正規式只能依字元的種類切分,判斷哪一種切法正確則需要詞典或統計模型。這也是 tokenizer 以 BPE 等方法從資料中學習詞元的原因之一。
        • 無法處理任意深度的巢狀結構。例如成對的括號 ((()))、層層包覆的 HTML 標籤,或英文的中心嵌入句 The rat the cat the dog chased bit died。這類結構需要記住「開了幾層」,而正規式沒有計數的能力。在形式語言理論中,正規式描述的是正規語言(regular language),位於喬姆斯基階層(Chomsky hierarchy)的最低一層;巢狀結構屬於上一層的上下文無關語言(context-free language),須以剖析器(parser)處理。反向參照雖然使實際的正規式超出正規語言的範圍,例如 ^(.+)\1$ 可比對「同一字串重複兩次」,但 JavaScript 與 Python 的 re 仍無法處理任意深度的巢狀結構。
        • 不易閱讀與維護。上方 GPT-2 的規則已不易一眼看懂。規則越長,越難確認它涵蓋了所有情況,也越難修改。某些寫法在特定輸入下還會因反覆回溯(backtracking)而執行得極慢,例如以 (a+)+$ 比對一長串 a 之後接一個 b 的文字。
        • 不處理字元的編碼差異。外觀相同的字元可能有不同的編碼,例如全形與半形的標點,或以組合字元表示的帶調字母。比對之前須先統一文字,即本節開頭表格中的 normalization。

        括號與計數 頁面練習

        括號是否配對,取決於每個位置「開了幾層」。下方的長條是讀到每個字元時的層數:左括號加一,右括號減一,配對的括號最後回到 0,且中途不會低於 0。比較正規式 ^\(+\)+$(若干個左括號接若干個右括號)的判斷。

        因此,需要結構、意義或統計的工作,通常交給剖析器、詞典或語言模型處理,正規式則負責前後的清理與擷取。

        綜合練習 動手做

        本練習結合終端機、正規式與 Git。在 command-line-lab 中以 code participants.txt 建立檔案,寫入下列內容並存檔:

        S001 Alice Chen S002 Bob Wang X003 Carol Lin
        第 1 項提示

        先以 pwd 確認位於 command-line-lab,再依序使用 ls 與 cat。

        第 5 項提示

        git add 之後可以一次接多個檔名,以空白分隔。commit 訊息可以自訂。

        完成後的儲存庫

        command-line-lab/
        ├── README.md
        ├── about-me.txt
        ├── participants.txt
        └── answers.txt

        此外,應能對照 Git 的四個區域,說明下列四種狀態的差異:已修改但尚未 add、已 add 但尚未 commit、已 commit 但尚未 push,以及已上傳到 GitHub。

        延伸閱讀

        • GitHub Skills: Introduction to Git:GitHub 官方的互動課程。將練習儲存庫複製到自己的帳號後,依指示逐步完成,內容包含 commit、歷史紀錄與分支;課程使用 VS Code。
        • Gitualize:各個 Git 指令的說明與動畫示範,包含本次未詳細說明的 pull、branch 與 merge。
        • Learn Git Branching:分支與合併的互動練習。
        • RegexOne:正規式分題練習。
        • Software Carpentry: The Unix Shell:命令列入門教材。教材使用 Unix 的選項,其中部分在 PowerShell 中不適用。

        參考答案

        逐步移動
        1. pwd 輸出 C:\Users\帳號名稱\Downloads\week04-lab/Users/帳號名稱/Downloads/week04-lab,最後一段為 week04-lab。
        2. 共三項:README.txt、data、notes。
        3. 藍底移到 data,pwd 的輸出末端增加 data。
        4. .. 表示上一層資料夾,因此回到 week04-lab。
        5. week04-lab 之下新增 practice。
        6. 仍然存在。cp 是複製,不影響原檔。
        7. 一個:README.txt。
        8. You found the README. If you can read this sentence in your terminal, cat works.
        9. README-copy.txt。mv 同時用於移動與重新命名。
        10. 仍為 week04-lab。.. 一律指目前資料夾的上一層。

        練習:自行移動的檢核:從 data 或 practice 執行 cd ..,都會回到 week04-lab,因為兩者都位於 week04-lab 之內。

        Git 的四個區域
        操作修改所在區域git status 的輸出
        存檔工作資料夾Untracked files(新檔案)或 Changes not staged for commit(已追蹤的檔案)
        git add暫存區Changes to be committed
        git commit本機歷史(GitHub 尚未更新)Your branch is ahead of 'origin/main' by 1 commit
        git pushGitHubYour branch is up to date with 'origin/main'
        正規式練習
        1. \d。每個數字各自為一個比對結果,包含 carol99 中的 9。
        2. \d+。+ 會盡可能比對更多字元。0912-345-678 被分為三段,因為 - 不是數字。
        3. 09\d{2}-\d{3}-\d{3}。02-2345-6789 不以 09 開頭,因此不會被比對。
        4. Alice|Bob。信箱中的 alice、bob 不會被比對,因為正規式預設區分大小寫;在 Flags 勾選 case insensitive 後即會比對。
        5. 行首:^S\d{3},並須啟用 multiline。若取消 multiline,只有 S001 會被比對,因為 ^ 只比對整段文字的開頭。
        Regexper 產生的鐵路圖:09,接 digit,接連字號,接 digit,接連字號,接 digit。三個 digit 下方的迴圈分別標示 once、2 times、2 times。
        Regexper 為 09\d{2}-\d{3}-\d{3} 畫出的鐵路圖。由左至右沿著線走一遍,經過的方框依序組成一個符合的字串。每個 digit 方框代表一位數字,下方的迴圈表示走過方框之後再回頭重複,迴圈上標示的是重複的次數:once 是一位再重複一次,共兩位;2 times 是一位再重複兩次,共三位。

        只寫 S\d{3} 在這份資料中也能得到正確結果,但也會比對到行中的 XS004 這類字串;加上 ^ 可以明確限定位置。

        更多符號、群組與反向參照
        1. ^\w+,比對 Alice、Bob、Carol、Office。須啟用 multiline。
        2. \d+$,比對 678、456、432、6789。須啟用 multiline。
        3. [\w.]+@[\w.]+。
        4. [\w.]+@\w+\.com,比對三個信箱,不包含 bob.wang@ntu.edu.tw。若寫成 .com,句點會比對任一字元。

        群組與反向參照:

        1. 前三行改寫成 Chen, Alice、Wang, Bob、Lin, Carol。Office 之後是一串空白,接著才是信箱;正規式中兩個 \w+ 之間只允許一個空格,因此這一行不符合。
        2. (.)\1,比對 高高、興興、天天、慢慢。
        3. (.)\1(.)\2,比對 高高興興。
        4. (.)(.)\1\2,比對 研究研究。
        討論:以正規式切分詞元
        1. don't 維持為一個詞元,words; 的分號也附在詞後。以空白切分只依據空白,不處理標點。
        2. 一個,即整個句子。中文書寫不以空白分詞,因此沒有分界點。
        3. 在 JavaScript(RegExr 即使用此規則)中,\w 只相當於 [A-Za-z0-9_],因此無法比對漢字;比對漢字須使用 \p{Script=Han}。Python 3 的 re 則預設支援 Unicode,\w 可以比對漢字,但 \p{Script=Han} 須改用 regex 套件。
        4. 空白附在後一個詞的前面,例如 ␣see、␣they。因此同一個詞出現在句首(前面沒有空白)與句中(前面有空白)時,例如 see 與 ␣see,在詞彙表中是兩個不同的詞元。
        綜合練習:answers.txt 範例
        ^S\d{3}
        flag: m (multiline)

        第 5 項可以執行:

        git add participants.txt answers.txt
        git commit -m "Add exit task"
        git push