Javascript 正则03-正则表达式括号的作用

正则表达式

正则表表达式是匹配模式, 要么匹配字符,要么匹配位置

正则表达式括号的作用

字符串中的正则和正则对象还是不太一样,需要注意一哈

需要注意: 验证正则和替换正则有可能范围不一样,具体需要看需求

括号提供了分组,以便于我们引用它

主要内容

  1. 分组和分支结构
  2. 捕获分组
  3. 反向引用
  4. 非捕获分组
  5. 相关案例

1. 分组和分支结构

这二者是括号最直接的作用也是最原始的功能

1.1 分组

/a+/ 表示连续出现 a ,而需要连续出现 ab,则需要 /(ab)+/


var reg = /(ab)+/g;
var string = 'ababa abbb ababab';
console.log(string.match(reg));

//  ["abab", "ab", "ababab"]

1.2 分支结构

在多选分支结构(p1|p2)中,括号的作用是提供了 子表达式的所有可能

比如匹配: i love javscript 和 i love regular expression


var reg = /^i love (javascript|regular expression)$/

console.log(reg.test('i love javascript'));
console.log(reg.test('i love regular expression'));

// true
// true

显然去掉了括号,就不是想要的结果了

2. 引用分组

一般情况下分组的作用是为了引用或者重复出现分组或者分支结构

在引用分组中,使用括号,我们可以进行数据提取,记忆更强大的替换操作

以日期为例, 假设格式为: yyyy-mm-dd,先实现一个简单正则 /\d{4}-\d{2}-\d{2}/

加上括号:/(\d{4})-(\d{2})-(\d{2})/

2.1 提取数据

提取年月日

  1. 使用字符串 match 方法

var reg = /(\d{4})-(\d{2})-(\d{2})/;
var date = '2017-06-07';
console.log(date.match(reg));

// ["2017-06-07", "2017", "06", "07", index: 0, input: "2017-06-07", groups: undefined]

var reg = /(\d{4})-(\d{2})-(\d{2})/g;
var date = '2017-06-07';
console.log(date.match(reg));

// VM372:4 ["2017-06-07"]

match 返回一个数组,没有匹配到返回 null, 第一个元素是匹配的整体结果,然后是各个分组的匹配子串,匹配下标,输入文本(注意 match 是否有 修饰符 g 数组格式不同)

  1. 使用 正则对象的 exec 方法
var reg = /(\d{4})-(\d{2})-(\d{2})/;
var date = '2017-06-07';
console.log(reg.exec(date));

// VM375:3 (4) ["2017-06-07", "2017", "06", "07", index: 0, input: "2017-06-07", groups: undefined]

exec 和 match 方法结果差不多,exec 没有 修饰符 g 限制

  1. 使用 构造函数的全局属性 1 9 获取
var reg = /(\d{4})-(\d{2})-(\d{2})/;
var date = '2017-06-07';
reg.test(date);// 正则操作即可
// reg.exec(date);
// date.match(reg);

console.log(RegExp.$1);
console.log(RegExp.$2);
console.log(RegExp.$3);

2.2 替换

比如,想把 yyyy-mm-dd 替换成 mm/dd/yyyy格式

var reg = /(\d{4})-(\d{2})-(\d{2})/;
var date = '2017-06-07';
date.replace(reg,'$3/$2/$1');

// "07/06/2017"

在replace中,第二个参数里 1 , 2, $3 表示相应的分组

等价于如下形式:

var reg = /(\d{4})-(\d{2})-(\d{2})/;
var date = '2017-06-07';
date.replace(reg,function () {
    return RegExp.$3 + '/' + RegExp.$2 + '/' + RegExp.$1;
});

// "07/06/2017"

也等价于:

var reg = /(\d{4})-(\d{2})-(\d{2})/;
var date = '2017-06-07';
date.replace(reg,function (match, year,month,day) {
    console.log(match); // '2017-06-07' input 文本内容
    return day + '/' + month + '/' + year;
});

// "07/06/2017"

3. 反向引用

反向引用概念只属于正则里,字符串中可以称作 api 分组引用

