JS正则表达式

regular expression : RegExp
用来处理字符串的规则

  • 只能处理字符串
  • 它是一个规则:可以验证字符串是否符合某个规则(test),也可以把字符串中符合规则的内容捕获到(exec/match...)
let str = "you can you up, no can no bb"
let reg = /\d+/
reg.test(str);    // false

str = "123-456"
reg.exec(str);  // ["123", index: 0, input: "123-456", groups: undefined]

编写正则表达式

两种创建方式

// 字面量方式
let reg = /\d+/;

// 构造函数模式创建
let reg = new RegExp("\\d+");

两者区别:
1.构造函数中传递的是字符串,所以\需要写两个才能代表斜杠
2.两个斜杠中间包起来的都是元字符,如果正则中要包含某个变量的值,则不能用字面量创建的方式创建,而应转用构造函数模式

let name = "leonard";
reg = /^@"+name+"@$/;
reg.test("@leonard@");          // false
reg.test('@""""nameeeee"@');    // true
reg = new RegExp("^@"+name+"@$");
reg.test("@leonard@");          // true

正则表达式由两部分组成

  • 元字符
  • 修饰符
/* 常用元字符 */
// 量词元字符:设置出现的次数
* 零到多次
+ 一到多次
?零次或一次
{n} 出现n次
{n,} 出现n到多次
{n,m} 出现n到m次

// 特殊元字符:单个或者组合在一起代表特殊的含义
\  转义字符
.  除\n外的任意字符
^  以哪一个元字符作为开始
$  以哪一个元字符作为结束
\n  换行符
\d  0~9之间的一个数字
\D  非0~9之间的一个数字
\w  数字、字母、下划线中的任意一个字符
\s  一个空白字符(包含空格、制表符、换页符等)
\t  一个制表符(一个TAB键:四个空格)
\b  匹配一个单词的边界
x | y  x或者y中的一个字符
[xyz]  x或者y或者z中的一个字符
[^xy]  除了x/y以外的任意字符
[a-z]  指定a-z这个范围中的任意字符
[^a-z]  除了a-z这个范围外的任意字符
()  正则中的分组符号
(?:)  只匹配不捕获
(?=)  正向预查
(?!)  负向预查

// 普通元字符:代表本身含义的
/leonard/  匹配的就是字符串"leonard"
/* 常用修饰符 : img */
i => ignoreCase  忽略单词大小写匹配
m => multiline    可以进行多行匹配
g => global       全局匹配

元字符详解

  • ^$
let reg = /^\d/;
reg.test("leonard");      // false
reg.test("2333leonard");  // true
reg.test("leonard2333");  // false
let reg = /\d$/;
reg.test("leonard");      // false
reg.test("2333leonard");  // false
reg.test("leonard2333");  // true
let reg = /^1\d{10}$/;
reg.test("1234567890");    // false
  • \
let reg = /^2.3$/;
let reg1 = /^2\.3$/;
reg.test("2.3");     // true
reg.test("2@3");     // true
reg1.test("2.3");    // true
reg1.test("2@3");    // false
  • x | y
let reg = /^(18|29)$/    // ()处理优先级问题
reg.test("18");     // true
reg.test("18");     // true
reg.test("129");    // false
reg.test("189");    // false
  • []
let reg = /^[@+]$/;  // 中括号中出现的字符一般都代表本身的含义
reg.test("@@");    // false
reg.test("@+");    // false
reg.test("@");     // true

reg = /^[\d]$/;   // \d在中括号中还是0-9
reg.test("d");    // false
reg.test("\\");   // false
reg.test("9");    // true

reg = /^[10-29]$/;    // 中括号中不存在多位数
reg.test("1");      // true
reg.test("9");      // true
reg.test("0");      // true
reg.test("10");     // false

