National Yang Ming Chiao Tung University
Uedu Main Site
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try a Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

1

Local AI

--

Uedu Code

--

陽明交大 AQI 69 24°C PM2.5 26
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 22

正規表示法與表單驗證

RegExp、字元類別、量詞與送出前的檢查
Interaction實作 25 minute(s)

Learning Objectives

  • 說明正規表示法的用途,用字面值 /pattern/flags 與 new RegExp 建立模式,並使用 test、match、search、replace、split
  • 用 .、[ ]、[^ ] 與 \d、\w、\s 等表示法比對單一字元
  • 用 *、+、?、{m,n} 指定重複次數,用 ^、$ 限定開頭結尾,用 ( ) 與 | 組成群組,並使用旗標 g、i、m
  • 在表單送出前用 required、pattern 屬性與 JavaScript 檢查欄位格式

正規表示法:描述文字的樣子

「09 開頭的 10 個數字」「一個大寫英文字母加 9 個數字」「有 @ 的電子郵件」,這些都是在描述文字的樣子。 正規表示法(regular expression,常簡稱 regex)就是用一串符號寫下這種規則,這串規則叫做模式(pattern)。

常見的用途:

  • 驗證使用者輸入的資料欄位(手機號碼、身分證格式、密碼強度)
  • 尋找或取代字串中符合規則的部分(把所有數字遮成 *)
  • 擷取字串中的某一部分(從日期字串取出年、月、日)

建立的方式有兩種:

const re1 = /a[bcd]e/i;                  // 1. 字面值:/模式/旗標,載入程式時就編譯好,模式固定時使用
const re2 = new RegExp('a[bcd]e', 'i');  // 2. 建構函式:模式是字串,可以在執行時才組出來

用 new RegExp 時,模式是一般字串,反斜線要寫兩次:/\d+/ 要寫成 new RegExp('\\d+')。 如果寫成 new RegExp('\d+'),字串裡的 \d 只剩下 d,模式就變成「一個以上的 d」。

方法 回傳
re.test(str) 有沒有符合:true/false
str.search(re) 第一個符合的位置;沒有是 -1
str.match(re) 沒有 g:第一個符合的結果(陣列,含 index);有 g:所有符合的文字;都沒有是 null
re.exec(str) 與沒有 g 的 match 相同
str.replace(re, 新字串) 取代符合的部分(有 g 才會取代全部)
str.split(re) 用符合的部分把字串切成陣列

建立模式與常用方法

逐步播放,看每個方法的回傳值。特別注意第 7 行:字串 '\\d+' 裡寫了兩個反斜線,digits.source 印出來的才是真正的模式 \d+。

單一字元表示法

一般的字母與數字代表它自己:/abc/ 比對連續的 abc。下面這些符號則代表「某一類」字元,每個只比對一個字元:

寫法 意思 例子
. 任何一個字元(換行除外) a.c:abc、a c、azc 符合;ac、abbc 不符合
[bcd] 中括號裡的任一個字元 a[bcd]e:abe、ace、ade 符合
[^bcd] 不是中括號裡的任一個字元 a[^bcd]e:afe 符合;abe、ae 不符合
[a-z]、[A-Z]、[0-9] 範圍 [A-Z] 是一個大寫英文字母
\d/\D 數字 [0-9]/非數字
\w/\W 文字字元 [a-zA-Z0-9_]/非文字字元 注意:\w 不包含中文
\s/\S 空白字元(空白、tab、換行)/非空白
\ 跳脫特殊符號,例如 \. 比對真正的點、\/ 比對斜線

小寫是「是」,大寫是「不是」:\d 與 \D、\w 與 \W、\s 與 \S 互為相反。 ^ 放在中括號裡面的開頭表示相反;放在模式的開頭則是「字串開頭」(下一節)。

互動練習:單一字元

