正则表达式入门
完整的正则表达式由两种字符构成。特殊字符称为“元字符”,其他称为“文字”或者是普通文本字符。
为了便于理解,我们可以把正则表达式想象为普通的语言,普通字符对应普通语言中的单词,而元字符对应语法。根据语言的规则,按照语法把单词组合起来,就会得到能传达思想的文本。
特殊字符 | 正则表达式 | 记忆方式 |
---|---|---|
换行符 | \n | new line |
换页符 | \f | form feed |
回车符 | \r | return |
空白符 | \s | space |
制表符 | \t | tab |
垂直制表符 | \v | vertical tab |
回退符 | [\b] | backspace,之所以使用[]符号是避免和\b重复 |
匹配区间 | 正则表达式 | 记忆方式 |
---|---|---|
除了换行符之外的任意字符 | . | 句号,除了句子结束符 |
单个数字,[0-9] | \d | digit |
除了[0-9] | \D | not digit |
包括下划线在内的单个字符,[A-Za-z0-9_] | \w | word |
非单字字符 | \W | not word |
匹配空白字符,包括空格、制表符、换页符和换行符 | \s | space |
匹配非空白字符 | \S | not space |
匹配一个词的边界 | \b | boundary |
匹配一个非单词边界 | \B | not boundary |
\b和\B例子:
/\bm/匹配“moon”中的‘m’;
/oo\b/并不匹配"moon"中的’oo’,因为’oo’被一个“字”字符’n’紧跟着。
/oon\b/匹配"moon"中的’oon’,因为’oon’是这个字符串的结束部分。这样他没有被一个“字”字符紧跟着。
/\w\b\w/将不能匹配任何字符串,因为在一个单词中间的字符永远也不可能同时满足没有“字”字符跟随和有“字”字符跟随两种情况。
/\Boo/匹配"noonday"中的’oo’, 而/y\B/匹配"possibly yesterday"中的’yes‘。
最容易理解的脱字符号 ^
和美元符号 $
。
正则表达式cat
寻找的是一行文本中任意位置的cat。但是^cat
只寻找行首的cat——^
用来把匹配文本“锚定”在这一行的开头。同样,cat$
只寻找位于行末的cat。
脱字符号^
和美元符号$
的特别之处在于,它们匹配的是一个位置,而不是具体的文本。
字符组
如果我们需要搜索的单词是grey,同时又不确定它是否写作“gray”,就可以使用正则表达式结构体[...]
。它允许使用者列出在某处期望匹配的字符,通常被称作字符组。 gr[ea]y
的意思是:先找到g,跟着是一个r,然后是一个a或者e,最后是一个y。
例如123456
匹配1到6中的任意一个数字。这个字符组可以作为H[123456]
的一部分,用来匹配<H1>
到<H6>
。在搜索html代码时很有用。
在字符组内部,字符组元字符 -
表示一个范围:H[1-6]
和H[123456]
是完全一样的。0-9
和a-z
常用作匹配数字和小写字母的简便方法。多重范围也是允许的,例如[0123456789abcdefABCDEF]
可以写作[0-9a-fA-F]
,可用于处理十六进制数字。
⚠️:只有在字符组内部,连字符才是元字符。否则它就只能匹配普通的连字符号。 其实,即使在字符组内部,它也不一定就是元字符。如果 连字符 出现在字符组放入开头,它表示的就是一个普通字符,而不是一个范围。同样的道理,问号也通常被当作元字符处理,但在字符组中则不是如此。例如[0-9A-Z_!?]
里面,真正的特殊字符只有那两个连字符。
小发现:
/[ads]/.test() // true
看到这里我觉得很奇怪了,为啥这里test结果为true?
我们再看另一个?:
var reg = /[ads]/;
reg.exec()
// 这里我们得到一个数组
// ["d", index: 2, input: "undefined", groups: undefined]
从这个例子可以发现如果没有制定匹配内容,会匹配undefined
这个字符串,导致匹配了内容。如果我们是这样的:/[aaa]/.test()
得到的结果就会是false了。
排除性字符组
用 [^…]
取代[…]
,这个字符组就会匹配任何未列出的字符。
例如,[^1-6]
匹配除了1到6以外的任何字符。 这个字符组中开头的^
表示 排除,所以这里列出的不是希望匹配的字符,而是不希望匹配的字符。
再看另一个例子,要匹配一对单词中字母q后面不是u的单词:q[^u]
。
用点号匹配任意字符
元字符.
是用来匹配任意字符的字符组的简便写法。
如果需要匹配06/17/52、06-17-52或者06.17.52,可以用03[-./]19[-./]76
来匹配。若需要匹配间隔为任意字符的,可以用03.19.76
来匹配。
在这里要注意的是,在03[-./]19[-./]76
中,点号.
并不是元字符(⚠️在字符组里面和外面,元字符的定义和意义是不同的)。这里的连字符-
同样也不是元字符,因为它们紧贴在[
或者[^
之后。如果连字符不在字符组的开头,例如[.-/]
,这个就是用来表示范围的。
匹配任意子表达式
|
的意思是或。我们回头来看看gr[ea]y
的例子,还能写作grey|gray
或者gr(e|a)y
。注意gr[e|a]y
不符合要求,因为在字符组中,|
只是一个和e
和a
一样的普通字符。
一个字符组只能匹配目标文本中的单个字符,而每个多选结构自身都可能是完整的正则表达式,都可以匹配任意长度的文本。
字符组基本可以算是一门独立的微型语言,而多选结构是“正则表达式语言主体”的一部分。
忽略大小写
-i
表示进行忽略大小写的匹配
总结:
-
在字符组内部,元字符的定义规则是不一样的。比如说,在字符组外部,点号是元字符,但在内部就是普通字符;脱字符
^
在字符组外部是匹配文本“锚定”在这一行的开头,在字符组内部紧接着[
时会匹配任何未列出的字符,在其他情况是普通字符。 -
不要混淆多选项和字符组
-
排除型字符组是表示所有未列出字符的字符组的简便方法。
[^x]
的意思并不是“只有当这个位置不是x时才能匹配”,而是说“匹配一个不等于x的字符”。例如,前面的概念可以匹配一个空行,而^x
则不行。
可选项元素
我们来看看 color 和 colour 的匹配。我们可以用colou?r
来解决这个问题。
元字符?
代表可选项。把它加在一个字符的后面,就表示此处容许出现这个字符,不过它的出现并非匹配成功的必要条件,即匹配前面的子表达式零次或一次。
重新出现
+
和*
的作用与?
类似。元字符+
表示之前紧邻的元素出现一次或多次,而*
表示之前紧邻的元素出现任意多次,或者不出现。+
、*
和?
这三个元字符,统称为量词,因为它们限定了所作用元素的匹配次数。
与?
一样,正则表达式中的*
也是永远不会匹配失败的,区别只在于它们的匹配结果(*是贪婪匹配,?是非贪婪匹配)。
规定重现次数的范围:区间
使用元字符序列来自定义重现次数的区间:…[min,max]
。这称为区间量词。
变量名
标识符只能包含字母、数字以及下划线,但不能以数字开头。我们可以用[a-zA-Z_][a-zA-Z_0-9]*
来匹配标识符。如果标识符的长度有限制,例如最多只能是32个字符,我们可以用到区间量词{min,max},即用{0,31}
来替代最后的*
。
引号内的字符串
匹配引号内的字符串可用“[^“]*”
。
var reg = /“[^“]*”/;
reg.exec('引号外面的内容“引号里面的内容”bulabula');
// ["“引号里面的内容”", index: 7, input: "引号外面的内容“引号里面的内容”bulabula", groups: undefined]
表示时刻的文字
例如“9:17am”或“12:30pm”
[0-9]?[0-9]:[0-9][0-9](am|pm)
能匹配到“9:17am”和“12:30pm”,但也能匹配无意义的时刻,如99:99*pm。
如果小时数是一个两位数,第一位只能是1。但是1?[0-9]
仍然能匹配到19,所以更好的办法应该把小时部分分为两部分处理。1[012]
匹配两位数,1-9
匹配一位数,结果就是(1[012]|[1-9])
。
我们再来看分钟数。第一位数字应该是[0-5]
,第二位数字应该是[0-9]
。综合起来就是(1[012]|[1-9]:[0-5][0-9](am|pm))
。
举一反三:24小时制的写法:
(([01]?[0-9])|(2[0-3])):[0-5][0-9]
接下来我们测试下:
var reg = /(([01]?[0-9])|(2[0-3])):[0-5][0-9]/;
reg.test('20:23') // true
reg.test('25:23') // true
怎么肥事???25:23也可以?
不急,我们再看下:
reg.exec('25:23')
// ["5:23", "5", "5", undefined, index: 1, input: "25:23", groups: undefined]
这里是匹配到了5:23.然后我想了下,改成了这样:
var reg = /(([01]+[0-9])|(2[0-3])):[0-5][0-9]/;
现在是可以的了。但是有一个缺陷,我也想匹配9:13这样的,即前面的0是可选的,这样就不支持了。
为了支持让它全部匹配,我又改成了这样:
var reg = /((^[01]?[0-9])|(^2[0-3])):[0-5][0-9]/;
这样就是可以的了。
子表达式
子表达式指的是整个正则中的一部分,通常是括号内的表达式,或者是括号内的表达式,或者是由|
分隔的多选分支。
例如,在[^(subject|date):]中,subject|date
通常被视为一个子表达式,其中的subject
和date
也算得上子表达式。严格来说,s
、u
、b
这些字符,都算子表达式。