常用的正则表达式
1.验证是否为有效数字
let reg = /^[+-]?(\d|([1-9]\d+))(\.\d+)?$/
2.验证6~16密码,含数字、字母、下划线
let reg = /^\w{6,16}$/
3.验证真实姓名(2~10位)
let reg = /^[\u4E00-\u9FA5]{2,10}(·[\u4E00-\u9FA5]{2,10}){0,2}$/
4.验证邮箱
let reg = /^\w+((-\w+)|(\.\w+))*@[a-zA-Z0-9]+((\.|-)[a-zA-Z0-9]+)*\.[a-zA-Z0-9]+$/
5.身份证号码
let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/


正则的捕获

实现正则捕获的方法

  • RegExp.prototype上的方法
    • exec
      基于exec实现正则的捕获
      1.捕获到的结果是null或者一个数组
      首项:本次捕获到的内容
      其余项:对应小组本次单独捕获的内容
      index:当前捕获内容在字符串中的起始索引
      input:原始字符串
      2.每执行一次exec只能捕获到一个符合正则规则的
      正则捕获的懒惰性:由于默认情况下lastIndex的值不会被修改,所以每次执行只捕获第一个
      解决办法:全局修饰符g,全部捕获后会出现循环捕获现象
let str = "leonard2333age18"
let reg = /\d+/;
reg.exec(str);    // ["2333" ...]
reg.exec(str);    // ["2333" ...]
reg.exec(str);    // ["2333" ...]
let reg1 = /\d+/g;
reg1.exec(str);    // ["2333" ...]
reg1.exec(str);    // ["18" ...]
reg1.exec(str);    // null
reg1.exec(str);    // ["2333" ...]
  • test
let str = "{0}年{1}月{2}日";
let reg = /\{(\d+)\}/g;
console.log(reg.test(str));    // true
console.log(RegExp.$1);        // 0
console.log(reg.test(str));    // true
console.log(RegExp.$1);        // 1
console.log(reg.test(str));    // true
console.log(RegExp.$1);        // 2
console.log(reg.test(str));    // false
console.log(RegExp.$1);        // 2,存储上一次捕获的结果
  • 字符串String.prototype上支持正则表达式处理的方法
    • replace
let time = "2019-8-8";
let reg = /(\d{4})-(\d{1,2})-(\d{1,2})/g;
console.log(time.replace(reg,"$1年$2月$3日"));    // "2019年8月8日"

time = time.replace(reg,(...args)=>{
    let [,$1,$2,$3] = args;
    return `${$1}年${$2}月${$3}日`    // 返回内容替换匹配内容
})
console.log(time);    // "2019年8月8日"

// 利用replace实现单词首字母大写
let str = "how are you?";
let reg1 = /\b([a-zA-Z])[a-zA-Z]*\b/g;
str = str.replace(reg1, (...args)=>{
  let [content, $1] = args;
  $1 = $1.toUpperCase();
  content = content.substring(1);
  return $1 + content;
})
console.log(str);    // "How Are You?"
  • match
let str = "leonard2333leonard123leonard333";
let reg = /\d+/g;
console.log(str.match(reg));    // ["2333", "123", "333"]
  • splite

基于exec实现一次全匹配的方法execAll

~ function(){
  function execAll(str = ""){
    if(!this.global) return this.exec(str);
    let ary = [],
        res = this.exec(str);
    while(res){
      ary.push(res[0]);
      res = this.exec(str);
    }
    return ary.length === 0 ? null : ary;
  }
  RegExp.prototype.execAll = execAll;
}()

let str = "leonard2333leonard123leonard333";
let reg = /\d+/g;
console.log(reg.execAll(str));    // ["2333", "123", "333"]

分组捕获

let str = "350623198401234423";
let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/;
let reg1 = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d|X)$/;    // 只匹配不捕获
reg.exec(str);
str.match(reg);
// ["350623198401234423", "350623", "1984", "01", "23", "2", "3"...]
reg1.exec(str);
str.match(reg1);
// ["350623198401234423", "350623", "1984", "01", "23", "2"...]

分组引用

let str = "tooth";
let reg = /^[a-zA-Z]([a-zA-Z])\1[a-zA-Z][a-zA-Z]$/;
console.log(reg.test(str));    // true

正则捕获的贪婪性