點上方的按鈕切換模式,或自己修改模式,看下方文字裡哪些部分被比對到。試試 a[^bcd]e 會比對到哪些字,再把文字改成你自己的例子。

多字元表示法:重複次數與錨點

在一個字元(或一組字元)後面加上量詞,指定它重複幾次。量詞只作用在它前面的那一個字元:

量詞 意思 例子
* 0 次以上 ab*c:ac、abc、abbc 符合
+ 1 次以上 ab+c:abc、abbc 符合;ac 不符合
? 0 次或 1 次 ab?c:ac、abc 符合;abbc 不符合
{m} 剛好 m 次 ab{2}c:只有 abbc 符合
{m,} 至少 m 次 ab{2,}c:abbc、abbbc 符合
{m,n} 至少 m 次、最多 n 次 ab{1,2}c:abc、abbc 符合

錨點:^ 表示字串開頭、$ 表示字串結尾。沒有錨點時,只要字串裡有一段符合就算通過: /\d{4}/.test('12345') 是 true(裡面有連續 4 個數字)。驗證欄位時幾乎都要寫成 /^…$/,要求整個字串符合。

量詞與錨點

同一組字串 ac、abc、abbc、abbbc,用五個模式各篩一次。每個模式都有 ^ 與 $,所以要整個字串符合。最後兩行比較有沒有錨點的差別(這是投影片自我評量的 ab{1,2}c{1} 與 abbcc)。

想一想:字元與量詞

1.

下面的程式會印出什麼?

console.log(/a[^123]b/.test('a1b'));
2.

下面的程式會印出什麼?

console.log(/a./.test('a'), /ab{2}/.test('abb'));
3.

下面的程式會印出什麼?

console.log(/ab{1,2}c{1}/.test('abbcc'));
4.

下面的程式會印出什麼?

console.log('2026-10-29'.replace(/-/, '/'));
第 1 次作答

群組、或與旗標

寫法 意思 例子
( ) 群組:把幾個字元當成一個單位,比對到的內容可以單獨取出 (ab)+:ab、abab
| 或 png|jpg:png 或 jpg
(?<名稱>…) 具名群組,結果放在 match(…).groups.名稱
$1、$2 在 replace 的新字串裡,代表第 1、2 個群組 '$3/$2/$1'

旗標寫在模式最後的斜線後面,可以同時用好幾個(/…/gi):

旗標 意思
g 全域:找出所有符合的部分,而不是找到第一個就停
i 不分大小寫
m 多行:^、$ 改成比對每一行的開頭與結尾

g 有一個陷阱:有 g 的 RegExp 物件會記住上一次找到的位置(lastIndex),同一個物件連續呼叫 test, 下一次會從上次的位置繼續找,結果可能不同。所以用來驗證的模式通常不加 g。

用群組擷取與重新排列

第 3 行的模式有三個群組,m[1]、m[2]、m[3] 依序是年、月、日。最後一段示範 g 旗標的 lastIndex 陷阱:同一個字串、同一個模式,第二次 test 卻是 false。

互動練習:量詞與常見格式

前六個按鈕是投影片的量詞範例,後面四個是常見的格式。下方文字可以自由修改,例如把手機號碼改成 11 碼,看看「手機」的模式還會不會比對到(想想看:為什麼還是會?要加上什麼才不會?)。

表單送出前的檢查

HTML 內建的檢查:不寫 JavaScript 也能做基本檢查,送出時瀏覽器會擋下不符合的欄位並顯示提示:

