深不可测的海 - Regular Expression

使用终端机搜寻特定字串时,大家一定用过 grep 这个指令吧~
但你有想过 grep 为什麽叫 grep 吗?
没有的话很正常,因为我也从没想过,直到在查找正规表达式时意外发现了他的身世/images/emoticon/emoticon37.gif

根据无所不知的维基百科:

grep名称来自於g/re/p(globally search a regular expression and print,以正规表示式进行全域寻找以及列印)

看来电脑的世界里regex真是无所不在呢...../images/emoticon/emoticon06.gif
如果你跟我一样是regex旱鸭子的话,别怕!这篇文章不会让你直接进泳池的,我们先在泳池边踢水就好,暖身好了的话,就开始吧~

基本概念

在JS中,正规表达式是一个物件,有两种方式可以建立它。

  1. 用建构器创建 let pattern = new RegExp('BBQ');
  2. 用字面值语法建立 (比较常使用这种)let pattern = /BBQ/;

那要怎麽样确认一串文字中有我们要的资讯呢?
可以使用RegExp.test(String) ,如果文字中有符合表达式定义好的规则就回传true,没有符合就回传false,先来看个简单的范例:
注:推荐使用regex101

let pattern = /BBQ/;

/BBQ/.test('I want to eat bbQ.'); // false
/BBQ/.test('BBQ BBQ BBQ.'); //true,但只会比对到第一个符合的

看样子,正规表达式在意大小写呢!要改变比对模式的话,可以使用flag
这些flag可以单独也可以合并使用。

用来调整搜寻方式的旗标flags

  • g: 只要符合的全部找出来,如果没有设定,找到第一个符合的就停止比对
  • i: 忽略大小写
  • m: 多行文字比对
/BBQ/i.test('I want to eat bbQ. hahaha.'); // true
/BBQ/ig.test('BBQ bbQ BBQ.'); // true,全部都会比对到

用来指定位置的锚点 anchor

上面的例子中,无论BBQ出现在哪个位置都无所谓,但如果要限制文字出现的位置呢?

  • ^: 匹配字串的开头
  • $: 匹配字串的结尾

/^BBQ/.test('I want to eat BBQ.'); // false
/^BBQ/.test('BBQ is yummy.'); //true

/BBQ$/.test('BBQ yo yo yo.'); // false 
/BBQ$/.test('We all love BBQ.'); // true 

从上面的例子可以知道,就算字串中有符合的文字,如果不在正规表达式指定的位置出现,就不会被比对。

用来提供范围的中括号 []

如果不需要完全符合特定文字,只需要字串符合特定范围呢?
比如说在中秋节发文的时候只要文字中有B和Q,就表示发文者可能很想吃烤肉(?)

/[BQ]/i.test('barbeque is good~'); // true

/BBQ/i.test('barbeque is good~'); // false,没有用中括号,代表要完全匹配BBQ才回传true

以下用常使用的范围做范例:

//输入必须要有数字
/[0-9]/.test('three two one'); // false
/[0-9]/.test('COVID-19'); // true

//输入必须含有a-z的字母
/[a-z]/.test('123'); // false
/[a-z]/.test('COVID-19'); //false
/[a-z]/.test('abc'); //true 

在前面的指定匹配位置时提到了一个锚点符号^
这个符号如果出现在中括号时,就成了否定字元,概念很像JS中的逻辑运算子NOT!

大概是因为某些范围在实务上经常使用,所以正规表达式中提供了字元类别来代表这些常用的字串范围

  • \d: 任何的 ASCII 数字,相当於[0-9]
  • \D: 任何的 ASCII 数字以外的字元,也可以表示成[^0-9]
  • \w: 任何字词字元,等同於 [A-Za-z0-9]
  • \W: 非字词字元,也可表示成 [^A-Za-z0-9]
  • \s: 任何空白字元
  • 还有超级多,愿意憋气下水的朋友可以看看w3Schools的Metacharacters

指定重复次数 {} + *

当我们需要描述特定规则出现的次数,就得拜托重复字元帮忙了~

  • 出现至少n次,但不多於m次 {n, m}
let pattern = /\d{2,5}/;  -> 代表2到5位数

/\d{2,5}/.test('4'); // flase
/\d{2,5}/.test('1234'); // true
/\d{2,5}/.test('123456'); // 虽然回传值是true,但只有比对12345,如果要限定只有2到5位数才回传true,可以使用锚点(如下)

/^\d{2,5}$/.test('123456'); // false
/^\d{2,5}$/.test('423'); // true
  • 刚好出现n次 {n}
/^\w{3}$/.test('BBQ');  // true
/^\w{3}$/.test('BBQ '); // false, 因为结尾是空白字元
  • 出现至少一次,可多於一次 +
/^\d+$/.test(''); // flase
/^\d+$/.test('1'); // true
/^\d+$/.test('42'); // true
/^\d+$/.test('42school'); // false
  • 可有可无的 (出现次数 >= 0 ) *
/^\d*$/.test(''); // true
/^\d*$/.test('1'); // true

选项及群组字元

脚开始抽筋的朋友再撑一下,认识完最後的两个字元就可以吃烤肉啦~

  • 选项字元 |
    • 选项的比对顺序是由左而右,直到找到一个符合的为止
    • 如果左边的选项有符合表达式的规则,则右边的选项会被忽略
/BBQ|mooncake/.test('pomelo'); //flase
/BBQ|mooncake/.test('BBQ'); // true
/BBQ|mooncake/.test('mooncake'); //true
  • 群组字元 (...)

根据JS大全,括弧有数个用途,其中之一是把不同的项目归组成一个子表达式,可以想成是一个个单元。我们实际看图比较清楚。

首先我们先定义规则为开头是四个数字,後面需紧跟着字词字元,测试的字串分别为5252BBQ和777lucky。
可以看到右侧的比对资讯栏中除了配对的字串外,还包含群组资讯,如果我们把规则中的括弧拿掉,就只出现比对的完整资料而已。


回到昨天还看不懂的魔法咒语 /^(a |an |the )/i
是不是好像没那麽困难了呢,如果还是看不懂的话没关系,先吃一波烤肉再说/images/emoticon/emoticon42.gif

参考资料

正规表示式 - 维基百科
grep - 维基百科
A quick and simple guide to JavaScript Regular Expressions
JavaScript大全 第七版


<<:  Day 16:自己动手,丰衣足食.IOS的Coroutine管理

>>:  [D06] OpenCV 介绍与用法

Day 13 AWS云端实作起手式第三弹 开始拼拼图吧

今天接着来看看如何搞定架设的设定档吧! 步骤 7 建立网站 在先前开启EC2时,我们透过user d...

android studio 30天学习笔记-day 8-基本介绍rxjava2

RxJava2是一套处理非同步(asynchronous)事件的library,这个library是...

LeetCode - 8 String to Integer (atoi)

本篇同步发布於Blog:[解题] LeetCode - 8 String to Integer (a...

Day 11 运算宝石:EC2 储存资源 EBS Types 方案比较

今天我们要来介绍 EBS Type方案比较,那我们开始吧! 在之前的文章中我们有提过,EBS 相对...

Day29 自动合成物品的小乌龟与指令

上次已经玩过挖矿龟 Mining Turtle,此外还有其他几个类似的小乌龟 包括 Digging ...