正则中的特殊字符
特殊字符其实就是在正则表达式中有特殊含义或者功能的字符,它们不能被简单的理解为字面意义。例如 * 的意思是出现零次或者多次。正则表达式中常见的特殊字符有 [ \ ^ $ . | ? * + ( )(这里重点不是特殊字符,更多的特殊字符及其用法示例可以查看 Regular expression syntax cheatsheet)。
需要注意的是 “/” 不是特殊字符。
转义
就是把特殊字符变成普通(常规)字符的方法,具体做法是在特殊字符前加上 \ , \ 也叫转义字符。
例如:想要通过正则匹配字符串中的 . ,但是 . 在正则表达式中属于特殊字符, 默认含义是匹配除了换行符以外的任意单个字符,所以可以使用 \. 将小数点号变成普通字符意义,例如匹配小数的正则表达式 /^([0-9]{1,}\.[0-9]*)$/。
// javascript
var myRe = /^([0-9]{1,}\.[0-9]*)$/;
/**
* 题外话:
* - 由于点(.)和星号(*)这样的特殊符号在一个字符集(就是用中括号“[]”括起来)中没有特殊的意义,
* 所以还可以写成 /^([0-9]{1,}[.][0-9]*)$/,这样就不用转义了
* - 但是转义也是起作用的,所以这样写 /^([0-9]{1,}[\.][0-9]*)$/ 效果都一样
*/
myRe.test("42.0"); // true
myRe.test("42"); // false
特殊字符前加上转义字符 \ 时,表示该字符不再是特殊字符,而是普通字符。那如果在非特殊字符前加上 \ 会怎样呢?
在非特殊字符之前的反斜杠表示下一个字符是特殊字符,不能按照字面理解(emmm,有点“劝失足从良,拉良家下水”的感觉)。例如:正则表达式中 \b 表示匹配一个词的边界,举例
var myRe = /\bm/;
'moon'.match(myRe); // ["m", index: 0, input: "moon", groups: undefined]
上面虽然说 / 不是特殊字符, 但是在 Javascript 中,它表示正则表达式字面量的开始和结束:/...pattern.../ 。所以如果想要匹配斜杠符号本身,也需要转义。
"/".match(/\//); // ["/", index: 0, input: "/", groups: undefined]
/* 下面这种写法是无法正常运行的 */
"/".match(///)
另外由于 \ 是特殊字符,所以要想匹配字符 \ 本身含义,需要转义:
'\\'.match(/\\/); // ["\", index: 0, input: "\", groups: undefined]
// \ 在JavaScript的字符串中也是转义功能,所以也需要转义自身,后面会说
JavaScript 中的特殊字符
在 JavaScript 中,字符串也有特殊字符,并且在字符串字面量中反斜杠 \ 也是转义字符。
var txt="We are the so-called "Vikings" from the north."
// Error: Uncaught SyntaxError: Unexpected identifier
在 JavaScript 中,字符串使用单引号或者双引号来起始或者结束。这意味着上面的字符串将被截为:We are the so-called。
要解决这个问题,就必须把在 "Viking" 中的引号前面加上反斜杠 (\)。这样就可以把每个双引号转换为字面上的字符。
var txt="We are the so-called \"Vikings\" from the north."
console.log(txt); // "We are the so-called "Vikings" from the north."
/**
* 题外话:
* 如果只是在字符串中显示引号本身,可以使用单双引号嵌套的方式达到效果
*/
var txt='We are the so-called "Vikings" from the north.'
在字符串中的反斜杠 \ 也表示转义或者类似 \n 这种只能在字符串中使用的特殊字符。这个引用会“消费”并且解释这些字符,比如:
\n变成一个换行字符\u1234变成包含该码位的 Unicode 字符- 还有一些没有特殊的含义,就像
\d或者\z,碰到这种情况的话会把反斜杠移除
在 JavaScript 的字符串中要获取
\字符本身要使用var str = "\\";这样写var str = "\"会报错。其实就是需要“转义”。
双重转义
上面说了 \ 在正则表达式中和 JavaScript 的字符串字面量中都是转义字符。
在 JavaScript 中有两种方法创建正则表达式:
正则表达式字面量
var re = /ab+c/;
// 复杂点,包含了转义特殊字符
var myRe = /[a-z]:\\/i
正则表达式字面量中使用转义,直接在特殊字符前加 \ 就可以。
调用RegExp对象的构造函数
RegExp 构造函数接收一个字符串作为参数,这个字符串参数很像正则表达式字面量,但是不能直接将字面量的开关标记 / 替换成字符串的引号,因为正如上面所说:“字符串字面量中反斜杠也是转义字符”。
var regStr = "\d\.\d";
console.log(regStr); // "d.d"
var regexp = new RegExp(regStr);
"Chapter 5.1".match(regexp); // null
通过上面的代码可以知道,字符串引号会消费 \,所以在给 new RehExp 传递参数的时候,需要双倍反斜杠 \\,这也就是双重转义的含义。一重字符串转义,一重正则表达式转义。
复杂点的双重转义:
var regexp1 = new RegExp("node_modules[/\\\\]"); // /node_modules[/\\]/
var regexp2 = new RegExp("[a-z]:\\\\","i"); // /[a-z]:\\/i
偏个题,在 ES5 中,RegExp构造函数的参数有两种情况。
-
参数是字符串,这时第二个参数表示正则表达式的修饰符(flag)
var regex = new RegExp('xyz', 'i'); // 等价于 var regex = /xyz/i; -
参数是一个正则表示式,但是不允许使用第二个参数添加修饰符
var regex = new RegExp(/xyz/i); // 等价于 var regex = /xyz/i; // 无法使用第二个参数 var regex = new RegExp(/xyz/, 'i'); // Uncaught TypeError: Cannot supply flags when constructing one RegExp from another
ES6 改变了这种行为。如果RegExp构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。而且,返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符。
new RegExp(/abc/ig, 'i')
// 等价于 /abc/i