正则表达式学习

147 阅读1分钟

处理字符串匹配,通过指定表达式,得到符合该规则的数据

1. 基础概念

1.1 常见元字符

符号含义例子匹配结果
*匹配前面的字符出现0次或多次abaaaa,aaabbbb,bbbb
+匹配前面的字符至少1次a+b+aaab,abbbb,aabb
[]匹配括号里任意一个字符[A-Z]*APPLY,CAPITALES
()表达式编组(优先运行)(ab)aaabab,abaab,abaab
{m,n}匹配前面的字符包含m~n次a{2,3}b{2,3}aabbb,aaabbb,aaabb
[^]匹配任意一个不在中括号里的字符[^A-Z]*apple,low,test
或者b(aie)dbad,bid,bed
.匹配任意单个字符(包含符号、数字、空格)b.dbad,b d,b$d
指定字符串开始位置的字符^aapple,asdf,a
\转义字符(把有特殊含义的字符转换成字面形式).|\.\
$从字符串末端匹配[A-Z][a-z]$Ab,cc,AA
?!不包含^(?![A-Z].)*$ff,$ym,a3

1.2 字符串匹配常用方法

1.2.1 search()

检索与正则表达式相匹配的值,返回与正则表达式查找内容匹配的第一个子字符串的位置,如果返回-1表示没有匹配到

语法:stringObj.search(rgExp);

var str = "acbgdjgl";
alert(str.search("h"));      //-1

1.2.2 match()

使用正则表达式模式对字符串执行查找,并将包含查找的结果作为数组返回

语法:stringObj.match(rgExp);

var str='sdf e443 fedef 55 66gg 333322312 dff 99';
var re=/\d+/g;
alert(str.match(re)); // ['443', '55', '66', '333322312', '99']

1.2.3 replace()

返回根据正则表达式进行文字替换后的字符串的复制

语法:stringObj.replace(rgExp, replaceText);

var str='abacdAef';
// 全局匹配,不区分大小写,将a替换为T
console.log(str.replace(/a/gi, 'T'));// 'TbTcdTef'

var r, re;                      // 声明变量。
var ss = "The rain in Spain falls mainly in the plain.";
re = /(\S+)(\s+)(\S+)/g;        // 创建正则表达式模式。
r = ss.replace(re, "$3$2$1");   // 交换每一对单词。
// 'rain The Spain in mainly falls the in plain.'
/*
match	  匹配的子串
p1,p2.. 代表第 n 个括号匹配的字符串
offset  匹配到的子字符串在原字符串中的偏移量
string  被匹配的原字符串
*/
function replacer(match, p1, p2, p3, offset, string) {
	return [p1, p2, p3].join(' - ');
}
var newString = 'abc12345#$*%'.replace(/([^\d]*)(\d*)([^\w]*)/, replacer);
console.log(newString);  // abc - 12345 - #$*%

1.2.4 test()

检索字符串中指定的值,返回true或false,测试是否存在

语法:rgexp.test(str);

 var str = "abcedg";
 var reg = "/a/i";
// str字符串中是否包含a(不区分大小写)
alert(reg.test(str));     //true

1.2.5 exec()

用正则表达式模式在字符串中运行查找,并返回包含该查找结果的第一个值,匹配失败,返回null

语法:rgExp.exec(str)

var str="Hello world!";
//查找"Hello"
var patt=/Hello/g;
var result=patt.exec(str);
document.write("返回值: " +  result);  // Hello
//查找 "RUNOOB"
patt=/RUNOOB/g;
result=patt.exec(str);
document.write("<br>返回值: " +  result); // null

2. 字符匹配技巧

2.1 模糊匹配

2.1.1 横向模糊匹配

指一个正则可匹配的字符串的长度不固定

// 匹配规则:第一个字符为a,接下来是2~5个b,最后一个是c
var reg = /ab{2,5}c/g;
var string = "abc abbc abbbc abbbbc abbbbbc abbbbbbc";
// 获取所有满足规则的子串
console.log(string.match(regex)) // ["abbc", "abbbc", "abbbbc", "abbbbbc"]

2.1.2 纵向模糊匹配

一个正则匹配的字符串,具体到某一位字符时,它可以是不是某个确定的字符,可能有多个结果

var regex = /a[123]b/g;
var string = "a0b a1b a2b a3b a4b";
console.log( string.match(regex) ); //  ["a1b", "a2b", "a3b"]

2.2 字符组

[]在正则中表示字符组,表示在一个位置可能出现多种情况字符,但只匹配一个位置

可以理解成纵向匹配

2.2.1 范围表示法

使用连字符-表示范围

[123456abcdefGHIJKLM] 可以表示为 [1-6a-fG-M]

2.2.2 排除字符组

该位置的匹配字符不能是某字符字符

