Javascript 选择 (OR) |
选择是正则表达式中的一个术语,实际上是一个简单的“或”。
在正则表达式中,它用竖线 |
表示。
例如,我们想要找出编程语言:HTML、PHP、Java 或 JavaScript。
对应的正则表达式为:html|php|java(script)?
。
用例如下:
let regexp = /html|php|css|java(script)?/gi;
let str = "First HTML appeared, then CSS, then JavaScript";
alert( str.match(regexp) ); // 'HTML', 'CSS', 'JavaScript'
我们看到过类似的东西 —— 方括号。它允许我们在多个字符中进行选择,例如 gr[ae]y
匹配 gray
或 grey
。
方括号只允许字符或字符类。选择允许任何表达式。正则表达式 A|B|C
表示表达式 A
、B
或 C
其一均可。
例如:
-
gr(a|e)y
等同于 gr[ae]y
。 -
gra|ey
表示 gra
或 ey
。
要将选择应用于模式中一部分内容的选择,我们可以将其括在括号中:
I love HTML|CSS
匹配I love HTML
或CSS
。I love (HTML|CSS)
匹配I love HTML
或I love CSS
。
示例:用于时间匹配的正则表达式
在之前的章节中有个任务是构建用于查找形如 hh:mm
的时间字符串,例如 12:00
。但是简单的 \d\d:\d\d
太模糊了。它也会匹配 25:99
(因为 25 和 99 都与模式匹配,但这不是有效的时间)。
如何构建更好的模式?
我们可以应用更精细的匹配。首先,对于时间:
- 如果第一位数是
0
或1
,那么下一位数可以是任何数值:[01]\d
。 - 否则,如果第一位数是
2
,那么下一位数必须是[0-3]
。 - 不允许其他的首位数。
我们可以使用选择在正则表达式中编写这两种变体:[01]\d|2[0-3]
。
接下来,分钟必须为从 00
到 59
的数。写成正则表达式即为 [0-5]\d
:第一个数字 0-5
,然后是任何数字。
如果我们将小时和分钟的正则表达式组合在一起,我们会得到:[01]\d|2[0-3]:[0-5]\d
我们差不多完成了,但有一个问题。选择 |
现在恰好位于 [01]\d
和 2[0-3]:[0-5]\d
之间。
也就是说:它只匹配符号左侧或右侧任一表达式。
[01]\d | 2[0-3]:[0-5]\d
此模式查找 [01]\d
或 2[0-3]:[0-5]\d
。
但这是错误的,应该只在正则表达式的“小时”部分使用选择,以允许 [01]\d
或 2[0-3]
。让我们通过将“小时”括在括号中来纠正这个问题:([01]\d|2[0-3]):[0-5]\d
最终的解决方案:
let regexp = /([01]\d|2[0-3]):[0-5]\d/g;
alert("00:00 10:10 23:59 25:99 1:2".match(regexp)); // 00:00,10:10,23:59
任务
查找编程语言
有很多编程语言,例如 Java、JavaScript、PHP、C 或 C++。
构建一个正则表达式,用来匹配字符串 Java JavaScript PHP C++ C
中包含的编程语言:
let regexp = /你的正则表达式/g;
alert("Java JavaScript PHP C++ C".match(regexp)); // Java JavaScript PHP C++ C
解决方案
首先想到的解法是列出所有编程语言,在它们之间加上 |
符号。
但运行结果不符合预期:
let regexp = /Java|JavaScript|PHP|C|C\+\+/g;
let str = "Java, JavaScript, PHP, C, C++";
alert( str.match(regexp) ); // Java,Java,PHP,C,C
正则表达式引擎注意查找选择。也就是说:它先检查是否存在 Java
,不存在 —— 接着匹配 JavaScript
及其后的字符串。
结果,JavaScript
永远匹配不到,因为 Java
先被匹配了。
C
和 C++
同理。
这个问题有两个解决方式:
- 更改顺序以先检查较长的匹配项:
JavaScript|Java|C\+\+|C|PHP
。 - 合并相同前缀:
Java(Script)?|C(\+\+)?|PHP
。
测试一下:
let regexp = /Java(Script)?|C(\+\+)?|PHP/g;
let str = "Java, JavaScript, PHP, C, C++";
alert( str.match(regexp) ); // Java,JavaScript,PHP,C,C++
查找 bbtag 对
“bb-tag” 形如 [tag]...[/tag]
,tag
匹配 b
、url
或 quote
中之一。
例如:
[b]text[/b]
[url]http://google.com[/url]
BB-tags 可以嵌套。但标签不能自嵌套,比如:
可以:
[url] [b]http://google.com[/b] [/url]
[quote] [b]text[/b] [/quote]
不可以:
[b][b]text[/b][/b]
标签可以包含换行,通常:
[quote]
[b]text[/b]
[/quote]
构造一个正则表达式用于查找所有 BB-tags 及其内容。
例如:
let regexp = /your regexp/flags;
let str = "..[url]http://google.com[/url]..";
alert( str.match(regexp) ); // [url]http://google.com[/url]
如果标签嵌套,那么我们需要记录匹配的外层标签(如果需要,我们可以继续在其内容中搜索):
let regexp = /你的正则表达式/flags;
let str = "..[url][b]http://google.com[/b][/url]..";
alert( str.match(regexp) ); // [url][b]http://google.com[/b][/url]
解决方案
起始标签是 \[(b|url|quote)\]
。
匹配字符串直到遇到结束标签 —— 让我们使用模式 .*?
和修饰符 s
来匹配包括换行符在内的任何字符,然后向结束标签添加反向引用。
完整模式为:\[(b|url|quote)\].*?\[/\1]
。
代码运行如下:
let regexp = /\[(b|url|quote)].*?\[\/\1]/gs;
let str = `
[b]hello![/b]
[quote]
[url]http://google.com[/url]
[/quote]
`;
alert( str.match(regexp) ); // [b]hello![/b],[quote][url]http://google.com[/url][/quote]
请注意,除了转义 [
之外,我们还必须为结束标签 [\/\1]
转义一个斜线,因为通常斜线会关闭模式。
查找引用的字符串
构建一个正则表达式以查找双引号 "..."
中的字符串。
字符串应该支持转义,就像 JavaScript 字符串一样。例如,引号可以插入为 \"
,换行符可以插入为 \n
,而反斜线本身可以插入为 \\
。
let str = "Just like \"here\".";
请注意,转义的引号 \"
不会结束字符串匹配。
所以,我们应该匹配两个引号之间的内容,且忽略中间转义的引号。
这是任务的基本部分,否则这个任务就没什么意思了。
要匹配的字符串示例:
.. "test me" ..
.. "Say \"Hello\"!" ..(其中有被转义的引号)
.. "\\" ..(其中有双反斜线)
.. "\\ \"" ..(其中有双反斜线和被转义的引号)
在 JavaScript 中,双反斜线用于把反斜线转义为字符串,如下所示:
let str = ' .. "test me" .. "Say \\"Hello\\"!" .. "\\\\ \\"" .. ';
// 存储中的字符串
alert(str); // .. "test me" .. "Say \"Hello\"!" .. "\\ \"" ..
解决方案
解决方案:/"(\\.|[^"\\])*"/g
。
一步一步来分析一下:
- 首先匹配左双引号
"
- 接着如果有反斜线
\\
,则匹配其后跟随的任意字符(.)。(技术上,我们必须在模式中用双反斜线,因为它是一个特殊的字符,但实际上是一个反斜线字符)。 - 如果没有,则匹配除双引号(表示字符串的末尾)和反斜线(排除仅存在反斜杠的情况,反斜杠仅在和其后字符一起使用时有效)外的任何字符:
[^"\\]
。 - ……继续匹配直到遇到右双引号
代码运行如下:
let regexp = /"(\\.|[^"\\])*"/g;
let str = ' .. "test me" .. "Say \\"Hello\\"!" .. "\\\\ \\"" .. ';
alert( str.match(regexp) ); // "test me","Say \"Hello\"!","\\ \""
查找完整标签
写出一个正则表达式,用于查找 <style...>
标签。它应该匹配完整的标签:该标签可能没有特性(attributes)<style>
,也可能有很多特性 <style type="..." id="...">
。
……同时正则表达式不应该匹配 <styler>
!
例如:
let regexp = /你的正则表达式/g;
alert( '<style> <styler> <style test="...">'.match(regexp) ); // <style>, <style test="...">
解决方案
模式的开头很明显:<style
。
……但我们不能简单地将表达式写为 <style.*?>
,因为会匹配上 <styler>
。
我们要匹配的是在 <style
之后紧跟着一个空格然后是可选的其他内容,或者直接是闭标签 >
。
写成正则表达式即为:<style(>|\s.*?>)
。
代码运行如下:
let regexp = /<style(>|\s.*?>)/g;
alert( '<style> <styler> <style test="...">'.match(regexp) ); // <style>, <style test="...">
更多建议: