jerryni / blog

:dog: :dog: :dog:
https://github.com/jerryni/blog/issues
11 stars 4 forks source link

正则表达式 一些笔记 RegExp #6

Open jerryni opened 8 years ago

jerryni commented 8 years ago

Table of Contents generated with DocToc

基础内容

这部分内容基本来自, 犀牛书第六版

在线测试网站: http://regexr.com/

基本概念:

[]包含的字符组成charater class, 匹配它所包含的任意字符 如[abc], a,b,c中的任意都匹配:

/[abc]/.test('a'); //true
/[abc]/.test('b'); //true
/[abc]/.test('c'); //true
/[abc]/.test('d'); //false

//^放在第一位是取反[^abc]除abc以外的任意字符
/[^abc]/.test('a'); //false
/[^abc]/.test('bde;'); //true

// -可以代表范围
/^[a-z]$/.test('x'); //true
/[a-zA-Z0-9]/ //匹配任意数字, 字母 

常用字符类:

字符 含义以及记忆方法
. 除换行符外
\w 单词 同 [0-9a-zA-Z] word
\s 空白 space
\d 数字 digit
[\b] 退格 这是特殊的一种

重复

基本符号是: {}

// /\d{2,3}/ 匹配2到3个的数字
/\d{2,3}/.test(1); // false
/\d{2,3}/.test(10); // true
/\d{2,3}/.test(100); // true
/\d{2,3}/.test(1000); // true
// /\d{2,}/ 2个以上的数字

// /\d?/ 0或者1个 
/a?/g.exec('a'); // ["a"]
/a?/g.exec(''); // [""] 这个也算匹配成功, 因为?可以标识0个匹配
/a?/g.exec('aa'); // ["a"]

// `*` 星号就是随便几个,相当于{0,}
// `+` 一个以上相当于{1, }

重复概念中的:贪婪模式与非贪婪模式

使用方法:

举例:

/a+/.exec('aaa'); // ['aaa']
/a+?/.exec('aaa'); // ['a']

// 虽然非贪婪模式是 尽可能少的匹配, 但是:

/a+b/.exec('aaab'); // ['aaab']
/a+?b/.exec('aaab'); // ['aaab']
// 两者结果相同, 因为正则的匹配原则是从匹配到的第一个元素开始计算;从`aaab`这个字符传第一位开始,aaab是匹配/a+?b/的

选择,分组,引用

选择符|: 或者; 循序 自左向右

/a|ab/g.exec('ab'); // ["a"] 而不是 ["ab"]

组合符()的作用:

合并多个独立单元变成一个子表达式,然后就可以用 '|','*','+','?'来处理;

/java(script)?/.test('javascript'); //true
/java(script)?/.test('java'); //true
/java(script)?/.test('jav'); //false

引用:子模式 在同一表达式中,引用前面的子表达式 用'\'后面跟数字来实现, \1第一个,\3第三个,数字是左括号的顺序;例子中\2代表的就是第二个做括号里的自引用(java)

/((java)+script)\sis\smore\sfun\sthan\s\2/.test('javascript is more fun than java'); //true
/((java)+script)\sis\smore\sfun\sthan\s\2/.test('javascript is more fun than jav'); //false

子模式中需要注意的一点:

对子表达式的引用, 指的是文本的引用,而不是模式;也就是说, 就是说, 各个引用部分是完全相同的字符;

