正则表达式包会教程

当想匹配比如手机号、邮箱等特殊形式的字符串时,大家往往都会想到正则表达式,不过当脑海中浮现出那一串神奇的既没有注释,甚至没有空格的字符串时,内心又会充满恐惧。此时你可能会动用搜索引擎,搜到别人写的那一串神奇字符,拷贝到自己的代码中,稍经测试,发现竟然能用,不禁发出一阵牛逼,然后就去写别的代码逻辑去了。再也不想看这一串字符。

是不是你也有过上述经历呢,对正则表达式怀有很大的畏惧之情,觉得自己无法驾驭它,但有时候又实在离不开它。下面我会简单介绍下正则表达式的由来,和驾驭它的正确姿势。本篇会议 JS 端的正则表达式为例子来演示。

由来

正则表达式诞生于 1951 年,它起源于对形式语言的数学研究。1968 年开始,正则表达式广泛应用于编辑器的字符匹配,和编译器中的词法分析。Ken Tompson 实现了第一个切实可用的匹配器,后来被用在了 Unix 的 grep 搜索工具中,g/re/p 全称 Global search for Regular Expression and Print matching lines。真正诞生更现代化的正则表达式实现是在 19 世纪 80 年代的 Perl 语言中。现在正则表达式已被各种语言广泛支持,比如 Java、Python、JavaScript 等。

由于正则表达式趋向于极致的简洁,甚至不惜容忍含义模糊,而且它不支持注释和空白,所有部分都紧密排列在一起,导致我们难以理解也属正常,但尽管有这些缺点,正则表达式依然被广泛的应用着。

举个例子

我们要提取一个 http 链接的 scheme 和 domain,那正则表达式如何写呢?我们可以简化成如下

let parse_url = /^(https?):\/{0,3}([0-9.\-A-Za-z]+)(?::\d+)?.*$/
parse_url.exec('http://www.kujiale.com:80/college')
会得到一个数组,["http://www.kujiale.com:80/college", "http", "www.kujiale.com"]
数组的首位是整个输入,第二位和第三位则为我们想要的内容。

下面我们一步步来分析该正则表达式

^ $ 表示匹配开始和结尾,它是一个锚,只有开头和结尾处满足该正则的字符串才匹配,而如果没有该锚,意味着,如果字符串中间部分满足该正则也会被匹配到。

比如当我们不用 ^ 符号时,同样执行上述代码,如果此时来匹配 xhttp://www.kujiale.com:80/college 则一样会成功匹配,而使用了 ^ 则会匹配失败。

注:^ 既可以表示开始的锚,也可以用作语意。后面会详述。

分组

上面例子中得到的结果为数组,包含了三部分内容,而无其他。在正则中,如果我们想获取其中的部分内容,我们就会使用一对括号来保住所要提取的部分,我们把这部分称为一个分组 (...), 一个分组会复制它所匹配的文本,并将其放到 result 数组里,每个分组会被指定一个编号,1,2,3... 。如果分组有嵌套,则会从按从左到右,从外到里的方式输出。如:

/((ht)(tp))(s)/.exec('https')
输出:["https", "http", "ht", "tp", "s"]

当我们不想捕获某个分组时,我们需要在分组中加上 ?: 表示不要捕获该分组,就像最初的例子中不要捕获端口号那样使用。

/((ht)(?:tp))(s)/.exec('https')
输出:["https", "http", "ht", "s"]

分组被捕获后,我们可以使用 \ + 编号来使用该分组。\1 表示分组一,\2 表示分组二,以此类推。比如我们要找一个文本中搜索一对重复的单词

const reg = /([A-Za-z]+)\s+\1/
reg.test('aaa bbb')  false
reg.test('aaa aaa')  true

字符集

正则匹配最终匹配的还是字符,所以我们需要表达字符的方式。如果只是匹配一个字符,那很简单,比如

/a/.test('a')  true

我们也可以加上 逻辑 |

/a|b/.test('b') true
/a|b/.test('a') true

如果是一类字符,如果一直用 逻辑那也太繁琐了,此时我们可以使用 [] ,比如匹配所有 a 到 z 的字符

/[a-z]/.test('b') true
/[ab]/.test('b') true

如果想加上 的语义,就像我们上面所说,那我们就需要在字符集中加上 ^ 符号

/[^ab]/.test('b') false

由于某些字符集经常用到,所以正则帮我们提取了出了这些常用的字符集,用一些特殊符号来表示

. 匹配除 \n \r 之外的任何单个字符

\d 等价于 [0-9]

\D 等价于 [^0-9]

\w 等价于[0-9A-Z_a-z]

\W 等价于[^0-9A-Z_a-z]

\s 匹配任何不可见字符, 比如空格换行等

\S 匹配任何可见字符

注:当字符集中使用到一些保留字符时,需要使用 \ 进行转意

匹配次数

我们除了匹配字符,还得需要定义匹配字符的数量,比如我们要匹配两个字母的单词,不是写成 /\w\w/ 而是可以写成 /\w{2}/

字符或字符集后面跟 {n, m} 表示该字符或字符集可重复的次数范围,一样,正则为我们封装了一些常用的次数匹配规则,比如 +, *, ? ,如下表所示。

{n,} 大于等于 n 次

{n} 等于 n 次

+ 等价于 {1,}

* 等价于 {0,}

? 等价于 {0,1}

标识

除了上述说的这些,你还可能看到过有些正则表达式后面加了 /g/i/m。 比较常用的是 /g/i 表示忽略字符大小写,/m 表示多行匹配,都不怎么常用。

其中 /g 的使用也有限制,比如 testsearch、和 RegExp 的 exec 方法会忽略掉 /g 标识。/g 可以用在 string.match 和 string.replace 方法中, 如下例子:

"[22].[44].[33].".match(/\d+/g)  // 输出 ["22", "44", "33"]
"[22].[44].[33].".match(/\d+/)   // 输出 ["22"]
 
"aaaa".replace(/a/, 'b')   // 输出 "baaa"
"aaaa".replace(/a/g, 'b')  // 输出 "bbbb"

引用捕获分组

你可以直接使用 \1 \2 这种形式来引用前面捕获到的分组,比如你想搜索重复的单词,可以使用下面方式。

/([A-Za-z]+)\s\1/gi

最短匹配

使用 ? 的方式。
a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab(第一到第三个字符)和ab(第四到第五个字符)。

写在最后

我们再回过头来看下这个正则,是不是也觉得不难了。

/^(https?):\/{0,3}([0-9.\-A-Za-z]+)(?::\d+)?.*$/

(https?) 表示捕获该部分,s 可有可无。/{0, 3},表示匹配 /,0 到 3 个都满足。后面就是匹配 域名、端口和后面的 path、query 部分。另外以上例子部分使用 javascript 语言,java 的正则表达式和 js 本质没有区别,但要特别注意 Java 中的转译需要用两个斜杆 \\

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容