欣迪

出處: https://www.hackerrank.com/challenges/js10-regexp-1/problem?isFullScreen=true

簡單地寫一下題目大綱:

用 Regular Expressions 驗證一個字串,規則如下:

  • 字串是全英文不含數字, 空格或符號
  • 字串長度大於 3
  • 字串開頭必須是母音 a, e, i, o, u 其中之一
  • 字串的結尾最後一個字等於第一個字

解法

一直很苦手的的 Regular Expressions。之前看了不少技術文章,仍然不得要領。 但在做完了這幾題後,開竅不少。

解題前先了解 js RegExp 的 .test 方法: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test

接下來看標準表達式的基本寫法(很難從它的敘述直接看懂,要多看一些範例再和技術文章對照): https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Guide/Regular_Expressions

開始吧

以下是整個解題的思路和心得分享。

1. 從開頭開始下手

首先 [ ] 內,是允許的內容。 ^ 則是代表文字開頭。

const re = /^[aeiou]/

re.test('apple') // return true
re.test('pan') // return false

如果沒有 ^ 號,則語意會變成,字串內必須包含 a, e, i, o, u 任一字元。

const re = /[aeiou]/

re.test('apple') // return true
re.test('pan') // return true
re.test('xyz') // return false

2. 中間與結尾

中間和結束字串必須一起考慮。結束字串的符號是 $ ,放在驗證字串的最後。

依照本題的規則,第二個字元可以是 a – z 其中的一個,參考標準表達式的寫法寫成 [a-z] 。

+ 代表的是 1 至多個內容,所以我們寫成下面的樣子:

const re = /^[aeiou][a-z]+$/

re.test('apple') // return true
re.test('pan') // return false
re.test('a pan') // return false
re.test('apple pan') // return false

如果沒有 $ 號呢?

這邊代表 a, e, i, o, u 的後面一定是一個 [a-z]+ 的字串。但是之後的字串就不管了。

const re = /^[aeiou][a-z]+/

re.test('apple') // return true
re.test('pan') // return false
re.test('a pan') // return false
re.test('apple pan') // return true
re.test('apple/pan') // return true

// 如果加 $,則在 [a-z]+ 後面就必須結束
const re_2 = /^[aeiou][a-z]+$/

re.test('apple') // return true
re.test('pan') // return false
re.test('a pan') // return false
re.test('apple pan') // return false
re.test('apple/pan') // return false

如果沒有 + 號呢?

+ 號代表 1 ~ n 個,把 + 號拿掉,表示後面接 1 個符合 [a-z] 規則的字串。 下方的寫法就是指驗證開頭兩個字。

const re = /^[aeiou][a-z]/

re.test('apple') // return true
re.test('pan') // return false
re.test('a pan') // return false
re.test('apple pan') // return true

// 如果加 $,則在 [a-z] 後面就必須結束,不可能超過兩個字串
const re_2 = /^[aeiou][a-z]$/

re.test('abc') // return false
re.test('ab') // return true

衍伸

除了 + 號外,也可用 {1, }

{a} 的用法:

表示需要出現幾次。 [a-z]{2} 表示兩碼任意的小寫英文字母。


{ a, b } 的用法

a 代表只少需要出現幾次,b 代表最多出現幾次(可不填,代表無限多次)。

假設我們希望接下來的字元是 [a-z] 必須至少出現 2 次 ,可以寫成 {2,}

常見的用法縮寫:

+ 等同於 {1,}
* 等同於 {0,}
? 等同於 {0,1}

3. 變數的引用

官方的內文: 匹配 ‘x’ 並記住此次的匹配

使用 .test 方法時,可以在規則內依序引用  \1, \2, …, \n 代表的就是前面的 pattern。

前面的方法,我們已經可以驗證以下幾個條件:

  1. 字串是全英文不含數字, 空格或符號
  2. 字串開頭必須是母音 a, e, i, o, u 其中之一
const re = /^[aeiou][a-z]+$/

re.test('apple') // return true
re.test('pan') // return true
re.test('abba') // return true

我們用來匹配的文字用 (x) 記錄,在用 \1 匹配到結尾。 寫法如下:

const re = /(^[aeiou])[a-z]+\1$/

re.test('apple') // return false
re.test('pan') // return false
re.test('abba') // return true

以 “abba” 這個字串為例。 字首 “a” 成為了 (^[aeiou]) 內批配的結果,也是第一個結果 \1,我們把這個結果用在字尾的驗證: \1$

我們把整段 Regular Expressions 翻譯成中文吧。

(^[aeiou]) 開頭必須是 a, e, i, o, u 裡面的其中一個字,然後記錄起來(\1)。

[a-z]+ 接下來的字串由至少 1 個小寫英文字組成。

\1$ 結尾前的字串,要跟 \1 一樣。

衍伸

符合以下條件下:

  • 字串是全英文不含數字, 空格或符號
  • 字串長度大於 3
  • 字串開頭必須是母音 a, e, i, o, u 其中之一

結尾字必須等於開頭兩個字:

const re = /(^[aeiou][a-z])[a-z]*\1$/

re.test('abba') // return false
re.test('abab') // return true

結尾字是第一和第二字其中一個:

x|y : 符合「x」或「y」。

const re = /(^[aeiou])([a-z])[a-z]*(\2|\1)$/

re.test('abcd') // return false
re.test('abca') // return true
re.test('abcb') // return true

結尾字不可以是第一個字:

const re = /^([aeiou])[a-z]+(?!\1)[a-z]$/

re.test('abcd') // return true
re.test('abca') // return false
re.test('abcb') // return true

結尾字不可以是第一個字或第二個字:

x(?!y) : 符合’x’,且後接的不是’y’。’y’為否定’x’存在的意義,後面不行前功盡棄(negated lookahead)。

這邊寫的有點複雜,基本上就是把 y 當作排除的條件。例如 (?![ab])[a-z] 就當作 a-z 排除 a, b。

const re = /^([aeiou])([a-z])[a-z]*(?!\1|\2)[a-z]$/

re.test('abcd') // return true
re.test('abca') // return false
re.test('abcb') // return false

到這裡,一般的格式驗證應該已經難不倒了。

訂閱 IT-Monk

訂閱最新文章的發布消息! 😚😚😚
Loading

作者介紹 - 欣迪

欣迪

從設計到寫程式,發現自己有追求前端技巧的自虐傾向。不斷的踩坑,再從坑裡爬出來,慢慢對攀岩有點心得。 目前在多間公司擔任網站設計顧問。 同時也是網站架設公司負責人。