除了使用相应 API 来引用分组,也可以在正则本身里引用分组。但只能引用之前出现过的分组,即反向引用

以日期为例

比如: 洗衣歌正则支持如下格式:

2016-06-12
2016/06/12
2016.06.12

var reg = /\d{4}(-|\/|\.)\d{2}(-|\/|\.)\d{2}/
var string1 = '2016-06-12'
var string2 = '2016/06/12'
var string3 = '2016.06.12'
console.log(reg.test(string1)); // true 
console.log(reg.test(string2)); // true
console.log(reg.test(string3)); // true

假设我们需要分隔符前后一致, 此时需要使用反向引用

var reg = /\d{4}(-|\/|\.)\d{2}\1\d{2}/
var string1 = '2016-06-12'
var string2 = '2016/06/12'
var string3 = '2016.06.12'

console.log(reg.test(string1)); // true 
console.log(reg.test(string2)); // true
console.log(reg.test(string3)); // true

注意里面的 \1 , 表示的引用之前的那个分组(-|\/.), 只能引用前面的分组

3.1 括号套欠怎么办

以左括号(开括号)为准。比如:


var regexp = /^((\d)(\d(\d)))\1\2\3\4$/;
var string = '1231231233';
console.log(regexp.test(string)); // true
console.log(RegExp.$1); // VM118:5 123
console.log(RegExp.$2); // VM118:6 1
console.log(RegExp.$3); // VM118:7 23
console.log(RegExp.$4); // VM118:8 3

分析这个正则匹配模式:

  1. 第一个数字,第二个数字,第三个数字: 1,2,3
  2. \1 是第一个分组内容, 那么看第一个开括号(左括号)对应的分组是什么 123
  3. 接下来是 \2 , 那么找到第二个开括号,对应分组匹配内容是 1
  4. \3, 找到第三个开括号, 对应分组匹配内容是 23
  5. 最后是 \4, 找到 第四个开括号,对应分组的匹配内容是 3.

Note: 括号套欠(分组套欠) 只需要找开括号(左括号)即可找到对应的分组

3.2 \10 表示什么?

\10: 正则表达式中 表示 第10个分组,比较罕见而已