<input type="text" name="id" required pattern="[A-Z]\d{9}" title="1 個大寫英文字母加 9 個數字">
<input type="password" name="pw" required pattern="[a-zA-Z0-9]{8,}" title="至少 8 個英文字母或數字">
<input type="email" name="email" required>
  • required:必填,空白就不能送出
  • pattern:用正規表示法限定格式,適用於 text、tel、email、url、password、search 這幾種欄位; 不必寫 ^ 與 $,瀏覽器一律要求整個值都符合。用 title 說明格式,提示訊息會顯示它
  • pattern 以 Unicode 的 v 旗標編譯,字元類別裡的 -、(、)、[、{、}、/、| 要當成一般字元時,必須加反斜線

投影片的例子(只檢查格式):

欄位 模式 說明
身分證字號 ^[A-Z]\d{9}$ 1 個大寫英文字母加 9 個數字,例如 A123456789;不檢查檢查碼是否正確
密碼 [a-zA-Z0-9]{8,} 至少 8 個英文字母或數字
信用卡號 ^\d{4}-\d{4}-\d{4}-\d{4}$ 16 個數字,每 4 個用 - 隔開
台灣手機 ^09\d{8}$ 09 開頭,共 10 個數字

用 JavaScript 檢查:需要自訂錯誤訊息的位置與文字時,在 <form> 加上 novalidate 關掉瀏覽器的內建檢查, 改在 submit 處理器裡自己檢查,不通過就 e.preventDefault():

form.addEventListener('submit', (e) => {
  if (!/^09\d{8}$/.test(phone.value.trim())) {
    e.preventDefault();
    phoneMsg.textContent = '手機號碼格式:09 開頭,共 10 碼';
  }
});

電子郵件的完整規則非常複雜,表單通常只用簡化的模式(@ 前後有內容、網域有點)擋掉明顯打錯的輸入; 信箱是否真的存在,要靠寄驗證信確認。另外,前端檢查是為了使用者方便,別人可以跳過網頁直接送資料, 伺服器一定還要再檢查一次。

互動練習:送出前驗證表單

表單加了 novalidate,瀏覽器不會自己檢查,要由你寫的 submit 處理器檢查。PHONE_RE 與 EMAIL_RE 已經寫好了。依任務完成處理器,改完按「執行」,再到預覽裡填入錯的、對的內容各送出一次。

想一想:旗標與表單驗證

1.

下面的程式會印出什麼?

const re = /a/g;
console.log(re.test('a'), re.test('a'));
2.

下面的程式會印出什麼?

console.log(new RegExp('\d+').test('123'));
3.

要檢查「台灣手機號碼:09 開頭,共 10 個數字」,下列哪一個模式最適合?

4.

<input pattern="\d{4}"> 的欄位填入 12345,瀏覽器的內建檢查會怎麼判斷?

5.

網頁已經用 JavaScript 檢查了所有欄位,伺服器還需要再檢查嗎?

第 1 次作答

實作:接受兩種手機號碼寫法

使用者常把手機號碼寫成 0912-345-678。請修改 PHONE,讓它同時接受 0912345678 與 0912-345-678(連字號可有可無),其他寫法一律不通過。完成後五行的結果應該是 true、true、false、false、false。

你的輸出
預期輸出

重點精華回顧

  • 正規表示法用模式描述文字的樣子,用來驗證輸入、尋找與取代、擷取部分字串。
  • 建立:字面值 /模式/旗標,或 new RegExp('模式', '旗標')(字串裡的反斜線要寫兩次)。
  • 方法:test(有沒有符合)、search(位置)、match/exec(結果)、replace(取代)、split(切開)。
  • 單一字元:.、[abc]、[^abc]、[a-z]、\d、\w、\s(大寫是相反),\ 跳脫特殊符號。
  • 重複次數:*(0 次以上)、+(1 次以上)、?(0 或 1 次)、{m}、{m,}、{m,n};^、$ 限定開頭與結尾,驗證時要用。
  • ( ) 群組可以擷取內容、在 replace 用 $1 參照,| 表示或;旗標 g 全部、i 不分大小寫、m 多行。
  • 表單驗證:HTML 的 required、pattern(自動要求整個值符合)與 title;或加上 novalidate,在 submit 處理器用 test 檢查並 preventDefault。伺服器一定要再檢查一次。
這一課完成了!
Next lesson:網頁測試伺服器
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。