正则表达式之必知必会小记

153 阅读4分钟

正则表达式是用来搜索和替换的字符串

匹配多个搜索结果

JavaScript中通过g来实现

let str = "hello my friends, my girls";

let reg = /my/g;
console.log(str.match(reg));

// [ 'my', 'my' ]

忽略大小写

通过i忽略大小写,

let str = "hello My friends, my girls";

let reg = /my/ig;
console.log(str.match(reg));

// [ 'My', 'my' ]·	

任意字符

.可以匹配任意单个字符

let str = `
sales1.xls
orders3.xls
sales2.xls
sales3.xls
apac1.xls
europe2.xls
na1.xls
na2.xls
sa1.xls `;

let reg = /sales./g;
console.log(str.match(reg));

// [ 'sales1', 'sales2', 'sales3' ]

匹配特殊字符

使用``来转义字符,.表示匹配.本身。

匹配多个字符中的某一个

使用[]来定义一个字符集合,必须匹配其中的某个成员(并非是全部)

// 不要匹配ca1.xls
let str = `
sales1.xls
orders3.xls
sales2.xls
sales3.xls
apac1.xls
europe2.xls
na1.xls
na2.xls
sa1.xls 
ca1.xls
`;

let reg = /[sn]a../g;
console.log(str.match(reg));

[ 'na1.', 'na2.', 'sa1.' ]

匹配一组字符

例如[0123456789]可以使用连字符-这个元字符来定义字符区间

// 不要匹配sam.xls
let str = `
sales1.xls
orders3.xls
sales2.xls
sales3.xls
apac1.xls
europe2.xls
sam.xls
na1.xls
na2.xls
sa1.xls
ca1.xls 
`;

let reg = /[sn]a[0-9]./g;
console.log(str.match(reg));

还有例如a-z``A-Z``a-f``A-F

-连字符只能在[]中使用,在其它地方使用只是普通的-

let str = `
body {
  background-color: #fefbd8;
 }
 h1 {
  background-color: #0000ff;
 }
 div {
  background-color: #d0f4e6;
 }
 span {
  background-color: #f08970;
 } `;

let reg = /#[a-fA-F0-9][a-fA-F0-9][a-fA-F0-9][a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]/g;
console.log(str.match(reg));

排除 [^0-9]

使用^来排除不需要匹配的字符

let str = 
`
sales1.xls
orders3.xls
sales2.xls
sales3.xls
apac1.xls
europe2.xls
sam.xls
na1.xls
na2.xls
sa1.xls
ca1.xls 
`;

let reg = /[ns]a[^0-9].xls/g;
console.log(str.match(reg));

// [ 'sam.xls' ]

^的效果将作用于给定字符集合里的所有字符或字符区 间,而不是仅限于紧跟在^字符后面的那一个字符或字符区间

匹配空白字符

\r回车符\n换行符\t制表符

这些是空白元字符 .

类元字符

匹配数字(非数字) ( \d )

\d等于[0-9]

\D等于[^0-9]

let str = `
var myArray = new Array();
...
if (myArray[0] == 0) {
...
}
if (myArray[1] == 0) {
...
}
`
console.log(str.match(/myArray[\d]/g))

// [ 'myArray[0]', 'myArray[1]' ]

匹配字母数字下划线 ( \w )

\w匹配任何一个字母数字字符或下划线字符( [a-zA-Z0-9]

\W任何一个非字母数字或下划线字符( [^a-zA-Z0-9]

let str = `
11213
A1C2E3
48075
48237
M1B4F2
90046
H1H2H2 
`
console.log(str.match(/\w\d\w\d\w\d/g))

//[ 'A1C2E3', 'M1B4F2', 'H1H2H2' ]

匹配空白字符 ( \s )

\s匹配任何一个空白字符( [\f\n\r\t\v] )

\S匹配任何一个非空白字符( [^\f\n\r\t\v] )

重复匹配

匹配一个或多个字符 ( + )

+表示匹配一个或多个字符,至少一个。等价于{1,}

匹配邮件格式support@forta.com

let str = `
Send personal email to ben@forta.com. For questions
about a book use support@forta.com. Feel free to send
unsolicited email to spam@forta.com (wouldn't it be
nice if it were that simple, huh?). 
`
console.log(str.match(/\w+@\w+.\w+/g))

[ 'ben@forta.com', 'support@forta.com', 'spam@forta.com' ]

匹配邮件(改进版)

let str = `
Send personal email to ben@forta.com. For questions
about a book use support@forta.com. Feel free to send
unsolicited email to spam@forta.com (wouldn't it be
nice if it were that simple, huh?). 
`
console.log(str.match(/\w+@\w+.\w+/g))

// [
  'ben@forta.com',
  'ben.forta@forta.com.',
  'support@forta.com.',
  'ben@urgent.forta.com.',
  'spam@forta.com'
]

匹配零个或多个字符( * )

*表示匹配零次或多次字符。

let str = `Hello .ben@forta.com is my email address. `

console.log(str.match(/\w+[\w.]*@[\w.]+.\w+/g))
// [ 'ben@forta.com' ]

匹配零个或一个字符 ( ? )

?表示匹配零次或一次字符。等价于{0, 1}

匹配两个http链接

let str = `
The URL is http://www.forta.com/, to connect
securely use https://www.forta.com/ instead.
`
console.log(str.match(/https?://[\w./]+/g))
// [ 'http://www.forta.com/', 'https://www.forta.com/' ]

匹配换行符\n或者\r\n

let str = `
"101","Ben","Forta"
"102","Jim","James"

"103","Roberta","Robertson"
"104","Bob","Bobson" 
`
console.log(str.match(/[\r]?\n/g))
// [ '\n', '\n', '\n', '\n', '\n', '\n' ]

匹配重复次数 { 3 }

使用{}重复匹配

匹配RGB颜色

let str = `
body {
    background-color: #fefbd8;
   }
   h1 {
    background-color: #0000ff;
   }
   div {
    background-color: #d0f4e6;
   }
   span {
    background-color: #f08970;
   }    
`
console.log(str.match(/#[a-zA-Z0-9]{6}/g))

// [ '#fefbd8', '#0000ff', '#d0f4e6', '#f08970' ]

匹配重复区间范围

可以使用{2, 4}表示最少匹配2次,最多匹配4

let str = `
4/8/17
10-6-2018
2/2/2
01-01-01 
`

console.log(str.match(/\d{1,2}[-/]\d{1,2}[-/]\d{2,4}/g))

// [ '4/8/17', '10-6-2018', '01-01-01' ]

匹配至少重复多少次 {1, }

{3, }表示最少要匹配3

匹配价格大于100的订单

let str = `
1001: $496.80
1002: $1290.69
1003: $26.43
1004: $613.42
1005: $7.61
1006: $414.90
1007: $25.00 
`
console.log(str.match(/\d+: $\d{3,}.\d+/g))

// [ '1001: $496.80', '1002: $1290.69', '1004: $613.42', '1006: $414.90' ]

防止过度匹配(+? *?)

+*都是“贪婪型”元字符,其匹配行为是多多益善,它们会尽可能从开头一直匹配到结尾,而不是碰到第一个匹配时就停止。在不需要“贪婪行为”时,变成“懒惰型”,在后面加一个?,例如+?``*?

let str = `
This offer is not available to customers
living in <b>AK</b> and <b>HI</b>.
`
console.log(str.match(/<[Bb]>.*</[Bb]/g))
console.log(str.match(/<[Bb]>.*?</[Bb]/g))

//[ '<b>AK</b> and <b>HI</b' ]
//[ '<b>AK</b', '<b>HI</b' ]

位置匹配

单词边界(\b)

\b匹配一个单词的开头或结尾

一边是单词(能被\w匹配的字母数字和下划线)

一边是其它内容(能被\W匹配的字符)

let str = `
The cat scattered his food all over the room. 
`
console.log(str.match(/\bcat\b/g))

// ["cat"]

\B表示不匹配单词边界,两边都有多余空格的连字符

let str = `
Please enter the nine-digit id as it
appears on your color - coded pass-key
`
console.log(str.match(/\B-\B/g))

// ["-"]

字符串边界(^$)

^表示字符串开头,$表示字符串结尾

多行模式 (m)

多行模式迫使正则表达式引擎将换行符视为字符串分隔符

^既可以匹配字符串开头,也可以匹配换行符之后的起始位置(新行);

$不 仅能匹配字符串结尾,还能匹配换行符之后的结束位置。

m表示多行模式

let str = `
<script>
function doSpellCheck(form, field) {
 // Make sure not empty
 if (field.value == '') {
 return false;
 }
 // Init
 var windowName='spellWindow';
 var spellCheckURL='spell.cfm?formname=comment&fieldname='+
field.name;
...
 // Done
 return false;
}
</script> 
`
console.log(str.match(/^\s*//.*$/mg))

// [ ' // Make sure not empty', ' // Init', ' // Done' ]

子表达式 ($nbsp;){2}

例如匹配连续空格&nbsp;{2}只能匹配到连续出现2次或以上;的情况。

使用()包裹则可以视为子表达式

let str = `
Hello, my name is Ben&nbsp;Forta, and I am
the author of multiple books on SQL (including
MySQL, Oracle PL/SQL, and SQL Server T-SQL),
Regular&nbsp;&nbsp;Expressions, and other subjects. 
`
console.log(str.match(/(&nbsp;){2}/g))

匹配IP地址

let str = `
Pinging hog.forta.com [12.159.46.200]
with 32 bytes of data: 
`
console.log(str.match(/(\d{1,3}.){3}\d{1,3}/g))

// [ '12.159.46.200' ]

子表达式嵌套((25[0-5]) | [2[0-4]])

例如匹配适合规则的IP地址,如果使用(\d{1,3}){3}\d{1,3},则可以匹配出500.500.500.500这样错误的IP地址。

利用嵌套规避这种情况

let str = `
Pinging hog.forta.com [12.159.46.200]
with 32 bytes of data: 
`
console.log(str.match(/(((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5])).){3}((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))/g))

 任意的 1 位或 2 位数字。

 任意的以 1 开头的 3 位数字。

 任意的以 2 开头、第二位数字在 0 到 4 之间的 3 位数字。

 任意的以 25 开头、第三位数字在 0 到 5 之间的 3 位数字。

反向匹配( \1 $1 )

\1表示重复匹配第一个子表达式.

$1在替换时使用

匹配两个重复的词

let str = `
This is a block of of text,
several words here are are
repeated, and and they
should not be. 
`
console.log(str.match(/[ ]+(\w+)[ ]+\1/g))

// [ ' of of', ' are are', ' and and' ]

匹配标签,防止错误标签<h2></h3>

let str = `
<body>
<h1>Welcome to my Homepage</h1>
Content is divided into two sections:<br/>
<h2>SQL</h2>
Information about SQL.
<h2>RegEx</h2>
Information about Regular Expressions.
<h2>This is not valid HTML</h3>
</body> 
`
console.log(str.match(/<[Hh]([0-6])>.*?</[Hh]\1>/g))

// [ '<h1>Welcome to my Homepage</h1>', '<h2>SQL</h2>', '<h2>RegEx</h2>' ]

反向匹配替换

例如Hello, ben@forta.com is my email address.,把b<a href="mailto:$1">$1</a>$1替换为ben@forta.com

通过exec方法,自动会把匹配到的内容保存到RegExp.$x中,可以直接拿来使用。

let str = `Hello, ben@forta.com is my email address.`;
let r = /(\w+[\w.]*@[\w.]*.\w+)/g;
r.exec(str);
let str2 = `<a href="mailto:$1">$1</a>`
console.log(str2.replace(/$1/g, RegExp.$1));
let str = `
313-555-1234 
248-555-9999 
810-555-9000`;
let r = /(\d{3})(-)(\d{3})(-)(\d{4})/gm;
r.exec(str);

let str2 = `(${RegExp.$1}) ${RegExp.$3}-${RegExp.$5}`;
console.log(str2);
// (313) 555-1234

环视

向前查看(?=)

向前查看用?=表示,需要匹配=号后面的内容,但是不出现在结果中。

必须放在子表示中 (?=xx)

例如,匹配 协议,不要带上冒号,匹配https:返回https

let str = `
http://www.forta.com/ 
https://mail.forta.com/ 
ftp://ftp.forta.com/
`;

let reg = /\w+(?=:)/g;

console.log(str.match(reg));
// [ 'http', 'https', 'ftp' ]

向后查看(?<=)

使用?<=匹配,例如匹配$23.45,返回结果23.45

必须放在子表示中 (?<=xx)

let str = `
ABC01: $23.45 
HGG42: $5.31 
CFMX1: $899.00 
XTC99: $69.96 
Total items found: 4
`;

let reg = /(?<=$)[\d.]+/g;

console.log(str.match(reg));
// [ '23.45', '5.31', '899.00', '69.96' ]

否定式向前查看(?!)

肯定式向前查看就是需要匹配这个字符,否定式向前查看就是不要匹配这个字符

匹配数量,不要匹配价格。

let str = `
I paid $30 for 100 apples, 
50 oranges, and 60 pears. 
I saved $5 on this order.
`;

let reg = /\b(?<!$)\d+\b/g;

console.log(str.match(reg));
// [ '100', '50', '60' ]

正则练习题

1. 金额千分位分割

let str = "12345678";
let reg = /\B(?=(\d{3})+(?!\d))/g;
console.log(
  str.replace(reg, (match) => {
    return ",";
  })
);

\B匹配非单词边界,例如12之间,23之间,匹配一个空字符串。

?=(\d{3})+表示向前查看,后面3位都是数值,后面6位都是数值,后面9位都是数值都可以匹配上...

?!\d表示否定向前查看,第4位不能是数值,第7位不能是数值,第10位不能是数值.....

2. 校验密码强度

密码必须包含大写、小写、数值、特殊字符(!@#$%)

let password = "aB124@72";
let reg = /(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%])[a-zA-z0-9!@#$%]{6,}/g;
console.log(reg.exec(password));

?=.*[a-z],向前查看,表示从字符串开始,任何一个字符后面有a-z的字符,则匹配成功

其它同理

[a-zA-Z0-9!@#$%]{6,}则表示最少匹配这些字符6次