如:该位置不能是a,b,c任意一个字符 [^abc]

2.2.3 常见的字符组简写

字符组解释
\d表示任意数字 [0-9]
\D表示除数字以外的任意字符 [^0-9]
\w表示数字、大小写字母、下划线 [0-9a-zA-Z_]
\W表示非单词字符 [^0-9a-zA-Z_]
\s表示空白字符,包括空格、水平制表符、垂直制表符、换行符、回车符、换页符 [\t\v\n\r\f]
\S表示非空白字符 [^\t\v\n\r\f]
.表示通配符,换行符、回车符、行分隔符、段分隔符外 [^\n\r\u2028\u2029]

2.3 量词

可以理解成横向匹配,不确定字符长度

2.3.1 常见量词简写

量词解释
{m,}至少出现m次
{m}出现m次 {m,m}
?出现或不出现 {0,1}
+出现至少一次 {1,}
*出现任意次 {0,}

2.3.2 贪婪匹配与惰性匹配

贪婪模式:尽可能多的匹配 (默认)

惰性模式:尽可能少的匹配 (在匹配表达式后加?,即可开启惰性模式)

// 贪婪模式
var regex = /\d{2,5}/g;
var string = "123 1234 12345 123456";
// 默认为贪婪模式,尽可能取多的匹配
// 正则表达式匹配规则:匹配数字个数在2~5个满足要求的字串。
//在贪婪模式下,会在子串中,取满足要求的最多个数的字符
console.log( string.match(regex) );
// => ["123", "1234", "12345", "12345"]

// 惰性模式
var regex2 = /\d{2,5}?/g;
// 惰性模式下,正则表达式等价于 /\d{2}/g
 console.log( string.match(regex2) );
// => ["12", "12", "34", "12", "34", "12", "34", "56"]

2.4 多选分支

满足多个子模式(表示多个正则表达式)任选其一,类似于或运算 【默认为惰性匹配

var regex = /good|nice/g;
var string = "good idea, nice try.";
console.log( string.match(regex) );
// => ["good", "nice"]

// 多选分支默认为惰性匹配
var regex = /good|goodbye/g;
var string = "goodbye";
console.log( string.match(regex) );
// => ["good"]

2.5 案例分析

2.5.1 匹配16进制颜色值

#ffbbad
#Fc01DF
#FFF
#ffE

分析

  • 包含两种情况,6位的和3位的,可以使用多选分支
  • 16进制6位匹配可以使用 #[\da-f]{6}
  • 16进制3位匹配可以使用 #[\da-f{3}
  • 使用gi模式,全局匹配,忽略大小写
// 优化正则表达式 /#([\da-f]{3}){1,2}/gi
// 表示3位的情况可能出现1次或两次,即对应的3位或6位
var regex = /#([\da-f]{6}|[\da-f]{3})/gi;
var string = "#ffbbad #Fc01DF #FFF #ffE";
console.log( string.match(regex) );
// => ["#ffbbad", "#Fc01DF", "#FFF", "#ffE"]

2.5.2 匹配时间

23:59
02:07

分析

  1. 需要对4位数进行匹配,分两个分支,分别是第一位数为2|[01]
  2. 当第一位为[01],第二位可以是\d
  3. 当第一位为2,第二位可以是[0-3]
  4. 第三位的范围是[0-5]时,第四位为\d
var regex = /^([01]\d|2[0-3]):([0-5]\d)$/
// 若要考虑前面没0的情况,需要优化正则表达式
// /^(0?\d|1\d|[2][0-3]):(0?\d|[1-5]\d)$/
console.log( regex.test("23:59") );
console.log( regex.test("02:07") );

2.5.3 window文件路径匹配

F:\study\javascript\regex\regular expression.pdf
F:\study\javascript\regex\
F:\study\javascript
F:\

