Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JS 正则 Ninja 笔记 #72

Closed
PolluxLee opened this issue Jul 24, 2018 · 0 comments
Closed

JS 正则 Ninja 笔记 #72

PolluxLee opened this issue Jul 24, 2018 · 0 comments

Comments

@PolluxLee
Copy link
Owner

PolluxLee commented Jul 24, 2018

# 标志

  • i:不区分大小写
  • g:匹配模式中的所有实例
  • m:允许匹配多行,比如(textarea)

这些标志附加到字面量尾部(例如,/test/ig)或者作为 RegExp 构造器的第二个参数(new RegExp("test", "ig"))

# 精确匹配

在一个 /test/ 的表达式中,一个接着一个的字符,t 后面跟着 e,e 后面跟着 s,s 后面跟着 t,表示这些字符必须在一个字符串中出现,才能匹配该模式

# 重复出现

  • ?:定义一个字符出现 0 次或 1 次,如,/t?est/ 匹配 test 和 est
  • +:定义一个字符出现 1 次或多次,如,/t+est/ 匹配 test、ttttest 或 ttest 等等
  • *:定义一个字符出现 0 次或多次,如,/t*est/ 匹配 est、test 或 ttttest 等等
  • {n}:定义一个字符出现的次数,如,/a{4}/ 匹配 aaaa
  • {m, n}:定义一个字符出现次数的区间,如,/a{4, 10}/ 匹配 4 ~ 10 个 a
  • {m, }:定义一个字符出现次数的开区间,如,/a{4, }/ 匹配 4 个或以上的 a

# 匹配一类字符

  • [a-z]:匹配字母表中 a 到 z 的任意一个字符
  • [0-9]:匹配 0 到 9 中任意一个数字
  • [^a-z]:匹配 a 到 z 之外的任意一个字符

# 其他

  • (x):用于捕获的括号,即,匹配并记住 x
  • (?:x):非捕获的括号,即,匹配但不记住 x
  • x(?=y):正向肯定查找,匹配 x 仅仅当 x 后面跟着 y
  • x(?!y):正向否定查找,匹配 x 仅仅当 x 后面不跟着 y

# 预定义字符类

预定义术语 匹配内容
\b 匹配单词边界,\w 之外的字符可分割单词
\n 换行符
. 匹配除了 \n 之外的任意字符
\d 等价于 [0-9]
\w 等价于 [A-Za-z0-9_]
\s 匹配任何空白字符,包括空格,制表符,换页符
\D 等价于 [^0-9]
\W 等价于 [^A-Za-z0-9_]
\S 匹配任何非空白字符
\B 匹配非单词边界

# 匹配开始与结束

  • ^:表示要从字符串的开头进行匹配,如 /^test/
  • $:表示模式必须出现在字符串结尾,如 /test$/

同时使用时表明指定的模式必须包含整个候选字符串:/^test$/

# 或操作符

|:表示或关系,如 /a|b/ 匹配 a 或 b 字符

# 转义