let str = "啊喽哈2333@3222";
// 正则捕获的贪婪性:默认情况下,正则捕获的时候,是按照当前正则所匹配的最长结果来获取的
let reg = /\d+/g;
console.log(str.match(reg));  //  ["2333","3222"]
// 在量词元字符后面设置?:取消捕获的贪婪性
reg = /\d+?/g;
console.log(str.match(reg));  //  ["2", "3", "3", "3", "3", "2", "2", "2"]

问号在正则中的五大作用

  • 问号左边是非量词元字符:代表量词元字符出现零到一次
  • 问号左边是量词元字符:取消捕获时的贪婪性
  • (?:) 只匹配不捕获
  • (?=) 正向预查
  • (?!) 负向预查

正向预查和负向预查

  • (?=pattern) 正向预查
    在任何匹配pattern的字符串开始处匹配字符串,是非获取字符串(即只匹配不获取)
var str = "leonard111leonard2333leonard233leonard666"
var reg = /leonard(?=23+)/g;
var a = str.match(reg);
console.log(a); // ["leonard", "leonard"]
  • (?!pattern) 负向预查
    匹配方式与正向相反,返回的是匹配不到的字符串,也是非获取字符串
var str = "leonard111leonard2333leonard233leonard666"
var reg = /leonard(?!1+)/g;
var a = str.match(reg);
console.log(a); // ["leonard", "leonard", "leonard"]
  • 曾经面过的一道题
    验证6-16位的密码,可以是数字、字母、下划线,但要求必须包含大小写字母、数字三项
    正向预查写法:
    let reg = /^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])\w{6,16}$/g
    负向预查写法:
    let reg = /^(?![0-9]+$)(?![A-Z]+$)(?![a-z]+$)(?![0-9a-z]+$)(?![0-9A-Z]+$)(?![a-zA-Z]+$)\w{6,16}$/g

面试题
获取出现次数最多的字母

// Method 1
let str = "let me put it this way";
let obj = {};

[].slice.call(str).forEach(item=>{
  if(item === ' ') return;
  if(typeof obj[item] !== 'undefined'){
    obj[item]++;
    return;
  }
  obj[item] = 1;
})

let max = 1,
    res = [];
for(let key in obj){
  max < obj[key] ? max = obj[key] : null;
}
for(let key in obj){
  max === obj[key] ? res.push(key) : null;
}
console.log(`出现最多次的字母是${res.join()},总共出现了${max}次`)

// Method 2
let str = "let me put it this way";
let obj = {};
str = str.replace(/\s/g,() => '').split('').sort((a,b) => a.localeCompare(b)).join('');

let reg = /([a-zA-Z])\1*/g;
let ary = str.match(reg).sort((a,b) => b.length - a.length);

let res = ary.slice(0,1);
let max = res[0].length;
for(let i = 1; i < ary.length; i++){
  if(ary[0].length !== ary[i].length) break;
  res.push(ary[i])
}
res = res.map((item) => {
  return item.substr(0,1);
})
console.log(`出现最多次的字母是${res.join()},总共出现了${max}次`);

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容

  • 正则表达式功能: 测试字符串的某个模式。例如对一个输入字符串进行测试,看在该字符串是否存在一个电话号码模式或一个信...
    DecadeHeart阅读 605评论 0 1
  • @(javascript)[js正则表达式] [toc] JS正则表达式 正则表达式一直是一个令人头疼但却又是十分...
    踏浪free阅读 428评论 0 1
  • 一、匹配符 \:指出接着的字符为特殊字符。 ^:表示匹配的字符必须在最前边。 $:表示匹配的字符必须在最后边。 *...
    活叁黄辉冯阅读 779评论 0 3
  • RegExp对象实例化 RegExp是JS的正则表达式对象,实例化一个RegExp对象有字面量和构造函数2种方式。...
    octcms阅读 2,004评论 0 3
  • js正则表达式(第一周) 从本周开始我将按时发表我的日志,每周一篇原创,日志内容主要是计算机知识,偶尔也会有电学内...
    发挥不广泛阅读 555评论 0 1