var a = '"a\'';
/['"][^'"]['"]/.test(a); //true
// 为了能够匹配到前后的引号相同可以如此修改:
/(['"])[^'"]*\1/.test(a); // false 这是只能匹配'a',或者"a"
/(['"])[^'"]*/.test('"a\'[\'"]'); // true

用(?:)去掉引用,下面的例子就是如何创造组合,但又不包含引用达式; 解读:\2不再是java,而是fun

/((?:java)+script)\sis\smore\s(fun)\sthan\s\2/.test('javascript is more fun than fun'); // true
/((?:java)+script)\sis\smore\s(fun)\sthan\s\2/.test('javascript is more fun than java'); // false

指定匹配位置

//^: 匹配行开头
/^java/.test('javascript is more fun than java'); //true
/^java/.test('ok. javascript is more fun than java'); //false

//$: 匹配行结尾
/java$/.test('javascript is more fun than java'); //true
/java$/.test('ok. javascript is more fun than a'); //false
/java$/.test('ok. javascript is more fun than a'); //false

// \b单词边界; 
/\bhello\b/.test('hello world'); //true
/\bhello\b/.test('helloworld'); // false
// 注意: [\b]是匹配 js中的特殊字符, 退格符...
// \B与\b是相反的

// (?=p): 先行断言... 必须与p匹配; 如例子,这个位置一定要有`:`
/java(?=:)/.test('java:'); //true
/java(?=:)/.test('java'); //false
// (?!p) 于(?=p) 相反, 一定不能匹配

修饰符

i: 大小写 ignore case g: 全局 global m: 多行 multiline

string对象应用

search: 返回索引, 没有就返回-1; 无法进行全局搜索, 忽略g修饰符

'javascript'.search(/script/); // 4

//search中的参数如果不是正则, 那么会使用regex初始化为正则
'javascript'.search('script'); // 4

replace:

replace可以使用 $应用表达式;

var a = '"content"';
a = a.replace(/"([^"]*)"/g, "WRAP$1WRAP");
console.log(a);  //WRAPcontentWRAP

match:

正则包含g, 返回所有匹配的结果

'1 ,2, sdf, 5'.match(/\d+/g); //["1", "2", "5"]

不包含g, 第一个参数是完整的完整匹配串; 后面的是引用. 这个和Regex的exec是一样的;

var a = 'http://foo.com';
a.match(/(\w+)\:\/\/(.+)/); // ["http://foo.com", "http", "foo.com"]

split:

用正则, 指定分隔符;

分割符为: ,, 而且无论旁边有多少个空格;

var a = 'ssdfj, ssdfkj  , lkjsdf';
a.split(/\s*,\s*/); // ["ssdfj", "ssdfkj", "lkjsdf"]

RegExp

很大的用途就是, 可以动态的创建 正则表达式;

正则直接量中的 单斜杠'\', 一定要替换为双斜"\"

var reg = new RegExp('\\d{3}', 'g'); //第二个参数是修饰符
reg.test(123); //true
reg.test(12); //false

// 5个属性:
reg.source; //"\d{3}"
reg.global; //true
reg.multiline; //false
reg.ignoreCase; //false
reg.lastIndex; //0 这个待讲

// exec方法
var a = new RegExp('(\\w+)\\:\/\/(.*)', 'g'); // 是否有g都会这样, 这是和string.match的不同点
a.exec('http://sdf'); // ["http://sdf", "http", "sdf"]
a.lastIndex; // 10
a.exec(123); //null 没有匹配项
a.lastIndex; // 0

// lastIndex
var pattern = /java/g;
var str = 'javascript is more fan than java';
while (result = pattern.exec(str)) {
    /*
    result: ["java", index: 0, input: "javascript is more fan than java"]
    pattern.lastIndex: 4
    result: ["java", index: 28, input: "javascript is more fan than java"]
    pattern.lastIndex: 32
    */
    console.log('result:', result);
    console.log('pattern.lastIndex:', pattern.lastIndex);
}
pattern.lastIndex;  // 0

一个注意点

同一个正则,当对不同的字符串进行,exec, test操作时, 可能会存在 lastIndex不为0的情况,会有匹配错误的问题;所以在匹配不同的字符串之前最好进行一次重置:reg.lastIndex = 0;

实例搜集

replace的经典应用

http://www.bennadel.com/blog/2198-special-references-in-javascript-s-string-replace-method.htm

$&: 当前匹配的内容 &`: 当前匹配内容的左侧 &': 右侧

var a = 'abc'
a.replace(/[bc]/g, '\+$&') //a+b+c 把b替换成+b, 再把c替换成+c

千分价格分隔符

自左向右的字符匹配法。。。一定从第一个字符开始分析,类似于索引搜索。

function x(a) {
var reg = /\B(?=(\d{3})+(?!\d))/g;
return a.replace(reg, ',');
}

demo1

理解舍弃原则

也就是消耗过的字符或是消耗过的字符的替换字符,不再参与匹配工作.

' k k k '.replace(/ k /g,' '); // " k "

密码匹配的例子

密码:至少8字符,至少有一个数字,至少有一个小写和一个大写字母,至少包含一个比如@#%$^的字符,不包含空白

^(?=.\d)(?=.[a-z])(?=.[A-Z])(?=.[@#%$^])(?=\S+$).{8,}$

手机

/^1[0-9]{9}$/ 1开头,第二位是3,4,5,7,8,后面接9位数字

中文

[\u4E00-\u9FFF]

其他

var patterns = new Object();

//匹配ip地址
patterns.ip = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])(\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])){3}$/;

//匹配邮件地址
patterns.email = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/;

//匹配日期格式2008-01-31,但不匹配2008-13-00
patterns.date = /^\d{4}-(0?[1-9]|1[0-2])-(0?[1-9]|[1-2]\d|3[0-1])$/;

/*匹配时间格式00:15:39,但不匹配24:60:00,下面使用RegExp对象的构造方法
来创建RegExp对象实例,注意正则表达式模式文本中的“\”要写成“\\”*/
patterns.time = new RegExp("^([0-1]\\d|2[0-3]):[0-5]\\d:[0-5]\\d$");

/*verify – 校验一个字符串是否符合某种模式
 *str – 要进行校验的字符串
 *pat – 与patterns中的某个正则表达式模式对应的属性名称
 */
function verify(str, pat) {
    thePat = patterns[pat];
    if (thePat.test(str)) {
        return true;
    } else {
        return false;
    }
}