javabloger 2019-06-21
TRY REGEX 是一个交互式的正则表达式学习项目
项目地址:https://github.com/callumacra...
在线地址:http://tryregex.com/
翻译版在线:http://www.rakuhi.com/tryrege...
很适合正则表达式初学者学习、练习
总共28个小课程,从零基础入门到能完成大多数正则表达式书写。
个人做了 翻译&解答,有问题之处欢迎指正
本交互课程意在介绍正则表达式,更明确的说是JavaScript下的正则表达式。课程当然也会教您用其他语言写正则表达式,但是您应该知道它们是有区别的。
左侧的控制台就是一个JavaScript控制台。请使用 setName('Your name')
(Your name替换为您的名字)命令设置您的姓名来开始课程。
还有一些有用的命令:执行 help()
来查看它们
正则表达式(也写作 regex 或 regexp)是一个用来描述搜索模式的字符串——类似星号(*)文件名匹配通配符,但更加有效(当然也更加复杂)。
我们将会从一个非常基础的例子开始,来让您掌握 JavaScript 中语法和正则表达式的使用。bio
是一个不知道是否包含您名字的变量。为了看看结果,请输入 bio.match(/{{ firstEscaped }}/);
。
从上个例子中您可以得知一些事情。第一,用于定义正则表达式的语法:使用两个斜杠包含表达式:/表达式/
如果您将它输入控制台,将会返回正则表达式。
第二,您可以对字符串使用 .match()
方法来测试正则表达式。还有一些其他方法可以调用:您可以直接对正则表达式使用 .exec()
方法来对执行一个字符串。请输入 /{{ firstEscaped }}/.exec(bio)
。
.exec()
方法和 .match()
方法做的是同样的事情,但它是对表达式调用而不是对字符串——这会很有用。
另一个可用的是 .test()
方法 ——可能是这些方法中最简单的 。类似于 .exec()
,但返回的是布尔值。试一试吧!
提示:左侧控制台可以使用向上键来获取之前输入的表达式。
我们会用到的最后一个方法是 .replace()
,用一个字符串替换另一个字符串的方法。输入一下内容来将 bio
变量中您的名字隐藏:bio.replace(/{{ firstEscaped }}/, '[redacted]')
我们迄今为止用过的表达式都不是特别有趣,也没有包含特殊字符。以下这些字符在正则表达式中应该被转义:
$()*+.?[^|]
为了规避它们,使用反斜杠转义,例如 /what?/
。
写一个正则表达式来测试 num
变量中是否包含字符串“3.5”。
是不包含的!num
的值是 123456,它不包含字符串“3.5”。
在正则表达式中点运算符有特殊的含义:它将匹配除了换行符以外的所有的单个字符(所以 /a.c/
将会匹配 "abc", "a c", "a$c" 等等)。未转义点运算符直接使用 /3.5/
将可能匹配到 num
中字符串,这里点运算符将会匹配 4。
试试吧。
有一些“量词”可以用来表达某个东西需要被匹配多少次。第一个量词是问号,作用是让前置项(一个字符或字符组)可选(即出现0次或1次)。
表达式 /regexp?/
将会匹配 "regex" 和 "regexp",问号让 p(只是p)可选。
写一个表达式同时能匹配"frontend" 和 "front-end",答案通过 answer()
函数传参(例子:answer(/表达式/)
)。
我们要学习的下一个量词是加号。它的意思是“一个或更多前置项”;/Princes+/
会匹配 "Princes", "Princess", "Princesssss" 等等。但不会匹配"Prince"。
您要写的下一个表达式将会有点复杂。写一个正则表达式,抽取出 shortStory
变量(可输入 shortStory
查看变量内容)中左右括号间的所有内容(包含括号)。提示:需要用到刚才说过的点运算符。
星号和加号类似;但是不同于“一个或更多”,星号的含义是“零个或更多的前置项”。/Princes*/
在 /Princes+/
匹配内容的基础上还能匹配 "Prince" 。
重复上一个例子,用星号替换加号。抽取出 shortStory
变量中左右括号间的所有内容,即便括号内没有内容。
有一个量词可以用来限制重复次数。语法是 {min,max}
其中 min 是最小出现次数,max 是最大出现次数。比如 /a{3,5}/
会且只会匹配 "aaa", "aaaa" 和 "aaaaa"。
写一个表达式匹配 bracketNumbers
中左右圆括号中间长度为5~8之间的字符。
为了进一步明确重复次数范围,您可以用{n}
指明确定的一个重复次数,其中 n 是重复次数。比如表达式 a{6}
只会匹配重复的六个 a。
在使用大括号时,您可以不指定最大重复个数,只留下最小重复个数。比如,/a{5,}/
会匹配5个或更多次重复的字母 a。
用 answer()
函数给出不使用 ?*+
字符,但和 /a?b+c*/
等价的正则表达式。
标志用来改变正则表达式的表达形式,位于表达式的后面(例子:/表达式/ig
)。每个标志用一个字母表示,JavaScript 支持四种标志——有两种将会在本课程中学到。i
标志让表达式不区分大小写——在没有这个标志时 /a/
只会匹配“a”,不会匹配“A”;/a/i
则会同时匹配“a”和“A”。
执行 /CAT/i.exec('Category')
来看看 i 标志的作用。
第二个常用的标志是全局匹配标志,用字母 g
表示。 /a/
只会匹配字符串中第一个 a, /a/g
则会匹配每一个单个的字母 a。
写一个正则表达式,来用字母 “e” 替换 shortStory
里所有的字母 “a”。
您可以使用字符串的 .replace(expr, replace)
方法来做替换。
字符组允许您指定一组或一个范围的字符去匹配。/[aeiou]/
匹配任意元音字母,/[a-m]/
匹配任意字母表前半部分的字母,/[aeiou0-9]/
匹配任意元音字母或数字。
注意在字符组中,您不需要转义点字符,它将被直接匹配。如果您要使用连字符(-),则需要转义。
我们定义合法的用户名包含5到12个字母(大写字母或小写字母)或连字符。写一段代码,在 username
合法时返回 true。
否定字符组将会匹配不在字符组内的字符。您可以在字符组前添加一个脱字号(^)来否定它。比如 /[^a-m]/
将匹配“z” 、“$”,但是不会匹配“c”。
弄清楚 “非 [a-m]” 和 “不在 [a-m] 范围” 的区别很重要,/c[^a]t/
将匹配 “cut”,但不会匹配 “cat” 和 “ct” ——这个非常重要。
现在合法的用户名可以包含任何非空格字符(但同样长度在 5 到 12 之间)。写出一个正则表达式来检验 username
合法性。
类型字符可以用作常见字符组的简略表达。有6种类型字符:d
匹配十进制数字(0-9),s
匹配空格, w
匹配单词字符(单词字 —— Unicode字符集、数字、下划线)。
另外三个可用的类型字符是将上面三个改成大写字母,就起到了否定它们的效果;比如 s
匹配所有非空格字符。
写一个正则表达式,能匹配一个后缀一个空格一串数字的单词。用 charTypeTest
测试:不要使用任何文字字符。
如果您想确保字符串以某种确定字(字符集)的开始或结束——比如,您希望包装字符串以大写字母开始——那么你就可以使用锚点。美元符号($)匹配字符串结尾,脱字符(^)匹配字符串开始。/^cat$/
将只会匹配到 “cat” 而不匹配其他任何字符串(/cat/
会匹配任何包含 “cat” 的字符串)。
写一个正则表达式来测试 possibleUrl
变量是否以 “http://” 或 “https://” 开始,且从头到尾不包含任何空格。
提示:需要用到问号、否定字符集。您可能会需要两种锚点。
您可以使用圆括号来创建组,可以将多项组合起来或者保存结果以供之后引用:/"(.+)"/
上面就是一个捕获组的示例,意思是圆括号内匹配到的字符串将被保存在 .match()
或 .exec()
返回的数组中。
回头看看之前我们用表达式 /(.{5,8})/.exec(shorterStory)
抓取两个圆括号之间的数据的例子。尝试用圆括号包住 .{5,8}
重新运行一下。
您可以看到现在返回数组有个两项:第一项是全部匹配内容,第二项只包含捕获组匹配数据。
还有一种组类型叫做非捕获组。这种组语法上稍有不同,它不在数组内存储直。如果不需要引用组,您可能倾向使用非捕获组:它会保持返回数组的纯净。上一个例子中,添加 ?:
在组的开始点运算符之前,将表达式变成非捕获组。
和我们zhi'q没有用组几乎一样。
非捕获组的主要用途是给一个组赋予量词。下面的表达式将会匹配 “I ate” 和 Carrot and I ate” 不会匹配其他内容:/^(?:{{ firstEscaped }} and )?I ate$/
写一个表达式,能够匹配 “ha” 出现两次或更多的字符串 (例如:“haha” 或 “hahahahaha”),用 answer()
函数给出答案。
提示:您的表达式不应该匹配 “hahah”。使用锚点来保证这一点。
您可以使用管道符号(|)来指定“或”关系。下面的表达式将匹配 “The dog ate” 和 “The cat ate”:/The (dog|cat) ate/
您也可以使用非捕获组,但是在这个例子中我们希望获取结果。您可以在一个组内用任意多个管道符号。让上面的表达式在保持原有匹配的基础上也能够匹配 “The rabbit ate” (带匹配串在 rabbit
变量中)。
在同一个表达式中,您可以引用之前捕获到的值。只需简单的写成反斜杠后跟着捕获组数字(它在返回字符串里的索引值)。比如,下面的表达式将匹配 “The cat ate with the other cat” 和 “The dog ate with the other dog”,但不会匹配 “The cat ate with the other dog”(当然,这本身就挺奇怪的):/The (dog|cat) ate with the other 1/
写一个表达式,能够匹配同一行内两个相同的单词(比如:“hello hello world”):和之前的例子一样,用 answer()
函数给出答案。
除了文字运算符(斜杠),JavaScript 还提供 RegExp 构造器,允许您使用字符串去指定需要的表达式。这个对于将变量放在表达式中非常有用。它的使用方法如下:
// Same as /regexp?/ig new RegExp('regexp?', 'ig');
用户名同样包含在变量中。 userData
变量包含用户信息:把它打印在控制台中来查看数据格式。使用 username
变量去抽取该用户的关联词。为了能够正确验证,请把答案写在同一行。
我们已经知道了两种捕获组使用捕获值得方法:第一种是返回数组,第二种是反向引用。您也可以在 .replace()
方法的第二个参数中获取它:
var text = '*italic text*'; var replace = '<em>$1</em>'; text.replace(/\*([^*]+)\*/, replace);
写一个类似上面的代码,将 boldText
变量替换成 <strong>
元素。
JavaScript 默认的匹配模式是 “贪婪”,也就是说匹配尽可能多个:'"Hi", "Hello"'.match(/".+"/)
上面的表达式将会返回 "Hi", "Hello"
,它匹配了两个双引号间内容。懒惰匹配则和贪婪匹配相反,它会匹配尽可能少的——所以这个例子中,只会匹配 "Hi"
。
通过在量词后面加上问号来实现懒惰匹配——用上面的例子试试吧。
断言是一个应该被匹配但不会被存储的模式:不是 “匹配a然后匹配b” ,而是 “匹配后面连着b的a,但是不匹配b”。在 JavaScript 中有两种断言类型,肯定先行断言 和 否定先行断言。“先行” 就是说向前查找;JavaScript 不支持 “后行”(向后查找)。
肯定先行断言表示我们想向前查找a的匹配。为了查找a b连接,我们可以使用 /a(?=b)/
。
使用断言来从 partialSums
中取出 “6+3”。不要使用任何数字符号,用 d
。
断言也可以是否定的,所以我们可以匹配不连接在某项后的字符串。注意和字符组不同的是,它可以匹配到一些不一样的——如果你说 “后面不连着b的a”,a可以在字符串的末尾。
否定断言的语法和肯定断言的语法类似,但用感叹号(!)替换等号:比如,/a(?!b)/
会匹配后面不是字母 b 的字母 a。
用一个肯定断言接一个否定断言来提取 partialSums
中的 “3+3”。
恭喜您,完成了 Try Regex 的课程。您基本上掌握了 JavaScript 中正则表达式的大部分内容,现在您可以写出适用于大多数场景的正则表达式了。
下面这些是延伸阅读内容(不可用链接已替换):
regular-expressions.info 通用非指定语言手册
正则表达式的有趣之处就在于,对于同一个问题于有多种不同的写法。下面给出的解答有些只是正确写法中一种,仅供参考。自己思考自己尝试才能真正掌握正则表达式。