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 互為相反。
^ 放在中括號裡面的開頭表示相反;放在模式的開頭則是「字串開頭」(下一節)。
多字元表示法:重複次數與錨點
在一個字元(或一組字元)後面加上量詞,指定它重複幾次。量詞只作用在它前面的那一個字元:
| 量詞 | 意思 | 例子 |
|---|---|---|
* |
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)。
想一想:字元與量詞
下面的程式會印出什麼?
console.log(/a[^123]b/.test('a1b'));
下面的程式會印出什麼?
console.log(/a./.test('a'), /ab{2}/.test('abb'));
下面的程式會印出什麼?
console.log(/ab{1,2}c{1}/.test('abbcc'));
下面的程式會印出什麼?
console.log('2026-10-29'.replace(/-/, '/'));
群組、或與旗標
| 寫法 | 意思 | 例子 |
|---|---|---|
( ) |
群組:把幾個字元當成一個單位,比對到的內容可以單獨取出 | (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。
表單送出前的檢查
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 碼';
}
});
電子郵件的完整規則非常複雜,表單通常只用簡化的模式(@ 前後有內容、網域有點)擋掉明顯打錯的輸入; 信箱是否真的存在,要靠寄驗證信確認。另外,前端檢查是為了使用者方便,別人可以跳過網頁直接送資料, 伺服器一定還要再檢查一次。
想一想:旗標與表單驗證
下面的程式會印出什麼?
const re = /a/g;
console.log(re.test('a'), re.test('a'));
下面的程式會印出什麼?
console.log(new RegExp('\d+').test('123'));
要檢查「台灣手機號碼:09 開頭,共 10 個數字」,下列哪一個模式最適合?
<input pattern="\d{4}"> 的欄位填入 12345,瀏覽器的內建檢查會怎麼判斷?
網頁已經用 JavaScript 檢查了所有欄位,伺服器還需要再檢查嗎?
實作:接受兩種手機號碼寫法
使用者常把手機號碼寫成 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。伺服器一定要再檢查一次。