var reg = /(1)(2)(3)(4)(5)(6)(7)(8)(9)(#) \10+/;
var string = '123456789# #####';
console.log(reg.test(string)); // true

3.3 引用不存在的分组会怎么样?

不存在就匹配对应的转义字符

反向引用是引用前面的分组,如果引用的分组不存在,此时正则不会报错,只是匹配反向引用的字符本身。如: \2 ,就匹配 “\2” ,匹配 2 的转义字符

var reg = /\1\2\3\4\5\6\7\8\9/;
var string = '\1\2\3\4\5\6\7\8\9';
console.log(reg.test(string));
console.log(string.split(''));

4. 非捕获分组

之前文中出现的分组,都会捕获它们匹配到的数据,以便后续引用,因此称为捕获型分组

如果只想要括号的原始功能,不进行捕获。即,既不在 API中进行引用,也不在 正则中反向引用,此时可以使用 非捕获分组 (?:p)

这个应该是 在分支结构分组 和 分组多次出现的时候会常用到 非捕获分组

var reg = /(?:ab)+/g;
var string = 'ababa abbb ababab';
console.log(string.match(reg)); // ["abab", "ab", "ababab"]

需要注意的是: 正则修饰符 g 对 match 捕获型分组和非捕获型分组都是有影响的

5. 相关案例

5.1 字符串 trim 方法模拟

trim 方法去掉字符串开头和结尾的空白符号


var reg = /^\s+|\s+$/g;
var string = '  123213 123123 dsfsdf   \r\n';
console.log('origin--', string.length);
console.log('trimmed--', string.replace(reg,'').length);

两种实现思路:

  1. 匹配开头和结尾的空白符, 然后替换成空格

function trim (str) {
    return str.replace(/^\s+|\s+$/g, '');
}
console.log(trim('  foobar  '))
  1. 匹配整个字符串, 然后用引用来提取出相应的数据

function trim(str) {
    return str.replace(/^\s*(.*?)\s*$/g, '$1');
}

console.log(trim('  foobar  '))

这里使用了惰性匹配 *? , 不然会匹配最后一个空格之前的所有空格,当然前者效率高。应该说 模糊的越多,效率越低,尤其使用 通配符 .

5.2 将每个单词首字母大写

‘hello world’

var string = 'hello world';
var reg = /\b\w/g;
console.log(string.replace(reg, function (match) {
    // console.log('match', match);
    return match.toUpperCase();
}));

another complement

思路: 找到每个单词首字母,然后toUpperCase


function titleize (str) {
    return str.toLowerCase().replace(/(?:^|\s)\w/g,function (c) {
        return c.toUpperCase();
    })
}
console.log(titleize('my name is epeli'))

5.3 驼峰化

pascal case: 每个单词首字母大写其余字母小写

‘hello-world-lala’

// 分割边界处的 字母大写
function toPascalCase(str) {
    return str.toLowerCase().replace(/\b\w/g,function (match,$1) {
        return match.toUpperCase();
    }).split('-').join('');
}
console.log(toPascalCase('hello-world-lala'));

another complement

这个方法非常好,不仅可以 pascal camel 还可以有 trim 效果

// 真简洁
function camelize (str) {
    return str.replace(/[-_\s]+(.)?/g, function (match, c) {
        return c ? c.toUpperCase() : '';
    })
}

console.log(camelize('-moz-transform')); //MozTransform

第一个方法使用的是位置操作,第二个方法使用的是 实体字符,所以第二个方法可以直接替换掉分隔符。

5.4 中划线

pascal case to 中划线格式


// 定位到大写字母位置
// 替换
function toStrike (str) {
    return str.replace(/[A-Z]/g, function (match) {
        return '-' + match.toLowerCase();
    })
}

console.log(toStrike('MozTransform'));
function dashrize (str) {
    return str
    // 将 大写字母前面添加 -
    .replace(/([A-Z])/g, '-$1')
    // 将 空白字符替换成 中划线
    .replace(/[-_\s]+/g,'-')
    // 全变成小写
    .toLowerCase()
}
console.log(dashrize('MozTransform'));

5.5 html 转义和 反转义

// ¢£hello¥€©®<>
function escapeHTML (str) {
    var escapeChars = {
        '¢': 'cent',
        '£': 'pound',
        '¥': 'yen',
        '€': 'euro',
        '©': 'copy',
        '®': 'reg',
        '<': 'lt',
        '>': 'gt',
        '"': 'quot',
        '\'': '#39'
    };
    return str.replace(new RegExp('['+ Object.keys(escapeChars).join('')+']', 'g'), function (match) {
        return '&' + escapeChars[match] + ';';
    } );
}
console.log(escapeHTML('<div>Blah blah blah</div>')); // &lt;div&gt;Blah blah blah&lt;/div&gt;

它的逆过程如下:

// 实体字符转换成等值的 html
function unescapeHTML (str) {
    var htmlEntities = {
        'cent': '¢',
        'pound': '£',
        'yen': '¥',
        'euro': '€',
        'copy': '©',
        'reg': '®',
        'lt': '<',
        'gt': '>',
        'quot': '"',
        'apos': '\'' 
    };
    return str.replace(/\&([^;]+);/g, function (match, key) {
        if (key in htmlEntities) {
            return htmlEntities[key];
        }
        return match;
    });
}
console.log(unescapeHTML('&lt;div&gt;Blah blah blah&lt;/div&gt;')); //<div>Blah blah blah</div>

5.6 匹配成对标签

要求匹配:<div>regular expression</div><p>hello javascript</p>

var reg = /<([^>]+)>[\d\D]*<\/\1>/;
var string1 = '<div>regular expression</div>'
var string2 = '<p>hello javascript</p>'
var string3 = '<title>wrong!</div>'
console.log(reg.test(string1)); // true
console.log(reg.test(string2)); // VM338:6 true
console.log(reg.test(string3)); // VM338:7 false

其中 使用了 反向引用,为了前后保持一致;[\d\D] 匹配任意字符

猜你喜欢

转载自blog.csdn.net/palmer_kai/article/details/80196569
今日推荐