正则的扩展

正则的扩展

  1. 1.         RegExp构造函数
  2. 2.         字符串的正则方法
  3. 3.         u修饰符
  4. 4.         y修饰符
  5. 5.         sticky属性
  6. 6.         flags属性
  7. 7.         RegExp.escape()
  8. 8.         后行断言

RegExp构造函数

在ES5中,RegExp构造函数的参数有两种情况。

第一种情况是,参数是字符串,这时第二个参数表示正则表达式的修饰符(flag)。

var regex = new RegExp(‘xyz‘, ‘i‘);

// 等价于

var regex = /xyz/i;

第二种情况是,参数是一个正则表示式,这时会返回一个原有正则表达式的拷贝。

var regex = new RegExp(/xyz/i);

// 等价于

var regex = /xyz/i;

但是,ES5不允许此时使用第二个参数,添加修饰符,否则会报错。

var regex = new RegExp(/xyz/, ‘i‘);

// Uncaught TypeError: Cannot supply flags when constructing one RegExp from another

ES6改变了这种行为。如果RegExp构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。而且,返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符。

new RegExp(/abc/ig, ‘i‘).flags

// "i"

上面代码中,原有正则对象的修饰符是ig,它会被第二个参数i覆盖。

字符串的正则方法

字符串对象共有4个方法,可以使用正则表达式:match()、replace()、search()和split()。

ES6将这4个方法,在语言内部全部调用RegExp的实例方法,从而做到所有与正则相关的方法,全都定义在RegExp对象上。

  • String.prototype.match 调用 RegExp.prototype[Symbol.match]
  • String.prototype.replace 调用 RegExp.prototype[Symbol.replace]
  • String.prototype.search 调用 RegExp.prototype[Symbol.search]
  • String.prototype.split 调用 RegExp.prototype[Symbol.split]

u修饰符

ES6对正则表达式添加了u修饰符,含义为“Unicode模式”,用来正确处理大于\uFFFF的Unicode字符。也就是说,会正确处理四个字节的UTF-16编码。

/^\uD83D/u.test(‘\uD83D\uDC2A‘)

// false

/^\uD83D/.test(‘\uD83D\uDC2A‘)

// true

上面代码中,\uD83D\uDC2A是一个四个字节的UTF-16编码,代表一个字符。但是,ES5不支持四个字节的UTF-16编码,会将其识别为两个字符,导致第二行代码结果为true。加了u修饰符以后,ES6就会识别其为一个字符,所以第一行代码结果为false。

一旦加上u修饰符号,就会修改下面这些正则表达式的行为。

1)点字符

点(.)字符在正则表达式中,含义是除了换行符以外的任意单个字符。对于码点大于0xFFFF的Unicode字符,点字符不能识别,必须加上u修饰符。

var s = ‘??‘;

/^.$/.test(s) // false

/^.$/u.test(s) // true

上面代码表示,如果不添加u修饰符,正则表达式就会认为字符串为两个字符,从而匹配失败。

2Unicode字符表示法

ES6新增了使用大括号表示Unicode字符,这种表示法在正则表达式中必须加上u修饰符,才能识别。

/\u{61}/.test(‘a‘) // false

/\u{61}/u.test(‘a‘) // true

/\u{20BB7}/u.test(‘??‘) // true

上面代码表示,如果不加u修饰符,正则表达式无法识别\u{61}这种表示法,只会认为这匹配61个连续的u。

3)量词

使用u修饰符后,所有量词都会正确识别码点大于0xFFFF的Unicode字符。

/a{2}/.test(‘aa‘) // true

/a{2}/u.test(‘aa‘) // true

/??{2}/.test(‘????‘) // false

/??{2}/u.test(‘????‘) // true

另外,只有在使用u修饰符的情况下,Unicode表达式当中的大括号才会被正确解读,否则会被解读为量词。

/^\u{3}$/.test(‘uuu‘) // true

上面代码中,由于正则表达式没有u修饰符,所以大括号被解读为量词。加上u修饰符,就会被解读为Unicode表达式。

4)预定义模式

u修饰符也影响到预定义模式,能否正确识别码点大于0xFFFF的Unicode字符。

/^\S$/.test(‘??‘) // false

/^\S$/u.test(‘??‘) // true

上面代码的\S是预定义模式,匹配所有不是空格的字符。只有加了u修饰符,它才能正确匹配码点大于0xFFFF的Unicode字符。

利用这一点,可以写出一个正确返回字符串长度的函数。

function codePointLength(text) {

var result = text.match(/[\s\S]/gu);

return result ? result.length : 0;

}

var s = ‘????‘;

s.length // 4

codePointLength(s) // 2

5i修饰符

有些Unicode字符的编码不同,但是字型很相近,比如,\u004B与\u212A都是大写的K。

/[a-z]/i.test(‘\u212A‘) // false

/[a-z]/iu.test(‘\u212A‘) // true

上面代码中,不加u修饰符,就无法识别非规范的K字符。

y修饰符

除了u修饰符,ES6还为正则表达式添加了y修饰符,叫做“粘连”(sticky)修饰符。

y修饰符的作用与g修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始。不同之处在于,g修饰符只要剩余位置中存在匹配就可,而y修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。

var s = ‘aaa_aa_a‘;

var r1 = /a+/g;

var r2 = /a+/y;

r1.exec(s) // ["aaa"]

r2.exec(s) // ["aaa"]

r1.exec(s) // ["aa"]

r2.exec(s) // null

上面代码有两个正则表达式,一个使用g修饰符,另一个使用y修饰符。这两个正则表达式各执行了两次,第一次执行的时候,两者行为相同,剩余字符串都是_aa_a。由于g修饰没有位置要求,所以第二次执行会返回结果,而y修饰符要求匹配必须从头部开始,所以返回null。

如果改一下正则表达式,保证每次都能头部匹配,y修饰符就会返回结果了。

var s = ‘aaa_aa_a‘;

var r = /a+_/y;

r.exec(s) // ["aaa_"]

r.exec(s) // ["aa_"]

上面代码每次匹配,都是从剩余字符串的头部开始。

使用lastIndex属性,可以更好地说明y修饰符。

const REGEX = /a/g;

// 指定从2号位置(y)开始匹配

REGEX.lastIndex = 2;

// 匹配成功

const match = REGEX.exec(‘xaya‘);

// 在3号位置匹配成功

match.index // 3

// 下一次匹配从4号位开始

REGEX.lastIndex // 4

// 4号位开始匹配失败

REGEX.exec(‘xaxa‘) // null

上面代码中,lastIndex属性指定每次搜索的开始位置,g修饰符从这个位置开始向后搜索,直到发现匹配为止。

y修饰符同样遵守lastIndex属性,但是要求必须在lastIndex指定的位置发现匹配。

const REGEX = /a/y;

// 指定从2号位置开始匹配

REGEX.lastIndex = 2;

// 不是粘连,匹配失败

REGEX.exec(‘xaya‘) // null

// 指定从3号位置开始匹配

REGEX.lastIndex = 3;

// 3号位置是粘连,匹配成功

const match = REGEX.exec(‘xaxa‘);

match.index // 3

REGEX.lastIndex // 4

进一步说,y修饰符号隐含了头部匹配的标志^。

/b/y.exec(‘aba‘)

// null

上面代码由于不能保证头部匹配,所以返回null。y修饰符的设计本意,就是让头部匹配的标志^在全局匹配中都有效。

在split方法中使用y修饰符,原字符串必须以分隔符开头。这也意味着,只要匹配成功,数组的第一个成员肯定是空字符串。

// 没有找到匹配

‘x##‘.split(/#/y)

// [ ‘x##‘ ]

// 找到两个匹配

‘##x‘.split(/#/y)

// [ ‘‘, ‘‘, ‘x‘ ]

后续的分隔符只有紧跟前面的分隔符,才会被识别。

‘#x#‘.split(/#/y)

// [ ‘‘, ‘x#‘ ]

‘##‘.split(/#/y)

// [ ‘‘, ‘‘, ‘‘ ]

下面是字符串对象的replace方法的例子。

const REGEX = /a/gy;

‘aaxa‘.replace(REGEX, ‘-‘) // ‘--xa‘

上面代码中,最后一个a因为不是出现下一次匹配的头部,所以不会被替换。

单单一个y修饰符对match方法,只能返回第一个匹配,必须与g修饰符联用,才能返回所有匹配。

‘a1a2a3‘.match(/a\d/y) // ["a1"]

‘a1a2a3‘.match(/a\d/gy) // ["a1", "a2", "a3"]

y修饰符的一个应用,是从字符串提取token(词元),y修饰符确保了匹配之间不会有漏掉的字符。

const TOKEN_Y = /\s*(\+|[0-9]+)\s*/y;

const TOKEN_G  = /\s*(\+|[0-9]+)\s*/g;

tokenize(TOKEN_Y, ‘3 + 4‘)

// [ ‘3‘, ‘+‘, ‘4‘ ]

tokenize(TOKEN_G, ‘3 + 4‘)

// [ ‘3‘, ‘+‘, ‘4‘ ]

function tokenize(TOKEN_REGEX, str) {

let result = [];

let match;

while (match = TOKEN_REGEX.exec(str)) {

result.push(match[1]);

}

return result;

}

上面代码中,如果字符串里面没有非法字符,y修饰符与g修饰符的提取结果是一样的。但是,一旦出现非法字符,两者的行为就不一样了。

tokenize(TOKEN_Y, ‘3x + 4‘)

// [ ‘3‘ ]

tokenize(TOKEN_G, ‘3x + 4‘)

// [ ‘3‘, ‘+‘, ‘4‘ ]

上面代码中,g修饰符会忽略非法字符,而y修饰符不会,这样就很容易发现错误。

sticky属性

与y修饰符相匹配,ES6的正则对象多了sticky属性,表示是否设置了y修饰符。

var r = /hello\d/y;

r.sticky // true

flags属性

ES6为正则表达式新增了flags属性,会返回正则表达式的修饰符。

// ES5的source属性

// 返回正则表达式的正文

/abc/ig.source

// "abc"

// ES6的flags属性

// 返回正则表达式的修饰符

/abc/ig.flags

// ‘gi‘

RegExp.escape()

字符串必须转义,才能作为正则模式。

function escapeRegExp(str) {

return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, ‘\\$&‘);

}

let str = ‘/path/to/resource.html?search=query‘;

escapeRegExp(str)

// "\/path\/to\/resource\.html\?search=query"

上面代码中,str是一个正常字符串,必须使用反斜杠对其中的特殊字符转义,才能用来作为一个正则匹配的模式。

已经有提议将这个需求标准化,作为RegExp对象的静态方法RegExp.escape(),放入ES7。2015年7月31日,TC39认为,这个方法有安全风险,又不愿这个方法变得过于复杂,没有同意将其列入ES7,但这不失为一个真实的需求。

RegExp.escape(‘The Quick Brown Fox‘);

// "The Quick Brown Fox"

RegExp.escape(‘Buy it. use it. break it. fix it.‘);

// "Buy it\. use it\. break it\. fix it\."

RegExp.escape(‘(*.*)‘);

// "\(\*\.\*\)"

字符串转义以后,可以使用RegExp构造函数生成正则模式。

var str = ‘hello. how are you?‘;

var regex = new RegExp(RegExp.escape(str), ‘g‘);

assert.equal(String(regex), ‘/hello\. how are you\?/g‘);

目前,该方法可以用上文的escapeRegExp函数或者垫片模块regexp.escape实现。

var escape = require(‘regexp.escape‘);

escape(‘hi. how are you?‘);

// "hi\\. how are you\\?"

后行断言

JavaScript语言的正则表达式,只支持先行断言(lookahead)和先行否定断言(negative lookahead),不支持后行断言(lookbehind)和后行否定断言(negative lookbehind)。

目前,有一个提案,在ES7加入后行断言。V8引擎4.9版已经支持,Chrome浏览器49版打开”experimental JavaScript features“开关(地址栏键入about:flags),就可以使用这项功能。

”先行断言“指的是,x只有在y前面才匹配,必须写成/x(?=y)/。比如,只匹配百分号之前的数字,要写成/\d+(?=%)/。”先行否定断言“指的是,x只有不在y前面才匹配,必须写成/x(?!y)/。比如,只匹配不在百分号之前的数字,要写成/\d+(?!%)/。

/\d+(?=%)/.exec(‘100% of US presidents have been male‘)  // ["100"]

/\d+(?!%)/.exec(‘that’s all 44 of them‘)                 // ["44"]

上面两个字符串,如果互换正则表达式,就会匹配失败。另外,还可以看到,”先行断言“括号之中的部分((?=%)),是不计入返回结果的。

"后行断言"正好与"先行断言"相反,x只有在y后面才匹配,必须写成/(?<=y)x/。比如,只匹配美元符号之后的数字,要写成/(?<=\$)\d+/。”后行否定断言“则与”先行否定断言“相反,x只有不在y后面才匹配,必须写成/(?<!y)x/。比如,只匹配不在美元符号后面的数字,要写成/(?<!\$)\d+/。

/(?<=\$)\d+/.exec(‘Benjamin Franklin is on the $100 bill‘)  // ["100"]

/(?<!\$)\d+/.exec(‘it’s is worth about €90‘)                // ["90"]

上面的例子中,"后行断言"的括号之中的部分((?<=\$)),也是不计入返回结果。

"后行断言"的实现,需要先匹配/(?<=y)x/的x,然后再回到左边,匹配y的部分。这种"先右后左"的执行顺序,与所有其他正则操作相反,导致了一些不符合预期的行为。

首先,”后行断言“的组匹配,与正常情况下结果是不一样的。

/(?<=(\d+)(\d+))$/.exec(‘1053‘) // ["", "1", "053"]

/^(\d+)(\d+)$/.exec(‘1053‘) // ["1053", "105", "3"]

上面代码中,需要捕捉两个组匹配。没有"后行断言"时,第一个括号是贪婪模式,第二个括号只能捕获一个字符,所以结果是105和3。而"后行断言"时,由于执行顺序是从右到左,第二个括号是贪婪模式,第一个括号只能捕获一个字符,所以结果是1和053。

其次,"后行断言"的反斜杠引用,也与通常的顺序相反,必须放在对应的那个括号之前。

/(?<=(o)d\1)r/.exec(‘hodor‘)  // null

/(?<=\1d(o))r/.exec(‘hodor‘)  // ["r", "o"]

上面代码中,如果后行断言的反斜杠引用(\1)放在括号的后面,就不会得到匹配结果,必须放在前面才可以。

转载于  阮一峰  博客。

时间: 2024-10-17 14:33:44

正则的扩展的相关文章

2015年8月27日课程作业(文件权限管理及grep正则和扩展正则表达式)-JY1506402-19+liuhui880818

学习内容:文件权限管理及grep正则和扩展正则表达式 系统环境:CentOS 6.7/7 x86_64 一.作业(练习)内容: 1.总结本此课程中所涉及命令的使用方法及相关示例展示: 2.总结基本正则表达式及扩展正则表达式 3.显示/etc/passwd文件中以bash结尾的行 4.显示/etc/passwd文件中的两位数或三位数 5.显示`netstat -tan`命令结果中以'LISTEN'后跟0个.1个或者多个空白字符结尾的行 6.添加用户bash.testbash.basher以及nol

基础正则和扩展正则的作用

*基础正则表达式:basic regular expression BRE包括:^ $ . [] [^] 和扩展正则不同的是grep和sed不需要加参数也可以使用 ^^d 以d开头的行,例如:ls l|grep "^d" 给三剑客使用只查看以d开头的行,正则表达式的意思为,以.....开头,^d就是以d开头[[email protected] data]# grep '^m' oldboy.txt 以m开头的行my qq is 49000448 $以什么什么结尾的行,例如grep &q

js-正则表达式、ES6--(4)正则的扩展

2019-10-27 学习内容: 两大部分:js正则匹配.es6正则扩展 补充: 1.什么叫先行断言? “先行断言”指的是,x只有在y前面才匹配,必须写成/x(?=y)/.比如,只匹配百分号之前的数字,要写成/\d+(?=%)/.“先行否定断言”指的是,x只有不在y前面才匹配,必须写成/x(?!y)/.比如,只匹配不在百分号之前的数字,要写成/\d+(?!%)/. 2.什么叫后行断言? “后行断言”正好与“先行断言”相反,x只有在y后面才匹配,必须写成/(?<=y)x/.比如,只匹配美元符号之后

ES6系列,4.正则的扩展

(1)RegExp构造函数 ES5中,RegExp构造函数有两种:第一种参数是字符串,这时第二个参数表示 正则表达式的修饰符(flag). var regex = new RegExp('xyz', 'i'); // 等价于 var regex = /xyz/i; 第二种参数是一个正则表示式,这时会返回一个原有正则表达式的拷贝: var regex = new RegExp(/xyz/i); // 等价于 var regex = /xyz/i; 但不允许此时用第二个参数: ES6规定,第一个参数

ES6入门——正则的扩展

1.RegExp构造函数 在ES5中,RegExp构造函数的参数有两种情况.第一种情况是参数是字符串,这时第二个参数表示正则表达式的修饰符:第二种情况是,参数是一个正则表示式,这时会返回一个原有正则表达式的拷贝.但是,ES5不允许此时使用第二个参数,添加修饰符,否则会报错 var regex = new RegExp('xyz','i'); //等价于 var regex = /xyz/i; var regex = new RegExp(/xyz/i); //等价于 var regex = /x

ES6--字符串、正则、数值、数组的扩展

三.字符串的扩展 字符编码 JavaScript内部,字符以UTF-16的格式储存,每个字符固定为2个字节.对于那些需要4个字节储存的字符(Unicode码点大于0xFFFF的字符),JavaScript会认为它们是两个字符.然而汉字往往就是4个字节存储,ES6之前处理起来有些麻烦. 示例:字符编码 var ChineseName = "李刚"; var EnglishName = "ligang"; ChineseName.codePointAt(1); // 0

sed正则经典例题

**题目:已知目标文件如下** <html> <title>First Web</title> <body> h1Helloh1 h2Helloh2 h3Helloh3 </body> </html> 请使文件中的内容输出为: <h1>Hello</h1> <h2>Hello</h2> <h3>Hello</h3> 创建环境 [[email protected] 

正则与shell脚本

Day01 计算机组成硬件-->内核[驱动]-->shell或图形-->人 汽车shellbash--宝马tcsh--吉利#cat /etc/shells bash-completion[rhel7] 命令和路径选项 /etc/profile 环境变量和启动程序/etc/bashrc  函数和别名/root/.bash_profile/root/.bashrc #vim /etc/profileHISTSIZE=1000    命令历史的条目echo "test" 标

正则、grep、sed、awk

每次用到正则都要蛋疼一下,索性总结一下在这里. 正则 正则表达式主要分为基础正则和扩展正则.注意,正则和一般命令行输入的命令的通配符不同.正则只使用于支持这种表示法的工具,如:vi,grep,sed.awk.而ls等命令不支持这种表示,只能使用bash自身的通配符. 基础部分: [abc] 匹配括号中的一个 [^abc] 匹配非括号中的一个(取反) ^word 以word开头 word$ 以word结尾 [n1-n2] 从n1到n2之间的所有连续字符. 注意:这个连续与否,与ASCII编码有关,