如果我们需要匹配 [、$、^ 或其他这样的特殊字符,就需要使用反斜杠对他们进行转义,让被转义字符作为字符本身进行匹配,如,要匹配 “\” 字符,就需要 “\\” 进行转义

# 分组

模式中添加小括号之后,可匹配字符串,如,/(ab)+/ 匹配一个或多个字符串 ab

当正则表达式有一部分是用括号进行分组时,它具有双重责任,同时也创建了捕获(capture)

# 反向引用

反向引用就是将 捕获 作为正则表达式中能够成功匹配术语时的候选字符串的一种操作

如,/^([dtn])a\1/ 可以匹配任意一个以 d, t 或 n 开头,且后面跟着一个 a 字符,并且再后面跟着的是和第一个捕获相同字符的字符串。它和 /[dtn]a[dtn]/ 不一样,a 后面的字符有可能不是 d, t 或 n 中的一个字符,但这个字符肯定是以触发该匹配的其中一个字符开头。因此,\1 匹配的字符需要在执行的时候才能确定

如,/<(\w+)>(.+)</\1>/ 可匹配,whatever,可像这样的元素,不使用反向引用,是无法做到的,因为我们无法知道关闭标签与开始标签是否匹配

# 捕获

String.prototype.match 方法

mdn
参数: 正则表达式对象,若未提供参数,则返回空字符串数组 [""]
返回值: 返回一个数组,数组的第一项是进行匹配完整的字符串,之后的项是用圆括号捕获的结果。如果整个没有匹配到,返回 null

RegExp.prototype.exec 方法

mdn
参数: 要匹配正则表达式的字符串
返回值: 如果匹配成功,exec() 方法返回一个数组,并更新正则表达式对象的属性。返回的数组将完全匹配成功的文本作为第一项,将正则括号里匹配成功的作为数组填充到后面
如果匹配失败,exec() 方法返回 null

使用 match() 进行局部搜索

var re = /<(\/?)(\w+)([^>]*?)>/
var str = "<div class='test'><b>Hello</b><i>world</i></div>"
str.match(re)  
//  ["<div class='test'>", "", "div", " class='test'"]

使用 match() 进行全局搜索

match 在全局搜索的时候拿不到捕获,只会返回匹配的结果

var re = /<(\/?)(\w+)([^>]*?)>/g
var str = "<div class='test'><b>Hello</b><i>world</i></div>"
str.match(re)
//  ["<div class='test'>", "<b>", "</b>", "<i>", "</i>", "</div>"]

使用 exec() 进行全局搜索

可以循环执行 exec() 进行全局搜索并获得捕获,因为每次执行都会更新下次匹配开始的位置,即,lastIndex

var re = /<(\/?)(\w+)([^>]*?)>/g
var str = "<div class='test'><b>Hello</b><i>world</i></div>"
var match
while((match = re.exec(str)) !== null) {
  console.log(match)
}

// ["<div class='test'>", "", "div", " class='test'"]
// ["<b>", "", "b", ""]
// ["</b>", "/", "b", ""]
// ["<i>", "", "i", ""]
// ["</i>", "/", "i", ""]
// ["</div>", "/", "div", ""]

需要注意的有几点:
1. 正则表达式最后要加上全局标志 g,否则每次匹配成功将导致死循环
2. exec 是正则的方法,由正则对象去调用
3. 在 while 循环里不能使用正则字面量,因为需要每次更新 lastIndex

反向引用的使用

使用 \1 引用了表达式中的第一个捕获

var str = "<b class='hello'>Hello <i>world</i></b> "
var re = /<(\w+)([^>]*)>(.*)<\/\1>/
str.match(re)
// ["<b class='hello'>Hello <i>world</i></b>", "b", " class='hello'", "Hello <i>world</i>"]

非捕获分组

我们并不希望所有的分组都具有捕获功能

为了阻止不必要的捕获,正则表达式的语法允许我们在开始括号后加一个 ?: 标记,如,(?:abc)

# String.prototype.replace 方法

将中横线字符转化成驼峰拼写法

1. replace 的第二个参数可以传入字符串,也可传入回调函数
2. 回调函数的第一个参数是匹配成功的完整字符串,第二个参数是第一个捕获结果

"border-bottom-width".replace(/-(\w)/g, (all, letter) => {
  return letter.toUpperCase()
})
//  "borderBottomWidth"

压缩查询字符串工具函数

var str = "foo=1&foo=2&blah=a&blah=b&foo=3"
// 我们想要得到这样的结果 "foo=1,2,3&blah=a,b"

function compress (source) {
  var keys = {}
  source.replace(/([^=&]+)=([^=&]*)/g, (all, key, value) => {
    keys[key] = (keys[key] ? keys[key]+',' : "") + value
    return ""
  })
  var result = []
  for (var key in keys) {
    result.push(`${key}=${keys[key]}`)
  }
  return result.join('&')
}

console.log(compress(str))  //  "foo=1,2,3&blah=a,b"

# 相关方法

RegExp.prototype.test()

test() 方法执行一个检索,用来查看正则表达式与指定的字符串是否匹配。返回 true 或 false

和 exec() (或者组合使用),一样,在相同的全局正则表达式实例上多次调用 test 将会越过之前的匹配

let str = 'hello world!';
let result = /^hello/.test(str);
console.log(result); 
// true

String.prototype.search()

search() 返回正则表达式在字符串中首次匹配项的索引,找不到返回 -1

function testinput(re, str){
  var midstring;
  if (str.search(re) != -1){
    midstring = " contains ";
  } else {
    midstring = " does not contain ";
  }
  console.log (str + midstring + re);
}

# 参考

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp

JavaScript Ninja

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant