脚本宝典收集整理的这篇文章主要介绍了1}([^""' >]*),脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
前言
正则表达式是软件领域为数不多的伟大创作。与之相提并论是分组交换网络、Web、Lisp、哈希算法、UNIX、编译技术、关系模型、面向对象等。正则自身简单、优美、功能强大、妙用无穷。
学习正则表达式,语法并不难,稍微看些例子,多可照葫芦画瓢。但三两篇快餐文章,鲜能理解深刻。再遇又需一番查找,竹篮打水一场空。不止正则,其他技术点同样,需要系统的学习。多读经典书籍,站在巨人肩膀前行。
这里涉及的东西太多,我就着重讲日常开发中可能会用到的内容,如果像深入理解的话推荐翻阅书籍《精通正则表达式》
(所以简单来说,学习正则就是投入高,收益低)(起初一看简单易懂,深入了解过后感叹正则的强大)
全文略长,可以选择感兴趣的部分看
1、介绍正则
正则表达式严谨来讲,是一种描述字符串结构模式的形式化表达方法。起始于数学领域,流行于 PErl 正则引擎。JavaScript 从 ES 3 引入正则表达式,ES 6 扩展
对正则表达式支持。
正则原理
对于固定字符串的处理,简单的字符串匹配算法(类KMP
算法)相较更快;但如果进行复杂多变的字符处理,正则表达式速度则更胜一筹。那正则表达式具体匹配原理是什么?这就涉及到编译原理的知识(编译原理着实是我大三里面最头疼的课程了)
正则表达式引擎实现采用一种特殊理论模型:有穷自动机(FinITe Automata
)也叫有限状态自动机(finite-state machine
)具体的细节见文章底部的参考文档
字符组
字符组
含义
[ab]
匹配 a 或 b
[0-9]
匹配 0 或 1 或 2 ... 或 9
1
匹配 除 a、b 任意字符
字符组
含义
d
表示 [0-9],数字字符
D
表示 [^0-9],非数字字符
w
表示 [_0-9a-zA-Z],单词字符,注意下划线
W
表示 [^_0-9a-zA-Z],非单词字符
s
表示 [ tvnrf],空白符
S
表示 [^ tvnrf],非空白符
.
表示 [^nru2028u2029]。通配符,匹配除换行符、回车符、行分隔符、段分隔符外任意字符
量词
匹配优先量词
忽略优先量词
含义
{m,n}
{m,n}?
表示至少出现 m 次,至多 n 次
{m,}
{m,}?
表示至少出现 m 次
{m}
{m}?
表示必须出现 m 次,等价 {m,m}
?
??
等价 {0,1}
+
+?
等价 {1,}
*
*?
等价 {0,}
锚点与断言
正则表达式中有些结构并不真正匹配文本,只负责判断在某个位置左/右侧的文本是否符合要求,被称为锚点。常见锚点有三类:行起始/结束位置、单词边界、环视。在 ES5 中共有 6 个锚点。
锚点
含义
^
匹配开头,多行匹配中匹配行开头
$
匹配结尾,多行匹配中匹配行结尾
b
单词边界,w 与 W 之间位置
B
非单词边界
(?=p)
该位置后面字符要匹配 p
(?!p)
该位置后面字符不匹配 p
需要注意,b
也包括 w
与 ^
之间的位置,以及 w
与 $
之间的位置。如图所示。
<img title="1}([^\"\"\' >]*)
" alt="1}([^\"\"\' >]*)" data-src="/img/bvbnRR8?w=799&h=219" src="https://static.segmentfault.COM/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
修饰符是指匹配时使用的模式规则。ES5 中存在三种匹配模式:忽略大小写模式、多行模式、全局匹配模式,对应修饰符如下。
修饰符 | 含义 |
---|---|
i | 不区分大小写匹配 |
m | 允许匹配多行 |
g | 执行全局匹配 |
u | Unicode 模式,用来正确处理大于uFFFF 的 Unicode 字符,处理四个字节的 UTF-16 编码。 |
y | 粘连模式,和g相似都是全局匹配,但是特点是:后一次匹配都从上一次匹配成功的下一个位置开始,必须从剩余的第一个位置开始,这就是“粘连”的涵义。 |
s | dotAll 模式,大部分情况是用来处理行终止符的 |
字符串对象共有 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]
" alt="1}([^\"\"\' >]*)" data-src="/img/bVbnRSc?w=650&h=255" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
字符串的replace方法,应该是我们最常用的方法之一了,这里我给详细的说一下其中的各种使用攻略。
" alt="1}([^\"\"\' >]*)" data-src="/img/bVbnRSM?w=1380&h=598" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
replace
函数的第一个参数可以是一个正则,或者是一个字符串(字符串没有全局模式,仅匹配一次),用来匹配你想要将替换它掉的文本内容
第二个参数可以是字符串,或者是一个返回字符串的函数。这里请注意,如果使用的是字符串,JS 引擎会给你一些 tips 来攻略这段文本:
变量名 | 代表的值 |
---|---|
$$ |
插入一个 "$"。 |
$& |
插入匹配的子串。 |
$` | 插入当前匹配的子串左边的内容。 |
$' | 插入当前匹配的子串右边的内容。 |
$n | 假如第一个参数是 RegExp 对象,并且 n 是个小于100的非负整数,那么插入第 n 个括号匹配的字符串。提示:索引是从1开始,注意这里的捕获组规则 |
如果你不清楚捕获组的顺序,给你一个简单的法则:从左到右数 >>> 第几个 '(' 符号就是第几个捕获组
(特别适用于捕获组里有捕获组的情况)(在函数模式里,解构赋值时会特别好用)
" alt="1}([^\"\"\' >]*)" data-src="/img/bVbnRSC?w=312&h=83" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
$`:就是相当于正则匹配到的内容的左侧文本
$':就是相当于正则匹配到的内容右侧文本
$&:正则匹配到的内容
$1 - $n :对应捕获组
如果参数使用的是函数,则可以对匹配的内容进行一些过滤或者是补充
下面是该函数的参数:
变量名 | 代表的值 |
---|---|
match | 匹配的子串。(对应于上述的$&。) |
p1,p2, ... |
假如replace()方法的第一个参数是一个RegExp 对象,则代表第n个括号匹配的字符串。(对应于上述的$1,$2等。)例如, 如果是用 /(a+)(b+)/ 这个来匹配, p1 就是匹配的 a+ , p2 就是匹配的 b+。
|
offset |
匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是“abcd”,匹配到的子字符串是“bc”,那么这个参数将是1) |
string | 被匹配的原字符串。 |
一个示例,从富文本里面,匹配到里面的图片标签的地址
" alt="1}([^\"\"\' >]*)" data-src="/img/bVbnRSF?w=2048&h=546" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
可以说,使用函数来替换文本的话,基本上你想干嘛就干嘛
" alt="1}([^\"\"\' >]*)" data-src="/img/bVbnRSS?w=2048&h=582" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
" alt="1}([^\"\"\' >]*)" data-src="/img/bVbnRST?w=1468&h=546" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
" alt="1}([^\"\"\' >]*)" data-src="/img/bVbnRS0?w=1060&h=546" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
和String.prototype.search 的功能很像,但是这个是返回布尔值,search返回的是下标,这个从语义化角度看比较适合校检
" alt="1}([^\"\"\' >]*)" data-src="/img/bVbnRS1?w=497&h=332" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
" alt="1}([^\"\"\' >]*)" data-src="/img/bVbnRS3?w=2048&h=798" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
主要内容是ES6 里新增的修饰符(u,y,s)(g,m,i 就不说了)、贪婪和非贪婪模式、先行/后行断言
ES6 对正则表达式添加了u
修饰符,含义为“Unicode 模式”,用来正确处理大于uFFFF
的 Unicode 字符。也就是说,会正确处理四个字节的 UTF-16 编码。少说废话,看图
" alt="1}([^\"\"\' >]*)" data-src="/img/bVbnRS4?w=390&h=291" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
但是很可惜的是 MDN给出的浏览器兼容性如下:(截止至2019.01.24),所以离生产环境上使用还是有点时间
" alt="1}([^\"\"\' >]*)" data-src="/img/bVbnRS6?w=1041&h=109" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
" alt="1}([^\"\"\' >]*)" data-src="/img/bVbnRS8?w=1040&h=107" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
除了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('xaya') // 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('xaya');
match.index // 3
REGEX.lastIndex // 4
实际上,y
修饰符号隐含了头部匹配的标志^
。
/b/y.exec('aba')
// null
上面代码由于不能保证头部匹配,所以返回null
。y
修饰符的设计本意,就是让头部匹配的标志^
在全局匹配中都有效。
下面是字符串对象的replace
方法的例子。
const REGEX = /a/gy;
'aaxa'.replace(REGEX, '-') // '--xa'
上面代码中,最后一个a
因为不是出现在下一次匹配的头部,所以不会被替换。
单单一个y
修饰符对match
方法,只能返回第一个匹配,必须与g
修饰符联用,才能返回所有匹配。
'a1a2a3'.match(/ad/y) // ["a1"]
'a1a2a3'.match(/ad/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
修饰符不会,这样就很容易发现错误。
很遗憾,这个的浏览器兼容性也不咋地
" alt="1}([^\"\"\' >]*)" data-src="/img/bVbnRTf?w=1041&h=167" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
但是,如果你的项目里有集成了babel,就可以使用以上的两个修饰符了,他们分别是
@babel-plugin-transform-es2015-sticky-regex
@babel-plugin-transform-es2015-unicode-regex
正则表达式中,点(.
)是一个特殊字符,代表任意的单个字符,但是有两个例外。一个是四个字节的 UTF-16 字符,这个可以用u
修饰符解决;另一个是行终止符(line terminator character)。
所谓行终止符,就是该字符表示一行的终结。以下四个字符属于”行终止符“。
n
)r
)虽然这个浏览器兼容性也很差,但是我们有方法来模拟它的效果,只是语义化上有点不友好
/foo.bar/.test('foonbar') // false
/foo[^]bar/.test('foonbar') // true
/foo[sS]bar/.test('foonbar') // true 我喜欢这种
贪婪模式:正则表达式在匹配时会尽可能多地匹配,直到匹配失败,默认是贪婪模式。
非贪婪模式:让正则表达式仅仅匹配满足表达式的内容,即一旦匹配成功就不再继续往下,这就是非贪婪模式。在量词后面加?即可。
" alt="1}([^\"\"\' >]*)" data-src="/img/bVbnRTk?w=374&h=273" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
在某些情况下,我们需要编写非贪婪模式场景下的正则,比如捕获一组标签或者一个自闭合标签
" alt="1}([^\"\"\' >]*)" data-src="/img/bVbnRTm?w=2048&h=438" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
这时捕获到了一组很奇怪的标签,如果我们的目标是只想捕获img标签的话,显然是不理想的,这时非贪婪模式就可以用在这里了
" alt="1}([^\"\"\' >]*)" data-src="/img/bVbnRTp?w=2048&h=438" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
只需要在量词后加 ?
就会启用非贪婪模式,在特定情况下是特别有效的
有时候,我们会有些需求,具体是:匹配xxx前面/后面的xxx。很尴尬的是,在很久之前,只支持先行断言(lookahead)和先行否定断言(negative lookahead),不支持后行断言(lookbehind)和后行否定断言(negative lookbehind),在ES2018 之后才引入后行断言
名称 | 正则 | 含义 |
---|---|---|
先行断言 | /want(?=asset)/ | 匹配在asset前面的内容 |
先行否定断言 | /want(?!asset)/ | want只有不在asset前面才匹配 |
后行断言 | /(?<=asset)want/ | 匹配在asset后面的内容 |
后行否定断言 | /(?<!asset)want/ | want只有不在asset后面才匹配 |
老实说,根据我的经验,后行断言的使用场景会更多,因为js 有很多的数据存储是名值对的形式保存,所以很多时候我们想要通过"name="来取到后面的值,这时候是后行断言的使用场景了
先行断言:只匹配 在/不在 百分号之前的数字
" alt="1}([^\"\"\' >]*)" data-src="/img/bVbnRTE?w=635&h=147" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
后行断言:
这里引例 @玉伯也叫射雕 的一篇 博文的内容
" alt="1}([^\"\"\' >]*)" data-src="/img/bVbnRTK?w=705&h=762" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
这里可以用后行断言
(?<=^|(第.+[章集])).*?(?=$|(第.+[章集]))
“后行断言”的实现,需要先匹配/(?<=y)x/
的x
,然后再回到左边,匹配y
的部分。这种“先右后左”的执行顺序,与所有其他正则操作相反,导致了一些不符合预期的行为。
首先,后行断言的组匹配,与正常情况下结果是不一样的。
/(?<=(d+)(d+))$/.exec('1053') // ["", "1", "053"]
/^(d+)(d+)$/.exec('1053') // ["1053", "105", "3"]
上面代码中,需要捕捉两个组匹配。没有“后行断言”时,第一个括号是贪婪模式,第二个括号只能捕获一个字符,所以结果是105
和3
。而“后行断言”时,由于执行顺序是从右到左,第二个括号是贪婪模式,第一个括号只能捕获一个字符,所以结果是1
和053
。
其次,“后行断言”的反斜杠引用,也与通常的顺序相反,必须放在对应的那个括号之前。
/(?<=(o)d1)r/.exec('hodor') // null
/(?<=1d(o))r/.exec('hodor') // ["r", "o"]
上面代码中,如果后行断言的反斜杠引用(1
)放在括号的后面,就不会得到匹配结果,必须放在前面才可以。因为后行断言是先从左到右扫描,发现匹配以后再回过头,从右到左完成反斜杠引用。
另外,需要提醒的是,断言部分是不计入返回结果的。
ES2018 引入了具名组匹配(Named Capture Groups),允许为每一个组匹配指定一个名字,既便于阅读代码,又便于引用。
" alt="1}([^\"\"\' >]*)" data-src="/img/bVbnRTP?w=1204&h=438" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
上面代码中,“具名组匹配”在圆括号内部,模式的头部添加“问号 + 尖括号 + 组名”(?<year>
),然后就可以在exec
方法返回结果的groups
属性上引用该组名。同时,数字序号(matchObj[1]
)依然有效。
具名组匹配等于为每一组匹配加上了 ID,便于描述匹配的目的。如果组的顺序变了,也不用改变匹配后的处理代码。
如果具名组没有匹配,那么对应的groups
对象属性会是undefined
。
具名组匹配 × 解构赋值
" alt="1}([^\"\"\' >]*)" data-src="/img/bVbnRTY?w=1320&h=474" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
具名组引用
如果要在正则表达式内部引用某个“具名组匹配”,可以使用k<组名>
的写法。
" alt="1}([^\"\"\' >]*)" data-src="/img/bVbnRTZ?w=484&h=165" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" style="cursor: pointer;">
我这里比较推荐一个正则可视化的网站:https://regexper.com/ 在上面贴上你的正则,会以图形化的形式展示出你的正则匹配规则,之后我们就可以大致上判断我们的正则是否符合预期(貌似需要科学上网)
如果想通过字符串来生成正则对象的话,有两种方式,一种是字面量方式,另一种是构造函数
构造函数:new Regexp('content', 'descriptor')
const input = '/123/g'
const regexp = eval(input)
密码的强度必须是包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间。
^(?=.*d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$
非全数字 全字母的 6-15位密码 先行否定断言
/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,15}$/
字符串仅能是中文。
^[u4e00-u9fa5]{0,}$
15位
^[1-9]d{7}((0d)|(1[0-2]))(([0|1|2]d)|3[0-1])d{3}$
18位
^[1-9]d{5}[1-9]d{3}((0d)|(1[0-2]))(([0|1|2]d)|3[0-1])d{3}([0-9]|X)$
“yyyy-mm-dd“ 格式的日期校验,已考虑平闰年。
^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$
下面的这个表达式可以筛选出一段文本中的URL。
^(f|ht){1}(tp|tps)://([\w-]+.)+[w-]+(/[w- ./?%&=]*)?
假若你想提取网页中所有图片信息,可以利用下面的表达式。
/<img [^>]*?src="(.*?)"[^>]*?>/g;
*[img][^>]*[src] *= *["']{0
脚本宝典总结
以上是脚本宝典为你收集整理的1}([^""' >]*)
全部内容,希望文章能够帮你解决1}([^""' >]*)所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。