js 正则表达式

257 阅读4分钟

javaScript中的正则表达式定义

正则表达式是一个描述字符模式的对象。 在javascript中,正则表达式用RegExp对象表示,可以通过RegExp()构造函数来创建,大多数情况下,我们使用字面量语法来创建,即定义为包含在一对斜杠(//)之间的字符

let pattern = /s$/

let pattern = new RegExp("s")

正则表达式的方法

  • exec:在字符串中执行查找匹配的RegExp方法,它返回一个数组(未匹配到则返回 null)

    let pattern = /java/i
    
    pattern.exec('javaScript is a great language')
    
  • test: 在字符串中测试是否匹配的RegExp方法,它返回 true 或 false

    let pattern = /java/i
    
    pattern.test('javaScript is a great language')
    

js中用于模式匹配的String方法

  • search: 返回匹配到的位置索引,或者在失败时返回-1
'JavaScript'.search(/java/i)
  • replace: 在字符串中执行查找匹配的String方法,并且使用替换字符串替换掉匹配到的子字符串

    'javaScript is a great language'.replace(/great/i, 'the best')
    
  • match:在字符串中执行查找匹配的String方法,它返回一个数组,在未匹配到时会返回 null.

    'javaScript is a great language'.match(/a/i)
    
    '1 plus 3 equals 4'.match(/\d+/g)
    
  • split:使用正则表达式或者一个固定字符串分隔一个字符串,并将分隔后的子字符串存储到数组中的 String 方法.

    '123456789'.split('')
    '123,456,789'.split(',')
    

直接量字符

在正则表达式中,所有的字母和数字都是按照字面含义进行匹配的。如果需要匹配特殊字符,需要通过反斜线(\)进行转义。

字符匹配
字母和数字自身
\t制表符
\n换行符
\r回车符
\f换页符

特殊字符: ^ $ . * + ? = ! : | \ / () [] {}

字符类

将字符放进方括号内就组成了字符类

字符匹配
[...]括号内的任意字符
[^...]不再括号内的任意字符
.除换行符以外的任意字符
\w字母或数字或下划线或汉字,相当于[a-zA-Z0-9]
\W非\w
\s任意的空白符
\S非\s
\d数字
\D非\D
/[hello]/g.test('hello world')
/./g.test('hello')
/\w/.test('hello')
/\W/.test('hello')
/\s/.test(' ')
/\d/.test('9')

重复

描述某元素重复出现的次数

字符含义
{n,m}重复n到m次
{n,}重复n次或更多次
{n}重复n次
?重复零次或一次
+重复一次或更多次
*重复零次或更多次
/a{1,3}/.test('cn')
/a{1,3}/.test('can')
/a{1,}/.test('caaan')
/a{2}/.test('caan')
/a?/.test('caan')
/a+/.test('cn')
/a*/.test('cn')

非贪婪的重复

在上表中,匹配的重复字符是按照尽可能多的匹配, 我们称为**‘贪婪的’匹配**。 如果要进行非贪婪的匹配,我们只需在待匹配的字符后面跟上一个?号即可。'??', '+?', '*?'或者'{1,5}?'

注意: 使用非贪婪匹配,得到的结果可能和预期的不一致

'aabab'.match(/a.*?b/g)
'abaab'.match(/a.*?b/g)
// 非贪婪匹配的效果
'aaa'.match(/a+?/g)
'aaa'.match(/a+/g)

// 不符合预期的结果
'aaab'.match(/a+?b/g)
'aaab'.match(/a+b/g)
// 因为正则表达式的匹配模式总是会寻找字符串中第一个可能匹配的位置,由于该匹配是从字符串的第一个字符开始的,不考虑更短的匹配。(最先开始的匹配拥有最高的优先权)

选择、分组和引用

  1. 字符**"|"**用于分隔供选择的字符。要注意条件的先后顺序,在匹配条件时,将会从左到右地测试每个条件,如果满足了某个条件的话,就不会去再管其它的条件了
'abc'.match(/ab|bc/g)
'bca'.match(/ab|bc/g)
  1. 正则表达式中圆括号()可以把单独的项组成子表达式(称为分组),以便于可以对分组中的项进行单独处理。

    /(ab|cd)+|ef/ : 可以匹配ef,也可以匹配ab或者cd一次或者多次
    
  2. 使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。通过在字符"\"后面组号来实现。

    /\b(\w+)\b\s+\1\b/  : 可以用来匹配重复的单词
    
    'go go'.match(/\b(\w+)\b\s+\1\b/g)
    

指定匹配位置

字符含义
^匹配字符串的开始
$匹配字符串的结束
\b匹配单词的开始或结束
/^hello$/.test('hello')

修饰符

字符含义
i不区分大小写
g全局匹配,即在找到第一个后,会接着往下找,知道匹配全部
'JavaScript'.match(/^java/i)
'JavaScript java hello'.match(/java/ig)

零宽断言(功能为指定匹配位置)

任意正则表达式都可以作为锚点条件。类似于 ^, $, \b这样的用于指定位置的。 形式为**(?=Exp)的表达式称为零宽先行断言**,作用为断言自身出现的位置的后面能匹配表达式exp, 即自身的后面能够正确匹配表达式exp

"I'm singing while you're dancing".match(/\b\w+(?=ing\b)/g) // 匹配以ing结尾的单词的前面部分(除了ing以外的部分)

(?<=exp)零宽度正回顾后发断言,与先行断言不同,用以断言自身前面的内容能正确匹配表达式exp

"reading a book".match(/(?<=\bre)\w+\b/g) //匹配以re开头的单词的后半部分(除了re以外的部分)

(?!exp)称为零宽度负向先行断言,要求自身后面的字符不能与表达式exp匹配

'333test'.match(/\d{3}(?!\d)/g) //匹配三位数字,而且这三位数字的后面不能是数字

实例:

// 匹配连续出现的四组以 . 连接的字符串
// abc.dss.sds.wer

'abc.dss.sds.wer'.match(/^(.{3}\.){3}.{3}$/g)

// var s1 = "get-element-by-id"; 给定这样一个连字符串,写一个function转换为驼峰命名法形式的字符串 getElementById

var f = function(s) {
    return s.replace(/-\w/g, function(x) {
        return x.slice(1).toUpperCase();
    })
}

//判断字符串是否包含数字
function containsNumber(str) {
    var regx = /\d/;
    return regx.test(str);
}

// 数字格式化问题,1234567890 --> 1,234,567,890
let test1 = '1234567890'
let format = test1.replace(/\B(?=(\d{3})+(?!\d))/g, ',')

console.log(format) // 1,234,567,890

下面简单分析下正则/\B(?=(\d{3})+(?!\d))/g:
1. /\B(?=(\d{3})+(?!\d))/g:正则匹配边界\B(非单词),边界后面必须跟着(\d{3})+(?!\d);
2. (\d{3})+:必须是1个或多个的3个连续数字;
3. (?!\d):第2步中的3个数字不允许后面跟着数字;
4. (\d{3})+(?!\d):所以匹配的边界后面必须跟着3*n(n>=1)的数字。
最终把匹配到的所有边界换成,即可达成目标。

// 去掉字符串左右两边的空格," jaw il " --> “jaw il”
function trim(str) {
    return str.replace(/(^\s*)|(\s*$)/g, "")
}
let str = "  jaw il "
console.log(trim(str)) // "jaw il"

参考资料

mdn js正则介绍

deerchao.cn/tutorials/r…

《JavaScript权威指南》