最近更新: 2007-01-25

Regular Expression (RegExp) in JavaScript

Regular Expression (以下簡稱 REGEX) 是以一組特定字元符號描述字串樣式規則的記述語法。簡單地說, REGEX 用於表達字元符號在字串中出現的規則。舉個例子說明,在 REGEX 中,字元 '^' 放在第一個位置表示字串開頭位置,當我寫下 ^A 的記述時,便表示必須是一個開頭為 A 的字串,如 Adam,才符合此一規則。這個表達規則通常稱為 pattern 。 ECMAScript/JavaScript 以內建的 RegExp Object 提供 REGEX 功能。See also: ECMA-262 15.10 - RegExp Objects

要產生一個 RegExp 個體有兩種方式。第一種是直接以斜線 (/) 包住 pattern ,例如 /^A/ 。注意不要再用單引號或雙引號包在斜線外圍,一但用了引號圍住,就只是一個 String 而非 RegExp 個體。 "/^A/" 就是錯誤寫法,這只是一個普通字串。第二種是向系統要求建立一個 RegExp 個體,即 new RegExp(pattern) ,引數 pattern 可以是一個字串也可以是另一個 RegExp 個體。第一種方法只能使用常值的 pattern ,我們不能用斜線包住一個變數或一個字串運算結果。如果 pattern 保存在變數之中,則必須使用第二種方式。

RegExp 個體提供兩個主要行為,即 exec() 和 test() 。 exec() 會對引數字串進行比對運算,並將匹配的字串內容記錄在陣列中回傳。而 test() 則僅僅測試引數字串是否符合規則,只回傳 truefalse 。此外,RegExp 個體也可以作為 String 個體的 match(), search(), replace(), split() 之引數。 String 的 match() 其實就是調用 RegExp 的 exec() ,兩者的結果相同。

ECMAScript/JavaScript 的 REGEX 規則是以 Perl 的規則為範本 (PHP 中稱為 PCRE 的內容) ,本文僅簡短說明幾個常見的字元意義。想了解更多用法者,請參看 Perl/PHP 的相關書籍,以及 ECMA-262 15.10 的內容。

  1. ^
    寫在 pattern 第一個位置時,表示其後一符號必須出現在字串開頭的位置。寫在 pattern 中間位置時則為否定之意,表示字串中不可有 ^ 之後一符號的內容。
  2. $
    寫在 pattern 最後一個位置時,表示其前一符號必須出現在字串尾端的位置。寫在 pattern 中時無特別意義。
  3. *
    表示字串中有 0 到無數個其前一符號的內容。
  4. +
    表示字串中有 1 到無數個其前一符號的內容。
  5. ?
    表示字串中有 0 到 1個其前一符號的內容。
  6. { }
    表示前一符號在字串中的重覆次數。例如 /A{2}/ 表示 'A' 重覆兩次 (即 'AA') ;/A{2,}/ 表示字串含有 2 到無數多個 'A' ;/A{2,5}/ 表示含有 2 到 5 個 'A' 。
  7. .
    表示一個任意字元。
  8. [ ]
    表示字串含有括號中任一字元的內容。可以 - 表示一組連續字元,例如 /[a-z]/, /[0-9]/ 。注意, [] 僅代表一個字元,例如 /[abc]/ 表示 'a' 或 'b' 或 'c' ,而不是 'abc' 。
  9. ( )
    表示一個 sub pattern ,符合 sub pattern 的字串內容會被存放在匹配陣列中,並依序指派數字代表此 sub pattern 。可以此數字在 pattern 的其他地方引用內容,例如 /The h([0-9]) means Title (\1)/ 表示第 1 個 sub pattern 是 0 到 9 的任一字元,而 \1 表示匹配的內容。故 'The h1 means Title 1', 'The h2 means Title 2' 到 'The h9 means Title 9' 符合規則。
  10. \
    表示轉義 (escaping) ,將其後的字元視為一般字元。例如要表示字串中含有 '/' 字元時,就必須寫作 /\//
  11. |
    「或」意,字串中含有 '|' 之前一符號或後一符號的內容。例如 /image\.(jpg|png)/ 表示 'image.jpg' 或 'image.png' 。通常會用 () 括住 '|' 的前後符號。
  12. \d
    表示任何一個數字,意同 [0-9] 。
  13. \D
    表示任何一個非數字,意同 [^0-9] 。
  14. \w
    表示任何一個字元與數字以及 '_' ,意同 [a-zA-Z0-9_] 。
  15. \W
    表示任何一個 \w 以外的字元。
  16. \s
    表示任何一個空白符號,包括 \t, \v 等。
  17. \S
    表示任何一個非空白符號。

Example 1.

/\d{4}-\d{2}-\d{2}/.test('2007-01-25');     // true
'2007-01-25'.match(/\d{4}-\d{2}-\d{2}/);    // true

var datePart = '2007-01-25'.match(/(\d{4})-(\d{2})-(\d{2})/);
// datePart is ['2007-01-25', '2007', '01', '25']

var emailPart = /^(\w+)@([\w.]+)/.exec('rock@example.com');
// emailPart = ['rock@example.com', 'rock', 'example.com']

var words = 'How are you doing, john?'.split(/[\s,\?\.]+/);
// words = ['How', 'are', 'you', 'doing', 'john', '']

Example 2. 一個簡單的 REGEX 測試工具,可以立即回應輸入的字串與 pattern 是否相符

REGEX test form

"
此輸入框中輸入字串

/
此輸入框中輸入 REGEX 內容。前後不用加 (/)。

|

 

 

 

 

<script type="text/javascript">
function r_test(regex, s) {
    var p = document.getElementById('r_test');
    var result = document.createTextNode(
        regex.test(s)
            ? 'Match!'
            : 'Not match!'
    );
    p.replaceChild(result, p.lastChild);
}

function r_exec(regex, s) {
    var p = document.getElementById('r_exec');
    var matches = regex.exec(s);
    var result = document.createTextNode(
        matches
            ? matches.toString()
            : 'Not match!'
    );
    p.replaceChild(result, p.lastChild);
}

function s_split(regex, s) {
    var p = document.getElementById('s_split');
    var splits = s.split(regex);
    var result = document.createTextNode(
        splits
            ? splits.toString()
            : 'Not match!'
    );
    p.replaceChild(result, p.lastChild);
}

function regexp_match() {
    var p = document.getElementById('regex');

    var inputString = document.getElementById('inputString').value;
    var patternString = document.getElementById('patternString').value;
    var regexFlags = document.getElementsByName('regexFlags');
    var flags = '';
    for (i = regexFlags.length - 1; i >= 0; --i) {
        if (regexFlags[i].checked) {
            flags += regexFlags[i].value;
        }
    }

    var regex = new RegExp(patternString, flags);
    p.replaceChild(document.createTextNode(regex.toString()), p.lastChild);

    r_test(regex, inputString);
    r_exec(regex, inputString);
    s_split(regex, inputString);
}
</script>
相關文章
樂多舊網址: http://blog.roodo.com/rocksaying/archives/2670695.html

樂多舊回應
未留名 (#comment-16434431)
Fri, 16 May 2008 13:49:08 +0800
感謝你的整理,對我幫助很大。
未留名 (#comment-16902965)
Mon, 21 Jul 2008 15:06:28 +0800
整理得好仔細 感恩
未留名 (#comment-18225753)
Thu, 25 Dec 2008 15:57:56 +0800
哇!Example 2的REGEX 測試工具是大大您自己寫的嗎?好厲害唷!!
謝謝您的分享~很受益呢!
^_^
未留名 (#comment-19931647)
Sat, 10 Oct 2009 16:58:36 +0800
Good resource for me to understand.

Thank you.
gmmark12@gmail.com(Mark) (#comment-21247183)
Mon, 20 Sep 2010 11:13:02 +0800
感謝整理!!
獲益良多~
未留名 (#comment-21467179)
Tue, 21 Dec 2010 16:16:50 +0800
石頭大你好:
今天拜讀你的文章,收穫良多,不過不知道你有沒有遇到下列的問題:
我將你的REGEX test form的原始碼整個copy至我的本機執行後,卻發現在Split執行的結果和在你網站上執行的結果不同。
例如:
split_string = "My~Silly.Test-String"
pattern = /(~|\.|-)/
在你的網頁中return ['My', '~', 'Silly', '.', 'Test', '-', 'String']
但是在我的本機中卻return
['My', 'Silly', 'Test', 'String']

我Google了一下,發現其他人在IE中也有這樣"Split broken"的問題,那個人利用regex.match的方式去另寫一個新method.
我看石頭大的code也十分標準~真的令我十分困惑 ==
PS:解法網頁出處 : http://blog.stchur.com/2007/03/28/split-broken-in-ie/
未留名 (#comment-21495463)
Mon, 03 Jan 2011 12:15:58 +0800
按照我對 ECMAScript 第5版規範(最新版)的解讀, IE 的結果是錯的。

--
If separator is undefined, then the result array contains just one String, which is the this value (converted to a String).

ECMA-262 5th edition, p.147
--

我又實際測了 Chrome, Opera 它們的結果,也跟 Firefox 一樣。符合的分割樣式內容,要留在回傳的陣列中。
jailin1124@gmail.com(allneplay) (#comment-22267519)
Fri, 06 Jan 2012 16:25:10 +0800
請問大大,ignoreCase和global是什麼意思?
未留名 (#comment-22267623)
Fri, 06 Jan 2012 17:37:36 +0800
如同英文字典上的意思。

ignore case 勿略大小寫。

global 找全部。因為 REGEX 預設碰到第一個符合的內容就結束比對。
未留名 (#comment-22747232)
Sun, 13 Jan 2013 14:28:47 +0800
請問大大,我想比對一個字串可以接受
1.中文字
2.英文(中間可含空白,如fisrt name 跟 last name的組合)
3.不可接受全空白
請問這該怎麼寫
看完你的教學,能力不足的我只會寫2+3的...
未留名 (#comment-22747950)
Mon, 14 Jan 2013 17:25:39 +0800
很難。因為你要排除掉所有的特殊符號,也就是類似 [^\s~`!@#$%^&*_-] 這種規則。下面這行可以滿足你的部份需求:

^[^\s~`!@#$%^&*_-]+\s?[^\s~`!@#$%^&*_-]+$

為什麼說是部份?因為還有很多標點符號沒有列進排除名單中。
未留名 (#comment-23005356)
Wed, 07 Aug 2013 18:16:03 +0800
請問大大~為什麼^([0-9]{4})[./]{1}([0-9]{1,2})[./]{1}([0-9]{1,2})$
去match日期格式假設2008/5/7,會存出陣列2008,5,7,我還是搞不太懂
未留名 (#comment-23005358)
Wed, 07 Aug 2013 18:16:04 +0800
請問大大~為什麼^([0-9]{4})[./]{1}([0-9]{1,2})[./]{1}([0-9]{1,2})$
去match日期格式假設2008/5/7,會存出陣列2008,5,7,我還是搞不太懂
未留名 (#comment-24466790)
Sat, 10 May 2014 14:53:57 +0800
因為你沒搞懂()的意思
未留名 (#comment-24788300)
Thu, 26 Jun 2014 11:21:09 +0800
石頭大您好:
拜讀您的文章後,有個問題想問問,就是regex是否適用於全形字呢,如12...90AB...Z?如果可以,那應該如何寫出regex呢?
感謝您
未留名 (#comment-24789809)
Thu, 26 Jun 2014 16:26:02 +0800
這要看你使用的 regex library 是否支援 unicode 或 utf-8 。
例如瀏覽器中的 javascript 支援 unicode ,所以可以用全形。例如 /[12]/ ,這可以判斷字串中是否包含一個全形的 1 或 2 。

至於 PHP 5 用的 regex library 還不支援 unicode ,所以不能用全形。事實上,你用了也不會得到正確比對結果。

例如 樣式 /[1]/ 去比 字串 "ab2c" (樣式是全形1,字串有全形2),在 JavaScript 會正確告訴你不合,但 PHP 會說1個符合。

因為在 PHP 眼中,全形 1 被視為兩個字元(byte),故 /[1]/ 在 PHP 眼中是兩個字元其中之一的樣式。
而全形2的第一個byte和全形1一樣,故 PHP 判定第1個 byte 符合樣式。