分析:

  1. 盘符匹配,[a-zA-Z]:\
  2. 文件夹匹配,不能包含特殊字符 [^\:*<>|"?\r\n/]
var regex = /^[a-zA-Z]:\([^\:*<>|"?\r\n/]+\)*([^\:*<>|"?\r\n/]+)?$/;
console.log( regex.test("F:\study\javascript\regex\regular expression.pdf") );
console.log( regex.test("F:\study\javascript\regex\") );
console.log( regex.test("F:\study\javascript") );
console.log( regex.test("F:\") );
// => true
// => true
// => true
// => true

2.5.4 匹配id

<div id="container" class="main"></div>

分析:

var regex = /id="[^"]*"/
var str = '<div id="container" class="main"></div>';
console.log(str.match(regex)[0]);
// => id="container"

3. 位置匹配方法

3.1 位置匹配

3.1.1 ^和$

^匹配行开头

$匹配行结尾

// m修饰符表示多行匹配
var result = "I\nlove\njs".replace(^|$/gm,'#');
console.log(result)
/*
#I#
#love#
#javascript#
*/

3.1.2 \b 和 \B

\b 是单词边界,具体是\w 与\W之间的位置,也包括\w与^之间的位置,和\w与$之间的位置

var result = "[JS] Lesson_01.mp4".replace(/\b/g, '#');
console.log(result);
// => "[#JS#] #Lesson_01#.#mp4#"

var result = "[JS] Lesson_01.mp4".replace(/\B/g, '#');
console.log(result);
// => "#[J#S]# L#e#s#s#o#n#_#0#1.m#p#4"

3.1.3 (?=p)和(?!p)

(?=p),其中p是一个子模式,即p前面的位置,或者说,该位置后面的字符要匹配p

// 正则表达式,表示l前面添加#
var result = 'hello'.replace(/(?=l)/g, '#');
console.log(result);
// => 'he#l#lo'

// 正则表达式,表示非l前面加#
var result = 'hello'.replace(/(?!l)/g, '#');
console.log(result);
// => '#h#ell#o#'

3.3 案例分析

3.3.1 数字的千位分隔符表示法

把'12345678'变成'12,345,678'

分析

  1. 弄出最后一个逗号
// 最后一个逗号位置在从后往前数最后的第三个位置,用正则表示为/(?=\d{3}$)/g
var result = '12345678'.replace(/(?=\d{3}$)/g,',')
console.log(result) // '12345,678'
  1. 弄出所有的逗号
// 因为逗号出现的位置,要求后面3个数字一组,即\d{3}至少出现一次,可以用量词+
var result = '12345678'.replace(/(?=(\d{3})+$)/g,',')
console.log(result); // // => ",123,456,789"

// 处理该位置为非开头位置
var regex = /(?!^)(?=(\d{3})+$)/g
  1. 【扩展】支持其它形式
var string = "12345678 123456789";
// 此时需要修改正则,^和$改为\b,表示边界位置
var regex = /(?!\b)(?=(\d{3})+\b)/g;
//  (?!\b) 即为\B,即简化正则为 /\B(?=(\d{3})+\b)/g
var result = string.replace(regex, '');
console.log(result); //  => "12,345,678 123,456,789"
  1. 【扩展】货币格式化

将 1888 格式化为 $ 1888.00

function format(num) {
  return num.toFixed(2).replace(/\B(?=(\d{3}+\b))/g, ',').replace(/^/, '$$ ');
}
console.log(format(1888)) // => "$ 1,888.00"

3.3.2 验证密码问题

密码长度为6-12位,由数字、小写字符、大写字符组成,但必须至少包含2种字符

分析

  1. 简化,"必须至少包括2种字符"
var regex = /^[0-9a-zA-Z]{6,12}$/;
  1. 判断是否包含某一种字符
// 必须包含数字 (?=.*[0-9])
var regex = /(?=.*\d)^[\dA-Za-z]{6,12}$/;
  1. 同时包含两种字符
// 同时包含数字和字母 (?=.*\d)(?=.*[a-z])
var regex = /(?=.*\d)(?=.*[a-z])^[\dA-Za-z]{6,12}$/;
  1. 采用多条件
    1. 同时包含数字和小写
    2. 同时包含数字和大写
    3. 同时包含小写和大写
var regex = /((?=.*[0-9])(?=.*[a-z])|(?=.*[0-9])(?=.*[A-Z])|(?=.*[a-z])(?=.*[A- Z]))^[0-9A-Za-z]{6,12}$/;

console.log( regex.test("1234567") ); // false 全是数字
console.log( regex.test("abcdef") ); // false 全是小写字母
console.log( regex.test("ABCDEFGH") ); // false 全是大写字母 
console.log( regex.test("ab23C") ); // false 不足6位 
console.log( regex.test("ABCDEF234") ); // true 大写字母和数字 
console.log( regex.test("abcdEF234") ); // true 三者都有
  1. 第二种解法

至少包含两种字符 = 不能全部是数字 + 不能全是小写字母 + 不能全是大写字母

// 不能全是数字 (?!p)
var regex = /(?!^\d{6,12}$)^[\dA-Za-z]{6,12}$/;

// 三个都不能
var regex = /(?!^\d{6,12}$)(?!^[a-z]{6,12}$)(?!^[A-Z]{6,12}$)^\d[a-zA-Z]{6,12}$/;

console.log( regex.test("1234567") ); // false 全是数字
console.log( regex.test("abcdef") ); // false 全是小写字母
console.log( regex.test("ABCDEFGH") ); // false 全是大写字母
console.log( regex.test("ab23C") ); // false 不足6位 
console.log( regex.test("ABCDEF234") ); // true 大写字母和数字 
console.log( regex.test("abcdEF234") ); // true 三者都有

4. 括号的作用(待更新)