正则表达式学习(一)--字符匹配

文章首发于sau交流学习社区一、前言正则表达式是匹配模式,要么匹配字符,要么匹配位置。正则里面的元字符太多了,没有系统性,可以分为:(1)字符匹配攻略(2)位置匹配攻略二、字符匹配包括:两种模糊匹配,字符数组,量词,分支结构。1.1两种模糊匹配正则精确匹配乜有意义的,比如正则/saucxs/,只能匹配字符串中的"saucxs"这个子串。var regex = /saucxs/;console.log(regex.test(‘saucxs’)); //trueconsole.log(regex.test(‘123saucxs4560’)); //true正则很强大的地方就是,实现模糊匹配。模糊匹配分为:(1)横向模糊匹配;(2)纵向模糊匹配。1.1.1横向模糊匹配横向模糊匹配指的是:一个正则可匹配的字符串的长度不是固定的,可以是很多种情况。实现方式:使用量词。比如:{m,n}表示的连续出现最少m次,最多n次。比如正则: /ab{2,5}c/表示匹配这样一个字符串:第一个字符是‘a’,接下来第2个到第5个字符是‘b’,最后是字符‘c’。正则可视化形式如下:RegExp:/ab{2,5}c/测试一下:var regex = /ab{2,5}c/g;var string = “abc abbc abbbc abbbbc abbbbbc abbbbbbc”;console.log( string.match(regex) ); // [“abbc”, “abbbc”, “abbbbc”, “abbbbbc”]正则里的g是正则的一个修饰符,表示全局匹配,即按照顺序找到满足匹配的所有子串。1.1.2纵向模糊匹配纵向模糊匹配的是,一个正则匹配的字符串,具体到某一个字符时。实现的方式:使用字符组。比如[abc],表示该字符可以是‘a’,‘b’,‘c’中的任何一个。比如正则/a[123]b/可以匹配到如下三种字符串’a1b’,‘a2b’,‘a3b’。可视化过程:RegExp:/a[123]b/测试一下var regex = /a[123]b/g;var string = “a0b a1b a2b a3b a4b”;console.log( string.match(regex) ); // [“a1b”, “a2b”, “a3b”]横向和纵向匹配,能解决很多的正则匹配问题1.2字符组字符组只是其中一个字符比如[abc],表示匹配一个字符,它可以是’a’,‘b’,‘c’之一。1.2.1范围表示法如果字符组中字符特别多,怎么处理?可以使用范围表示法。比如[123456abcdefGHIJKLM],可以写成[1-6a-fG-M]。用连字符 - 来省略和简写。如果要匹配’a’,’-’,‘z’这三个字符中任意一个字符,怎么处理?答:这个时候不能写成[a-z],因为这个是表示的是小写字母中的任何一个字符。可以写成[-az]或[az-]或[a-z]。就是说要不放在开头,要不放在结尾,要么转义。个人觉得用[a-z]最好,使用将连字符 - 转义成普通字符。1.2.2排除字符组纵向模糊匹配,还有一种情形:某一个字符可以是除了’a’,‘b’,‘c’之外的任何字符。这个时候就需要使用排除字符组(反义字符组),比如1,表示的是一个除’a’,‘b’,‘c’之外的任意一个字符。字符组的第一位放^(脱字符),表示求反的意思。1.2.3常见的简写形式有了字符组的概念后,一些常见的符号我们就可以理解了,因为都是系统自带的简写形式。如果要匹配任意字符怎么办?可以使用 [dD]、[wW]、[sS] 和 [^] 中任何的一个。1.3量词量词也称为重复,掌握{m,n}的准确含义,只需要记住一些简写形式。1.3.1简写形式正则RegExp:/a{1,2}b{3,}c{4}d?e+f*/1.3.2贪婪匹配与惰性匹配看个栗子:var regex = /\d{2,5}/g;var string = “123 1234 12345 123456”;console.log( string.match(regex) ); // [“123”, “1234”, “12345”, “12345”]正则/d{2,5}/表示数字连续出现2到5次。会匹配第2位,第3位,第4位,第5位的连续数字。但是他是贪婪的,会尽可能多的匹配。还有一种就是懒惰的匹配(尽可能少的匹配):var regex = /\d{2,5}?/g;var string = “123 1234 12345 123456”;console.log( string.match(regex) ); // [“12”, “12”, “34”, “12”, “34”, “12”, “34”, “56”]正则/d{2,5}?/表示,虽然2到5次都行,当2个就够的时候,就不再往下尝试了。惰性实现:通过在量词后面加个问号就能实现惰性匹配记忆方式:量词后面加个问号,问一问你知足了吗,你很贪婪吗?RegExp:/a{1,2}?b{3,}?c{4}?d??e+?f*?/1.4多选分支一个模式可以实现横向和纵向模糊匹配,而多选分支可以支持多个子模式任选其一。具体形式::(p1|p2|p3),其中 p1、p2 和 p3 是子模式,用 |(管道符)分隔,表示其中任何之一。比如:要匹配字符串good和nice,可以使用/good|nice/。可视化形式如下:RegExp:/good|nice/测试如下var regex = /good|nice/g;var string = “good idea, nice try.";console.log( string.match(regex) ); // [“good”, “nice”]有个地方需要注意:我用 /good|goodbye/,去匹配 “goodbye” 字符串时,结果是 “good”:var regex = /good|goodbye/g;var string = “goodbye”;console.log( string.match(regex) ); // [“good”]而把正则改成 /goodbye|good/,结果是:var regex = /goodbye|good/g;var string = “goodbye”;console.log( string.match(regex) ); // [“goodbye”]也就是说,分支结构也是惰性的,即当前面的匹配上了,后面的就不再尝试了。1.5案例分析匹配字符,无非就是字符组,量词,分支结构的组合使用。多练习一下:1.5.1匹配16进制的颜色值要求匹配:#ffbbad#Fc01DF#FFF#ffE分析:表示一个16进制字符,可以使用字符组[0-99a-fA-F];其中字符可以出现3或6次,需要使用量词和分支结构;使用分支结构,需要注意顺序。var regex = /#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g;var string = “#ffbbad #Fc01DF #FFF #ffE”;console.log( string.match(regex) ); // ["#ffbbad”, “#Fc01DF”, “#FFF”, “#ffE”]可视化形式:REgExp:/#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g1.5.2匹配时间以24小时为例要求匹配:23:5902:07分析:一共四位数字,第一位数字可以为[0-2];当第1位为'2’时,第2位可以是为[0-3],其他情况,第2位为[0-9];第三位数字为[0-5],第四位为[0-9]。正则如下:var regex = /^([01][0-9]|[2][0-3]):[0-5][0-9]$/;console.log( regex.test(“23:59”) ); //trueconsole.log( regex.test(“02:07”) ); //trueconsole.log( regex.test(“24:00”) ); //falseconsole.log( regex.test(“24:01”) ); //false注意:正则中使用了^和$,分别表示字符串开头和结尾。如果要求可以匹配'7:9’,也就是说时分前面的'0’可以省略。var regex = /^(0?[0-9]|1[0-9]|[2][0-3]):(0?[0-9]|[1-5][0-9])$/;console.log( regex.test(“23:59”) ); // trueconsole.log( regex.test(“02:07”) ); //trueconsole.log( regex.test(“7:9”) ); //true可视化形式:RegExp:/^(0?[0-9]|1[0-9]|[2][0-3]):(0?[0-9]|[1-5][0-9])$/1.5.3匹配日期比如要求yyyy-mm-dd格式要求匹配:2017-06-10分析:年,4位数字即可,可以用[0-9]{4};月,共12个月,分为两种:“01”,“02”,…,“09"和"10”,“11”,“12”,可以用(0[1-9]|1[0-2]);日,最大31天,可以用(0[1-9]|12|3[01])。正则如下:var regex = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/;console.log( regex.test(“2017-06-10”) ); //true可视化形式:RegExp:/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/注意:其实并不是很准确的,因为要考虑的还有是否是闰年,2月份特殊情况等情况。1.5.4匹配id要求从<div id=“container” class=“main”></div>提取出id=“container"最初想应该是这样的var regex = /id=”."/var string = ‘<div id=“container” class=“main”></div>’;console.log(string.match(regex)[0]);// id=“container” class=“main"可视化形式:RegExp:/id=”."/g因为 . 是通配符,本身就会匹配双引号的,而量词 * 又是贪婪的,当遇到container后面双引号时候,是不会停下来的,会继续匹配,直到遇到最后一个双引号为止。解决办法:使用惰性匹配var regex = /id=".?"/var string = ‘<div id=“container” class=“main”></div>’;console.log(string.match(regex)[0]);// id=“container"其实这样也是有问题的。效率比较低,因为匹配原理会涉及到“回溯”这个概念。可以优化一下:var regex = /id=”[^"]"/var string = ‘<div id=“container” class=“main”></div>’;console.log(string.match(regex)[0]);// id=“container"abc ↩ ...

March 29, 2019 · 2 min · jiezi

《JavaScript 正则表达式迷你书》知识点小抄本

介绍这周开始学习老姚大佬的《JavaScript 正则表达式迷你书》 , 然后习惯性的看完一遍后,整理一下知识点,便于以后自己重新复习。 我个人觉得:自己整理下来的资料,对于知识重现,效果不错。 感谢原书作者老姚,本文无意抄袭,只是作为自己知识点的整理,后续也会整理到自己的 JavaScript知识库——《Cute-JavaScript》 网站中。 另外,请读者们注意,这篇文章是知识点的整理,方便复习,所以不会介绍太详细,因为毕竟原书写得非常棒,刚入门的朋友,我还是建议看下原书。 然后可以看看这篇文章,来回顾重要知识点。《JavaScript 正则表达式迷你书》《Cute-JavaScript》目录一、正则表达式字符匹配二、正则表达式位置匹配三、正则表达式括号的使用四、正则表达式回溯法原理五、正则表达式的拆分六、正则表达式的构建七、正则表达式编程文章推荐(补充中)老姚 —— JS正则表达式完整教程常见的正则表达式可视化描述工具推荐Regulex*%3F%24)Rubular一、正则表达式字符匹配原书这么一句话,特别棒:正则表达式是匹配模式,要么匹配字符,要么匹配位置,要记住。1. 两种模糊匹配正则表达式的强大在于它的模糊匹配,这里介绍两个方向上的“模糊”:横向模糊和纵向模糊。横向模糊匹配即一个正则可匹配的字符串长度不固定,可以是多种情况。 如 /ab{2,5}c/ 表示匹配: 第一个字符是 “a” ,然后是 2 - 5 个字符 “b” ,最后是字符 “c” :let r = /ab{2,5}c/g;let s = “abc abbc abbbc abbbbbbc”;s.match(r); // [“abbc”, “abbbc”]纵向模糊匹配即一个正则可匹配某个不确定的字符,可以有多种可能。 如 /[abc]/ 表示匹配 “a”, “b”, “c” 中任意一个。let r = /a[123]b/g;let s = “a0b a1b a4b”;s.match(r); // [“a1b”]2. 字符组范围表示法可以指定字符范围,比如 [1234abcdUVWXYZ] 就可以表示成 [1-4a-dU-Z] ,使用 - 来进行缩写。 如果要匹配 “a”, “-”, “z” 中任意一个字符,可以这么写: [-az] 或 [a-z] 或 [az-] 。排除字符组即需要排除某些字符时使用,通过在字符组第一个使用 ^ 来表示取反,如 [^abc] 就表示匹配除了 “a”, “b”, “c” 的任意一个字符。常见简写形式字符组具体含义\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] 。通配符,表示几乎任意字符。换行符、回车符、行分隔符和段分隔符除外。然后表示任意字符,就可以使用 [\d\D]、[\w\W]、[\s\S] 和 [^] 任意一个。3. 量词量词也称重复,常用简写如下:量词具体含义{m,}表示至少出现 m 次。{m}等价于 {m, m} ,表示出现 m 次。?等价于 {0, 1} ,表示出现或不出现。+等价于 {1, } ,表示至少出现1次。等价于 {0, } ,表示出现任意次,也有可能不出现。贪婪匹配和惰性匹配在正则 /\d{2,4}/ ,表示数字连续出现 2 - 4 次,可以匹配到 2 位、 3 位、4 位连续数字。 但是在 贪婪匹配 如 /\d{2,4}/g ,会尽可能多匹配,如超过 4 个,就只匹配 4 个,如有 3 个,就匹配 3 位。 而在 惰性匹配 如 /\d{2,4}?/g ,会 尽可能少 匹配,如超过 2 个,就只匹配 2 个,不会继续匹配下去。let r1 = /\d{2,4}/g;let r2 = /\d{2,4}?/g;let s = “123 1234 12345”; s.match(r1); // [“123”, “1234”, “1234”]s.match(r2); // [“12”, “12”, “34”, “12”, “34”]惰性量词贪婪量词{m,m}?{m,m}{m,}?{m,}???+?+?4. 多选分支即提供多个子匹配模式任选一个,使用 |(管道符)分隔,由于分支结构也是惰性,即匹配上一个后,就不会继续匹配后续的。 格式如:(r1|r2|r3),我们就可以使用 /leo|pingan/ 来匹配 “leo” 和 “pingan”。let r = /leo|pingan/g;let s = “leo cool,pingan good.";s.match(r);// [“leo”, “pingan”]// 多选分支的惰性表现let r1 = /leo|leooo/g;let r2 = /leooo|leo/g;let s = “leooo”;s.match(r1);// [“leo”]s.match(r2);// [“leooo”]5. 案例分析匹配字符,无非就是字符组、量词和分支结构的组合使用。十六进制颜色值匹配let r = /#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}/g;let s = “#ffaacc #Ff00DD #fFF #01d #9Gd”;s.match(r); // ["#ffaacc”, “#Ff00DD”, “#fFF”, “#01d”]时间和日期匹配// 时间 12:23 或 01:09let r = /^([01][0-9]|[2][0-3]):[0-5][0-9]$/; r.test(“23:25”); // truer.test(“03:05”); // true// 时间 12:23 或 1:9let r = /^(0?[0-9]|1[0-9]|[2][0-3]):(0?[0-9]|[1-5][0-9])$/; r.test(“23:25”); // truer.test(“03:05”); // truer.test(“3:5”); // true// 日期 yyyy-mm-ddlet r = /^[0-9]{4}-(0[1-9]|[1][0-2])-(0[1-9]|[12][0-9]|3[01])$/;r.test(“2019-09-19”); // truer.test(“2019-09-32”); // falseWindows操作系统文件路径匹配盘符使用 [a-zA-Z]:\ ,这里需要注意 \ 字符需要转义,并且盘符不区分大小写; 文件名或文件夹名,不能包含特殊字符,使用 [^\:<>|"?\r\n/] 表示合法字符; 并且至少有一个字符,还有可以出现任意次,就可以使用 ([^\:<>|"?\r\n/]+\) 匹配任意个 文件夹\; 还有路径最后一部分可以是 文件夹 ,即没有 \ 于是表示成 ([^\:<>|"?\r\n/]+)?。let r = /^[a-zA-Z]:\([^\:<>|"?\r\n/]+\)([^\:<>|"?\r\n/]+)?$/;r.test(“C:\document\leo\a.png”); // truer.test(“C:\document\leo\”); // truer.test(“C:\document”); // truer.test(“C:\”); // trueid匹配如提取 <div id=“leo” class=“good”></id> 中的 id=“leo” :let r1 = /id="."/; // tips1let r2 = /id=".?"/; // tips2let r3 = /id="[^"]"/; // tips3let s = ‘<div id=“leo” class=“good”></id>’;s.match(r1)[0]; // id=“leo” class=“good"s.match(r2)[0]; // id=“leo"s.match(r3)[0]; // id=“leo"tips1:由于 . 匹配双引号,且 * 贪婪,就会持续匹配到最后一个双引号结束。 tips2:使用惰性匹配,但效率低,有回溯问题。 tips3:最终优化。二、正则表达式位置匹配位置匹配,就是要匹配每个字符两边的位置。 在 ES5 中有6个位置: ^,$,\b,\B,(?=p) 和 (?!p)。 另外把位置理解成空字符是非常有用的:/^^hello$$/.test(‘hello’); // true/^^^hello$$/.test(‘hello’); // true1. ^ 和 $^ 匹配开头,多行中匹配行开头。 $ 匹配结尾,多行中匹配行结尾。“hello”.replace(/^|$/g, “#”); // “#hello#““hello\nleo\nhaha”.replace(/^|$/gm, “#”);/#hello##leo##haha#/多行匹配模式使用 m 修饰符。2. \b 和 \B\b 匹配单词边界,即 \w 和 \W 之间的位置,包括 \w 和 ^ 之间的位置,和 \w 和 $ 之间的位置。 \B 和 \b 相反,即非单词边界,匹配中除去 \b,剩下的都是 \B 的。也就是 \w 与 \w、 \W 与 \W、^ 与 \W,\W 与 $ 之间的位置。。"[HI] Leo_1.mp4”.replace(/\b/g,”#”);// “[#HI#] #Leo_1#.#mp4#”"[HI] Leo_1.mp4”.replace(/\B/g,”#");// “#[H#I]# L#e#o#_#1.m#p#4"3. (?=p) 和 (?!p)p 为一个子模式,即 (?=p) 匹配前面是 p 的位置,而 (?!p) 则匹配前面不是 p 的位置。“hello”.replace(/(?=l)/g, “#”);// “he#l#lo"“hello”.replace(/(?!l)/g, “#”);// “#h#ell#o#“4. 相关案例匹配数字千位分隔符// 匹配最后一个逗号"12345678”.replace(/(?=\d{3}$)/g, “,”); // “12345,678”// 匹配所有逗号"12345678”.replace(/(?=(\d{3})+$)/g, “,”); // “12,345,678”// 匹配其余"123456789”.replace(/(?=(\d{3})+$)/g, “,”); // “,123,456,789”// 修改"123456789”.replace(/(?!^)(?=(\d{3})+$)/g, “,”); // “12,345,678”// 其他形式"12345678 123456789".replace(/(?!\b)(?=(\d{3})+\b)/g, “,”); // (?!\b) 等于 \B ,要求当前是一个位置,但不是 \b 前面的位置// “12,345,678 123,456,789"数据格式化let num = 1888;num.toFixed(2).replace(/\B(?=(\d{3})+\b)/g, “,”).replace(/^/,"$$ “);// “$ 1,888.00"验证密码// 密码长度 6-12 位数字或字母let r = /^[0-9A-Za-z]{6,12}$/;// 必须包含一个字符(数字) + 密码长度 6-12 位数字或字母let r = /(?=.[0-9])^[0-9A-Za-z]{6,12}$/;// 必须包含两个个字符(数字和小写字符) + 密码长度 6-12 位数字或字母let r = /(?=.[0-9])(?=.[a-z])^[0-9A-Za-z]{6,12}$/;r.test(“aa1234566”); // truer.test(“1234566”); // false// 密码长度 6-12 位数字或字母 // 即 不能全是数字 或 不能全是大写或小写字母let r = /(?!^[0-9]{6,12}$)(?!^[a-z]{6,12}$)(?!^[A-Z]{6,12}$)^[0-9A-Za-z]{6,12}$/;三、正则表达式括号的使用简单理解:括号提供了分组,便于我们使用它。 通常有两种引用情况:在JS代码中引入,和在正则表达式中引入。 分组和分支结构,主要是强调括号内是一个整体,即提供子表达式。分组如 /(ab)+/g 匹配连续出现的 ab 。分支结构如 /(a|b)+/g 匹配出现的 a 或 b 表达式。1.分组引用如在日期匹配的时候,就可以这么改造:// 原来let r = /\d{4}-\d{2}-\d{2}/;// 现在let r = /(\d{4})-(\d{2})-(\d{2})/;提取数据"2019-03-14”.match(r);r.exec(“2019-03-14”);// [“2019-03-14”, “2019”, “03”, “14”, index: 0, input: “2019-03-14”]RegExp.$1; // “2019"RegExp.$2; // “03"RegExp.$3; // “14"替换将 yyyy-mm-dd 转成 mm/dd/yyyy。“2019-03-14”.replace(r, “$2/$3/$1”);// 等价于"2019-03-14”.replace(r, function(){ return RegExp.$2 + ‘/’ + RegExp.$3 + ‘/’ + RegExp.$1;});2. 反向引用使用 \n 表示第 n 个分组,比如 \1 表示第 1 个分组:let r = /\d{4}(-|/|.)\d{2}\1\d{2}/;r.test(“2019-03-15”);r.test(“2019/03/15”);r.test(“2019.03.15”);r.test(“2019-03/15”);多个括号嵌套按照开括号的顺序:let r = /^((\d)(\d(\d)))\1\2\3\4$/;let s = “1231231233”;r.test(s);console.log([RegExp.$1,RegExp.$2,RegExp.$3,RegExp.$4]);// [“123”, “1”, “23”, “3”]特殊情况\10 表示的是第 10 个分组,若要匹配 \ 和 0 时,使用 (?:\1)0 或 \1(?:0)。let r = /(1)(2)(3)(4)(5)(6)(7)(8)(9)(#) \10+/;let s = “123456789# #####";r.test(s); // true当引用不存在的分组如匹配 \2 是前面不存在,则匹配 \2 本身,即对 2 的转义,不同浏览器可能不同:let r = /\1\2\3\4/;r.test("\1\2\3\4”); // true”\1\2\3\4”.split(’’);// [””, “”, “”, “"]分组后面有量词当分组后面有量词的话,则捕获的是最后一次的匹配:“12345”.match(/(\d)+/); // [“12345”, “5”, index: 0, input: “12345”]/(\d)+ \1/.test(“12345 1”); // false/(\d)+ \1/.test(“12345 5”); // true3. 相关案例这里只写出核心代码。模拟字符串 trim 方法// 1 匹配首尾空白符,替换成空字符” aaa “.replace(/^\s+|\s+$/g, “”); // “aaa”// 2 匹配整个字符串,再用引用提取对应数据” aaa “.replace(/^\s*(.?)\s$/g, “$1”);// “aaa"每个单词首字母大写"hi leo hi boy!".toLowerCase().replace( /(?:^|\s)\w/g, c => c.toUpperCase());// “Hi Leo Hi Boy!“驼峰化 和 中划线化”-leo-and-pingan”.replace(/[-\s]+(.)?/g, (match, c) => c ? c.toUpperCase() : ‘’);// “LeoAndPingan"“LeoAndPingan”.replace(/([A-Z])/g, “-$1”).replace( /[-\s]+g/,”-”).toLowerCase();// “-leo-and-pingan"匹配成对HTML标签匹配成对标签 <h1>leo<\h1>,而不匹配不成对标签 <h1>leo<\h2>。let r = /<([^>]+)>[\d\D]</\1>/;r.test("<h1>leo leo leo</h1>”); // truer.test("<a>leo leo leo</a>”); // truer.test("<h1>leo leo leo</h2>"); // false四、正则表达式回溯法原理概念理解起来比较容易。 比如用 /ab{1,3}c/ 去匹配下面两个字符串。当匹配 abbbc,按顺序匹配,到了第 3 个 b 后,直接匹配 c,这样就没有回溯。当匹配 abbc,按顺序匹配,到了第 2 个 b 后,由于规则是 b{1,3} ,则会继续往下匹配,然后发现下一位是 c,于是回退到前一个位置,重新匹配,这就是回溯。另外像 /"."/ 来匹配 “abc"de 的话,就会有三个回溯情况,为了减少不必要的回溯,我们可以把正则修改为 /”[^"]"/。介绍 回溯法,也称试探法,本质上是深度优先探索算法,基本思路是:匹配过程中后退到之前某一步重新探索的过程。1. 常见的回溯形式贪婪量词多个贪婪量词挨着存在,并相互冲突时,会看匹配顺序,深度优先搜索:“12345”.match(/(\d{1,3})(\d{1,3})/);// [“12345”, “123”, “45”, index: 0, input: “12345”]惰性量词有时候会因为回溯,导致实际惰性量词匹配到的不是最少的数量:“12345”.match(/(\d{1,3}?)(\d{1,3})/);// 没有回溯的情况 [“1234”, “1”, “234”, index: 0, input: “12345”]“12345”.match(/^\d{1,3}?\d{1,3}$/);// 有回溯的情况 [“12345”, index: 0, input: “12345”]分支结构分支机构,如果一个分支整体不匹配,会继续尝试剩下分支,也可以看成一种回溯。“candy”.match(/can|candy/); // [“can”, index: 0, input: “candy”]“candy”.match(/^(?:can|candy)$/); // [“candy”, index: 0, input: “candy”]2. 本章小结简单总结:一个个尝试,直到,要么后退某一步整体匹配成功,要么最后试完发现整体不匹配。贪婪量词:买衣服砍价,价格高了,便宜点,再便宜点。懒惰量词:卖衣服加价,价格低了,多给点,再多给点。分支结构:货比三家,一家不行换一家,不行再换。五、正则表达式的拆分拆分正则代码块,是理解正则的关键。 在 JavaScrip 正则表达式有以下结构:字面量: 匹配一个具体字符,如 a 匹配字符 a。字符组: 匹配一个有多种可能性的字符,如 [0-9] 匹配任意一个数字。量词: 匹配一个连续出现的字符,如 a{1,3} 匹配连续最多出现 3 次的a字符。锚: 匹配一个位置,如 ^ 匹配字符串的开头。分组: 匹配一个整体,如 (ab) 匹配 ab 两个字符连续出现。分支: 匹配一个或多个表达式,如 ab|bc 匹配 ab 或 bc 字符。另外还有以下操作符:优先级操作符描述操作符1转义符\2括号和方括号(…)/(?:…)/(?=…)/(?!…)/[…]3量词限定符{m}/{m,n}/{m,}/?//+4位置和序列^/$/\元字符/一般字符5管道符``Tips:优先级从上到下,由高到低。1. 注意要点匹配字符串整体不能写成 /^abc|bcd$/ ,而是要写成 /^(abc|bcd)$/。量词连缀问题需要匹配:每个字符是 a/b/c 中其中一个,并且字符串长度是 3 的倍数: 不能写成 /^[abc]{3}+$/ ,而是要写成 /([abc]{3})+/。元字符转义问题元字符就是正则中的特殊字符,当匹配元字符就需要转义,如: ^、$、.、、+、?、|、\、/、(、)、[、]、{、}、=、!、:、- 。// “[abc]” => /[abc]/ 或者 /[abc]/ // “{1,3}” => /{1}/ 或者 /{1}/ 因为不构成字符组2. 案例分析身份证号码/^(\d{15}|\d{17})[\dxX]$/.test(“390999199999999999”);// trueIPV4地址需要好好分析:let r = /^((0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5]).){3}(0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5])$/六、正则表达式的构建正则的构建需要考虑以下几点的平衡:匹配预期的字符串不匹配非预期的字符串可读性和可维护性效率我们还需要考虑这么几个问题:是否需要使用正则如能使用其他 API 简单快速解决问题就不需要使用正则:“2019-03-16”.match(/^(\d{4})-(\d{2})-(\d{2})/); // 间接获取 [“2019”, “03”, “16”]“2019-03-16”.split("-"); // [“2019”, “03”, “16”]"?id=leo".search(/?/); // 0"?id=leo".indexOf("?"); // 0"JavaScript".match(/.{4}(.+)/)[1]; // “Script"“JavaScript”.substring(4); // “Script"是否需要使用复杂正则/(?!^[0-9]{6,12}$)(?!^[a-z]{6,12}$)(?!^[A-Z]{6,12}$)^[0-9A-Za-z]{6,12}$/ 将这个正则拆分成多个小块,如下:var regex1 = /^[0-9A-Za-z]{6,12}$/;var regex2 = /^[0-9]{6,12}$/;var regex3 = /^[A-Z]{6,12}$/;var regex4 = /^[a-z]{6,12}$/;function checkPassword (string) { if (!regex1.test(string)) return false; if (regex2.test(string)) return false; if (regex3.test(string)) return false; if (regex4.test(string)) return false; return true;}1. 准确性即需要匹配到预期目标,且不匹配非预期的目标。匹配固定电话如需要匹配下面固定电话号码,可以分别写出对应正则:055188888888 => /^0\d{2,3}[1-9]\d{6,7}$/0551-88888888 => /^0\d{2,3}-[1-9]\d{6,7}$/(0551)88888888 => /^0\d{2,3}-[1-9]\d{6,7}$/然后合并:let r = /^0\d{2,3}[1-9]\d{6,7}$|^0\d{2,3}-[1-9]\d{6,7}$|^(0\d{2,3})[1-9]\d{6,7}$/然后提取公共部分:let r = /^(0\d{2,3}|0\d{2,3}-|(0\d{2,3}))[1-9]\d{6,7}$/再优化:let r = /^(0\d{2,3}-?|(0\d{2,3}))[1-9]\d{6,7}$/匹配浮点数先确定,符号部分([+-])、整数部分(\d+)和小数部分(.\d+)。1.23、+1.23、-1.23 => /^[+-]?\d+.\d+$/10、+10、-10 => /^[+-]?\d+$/.2、+.2、-.2 => /^[+-]?.\d+$/整理后:let r = /^[+-]?(\d+.\d+|\d+|.\d+)$/;// 考虑不匹配 +.2 或 -.2let r = /^([+-])?(\d+.\d+|\d+|.\d+)$/;// 考虑不匹配 012 这类 0 开头的整数let r = /^[+-]?(\d+)?(.)?\d+$/;2. 效率正则表达式运行过程:编译设定起始位置尝试匹配若匹配失败则返回前一步重新匹配返回匹配成功失败的结果我们常常优化对 3 和 4 步进行优化:使用具体字符组替代通配符,消除回溯如 /”[^”]"/ 代替 /".?"/。使用非捕获型分组当不需要使用分组引用和反向引用时,此时可以使用非捕获分组。如 /^[-]?(?:\d.\d+|\d+|.\d+)$/ 代替 /^[-]?(\d.\d+|\d+|.\d+)$/。独立出确定字符加快判断是否匹配失败,进而加快移位的速度。如 /aa/ 代替 /a+/。提取分支公共部分减少匹配过程中可消除的重复。如 /^(?:abc|def)/ 代替 /^abc|^def/。减少分支的数量,缩小它们的范围如 /rea?d/ 代替 /red|read/。七、正则表达式编程这里要掌握正则表达式怎么用,通常会有这么四个操作:验证切分提取替换1. 四种操作验证匹配本质上是查找,我们可以借助相关API操作:// 检查字符串是否包含数字let r = /\d/, s = “abc123”;!!s.search(r); // truer.test(s); // true!!s.match(r); // true!!r.exec(s); // true切分"leo,pingan".split(/,/); // [“leo”, “pingan”]let r = /\D/, s = “2019-03-16”;s.split(r); // [“2019”, “03”, “16”]s.split(r); // [“2019”, “03”, “16”]s.split(r); // [“2019”, “03”, “16”]提取// 提取日期年月日let r = /^(\d{4})\D(\d{2})\D(\d{2})$/;let s = “2019-03-16”;s.match(r); // [“2019-03-16”, “2019”, “03”, “16”, index: 0, input: “2019-03-16”]r.exec(s); // [“2019-03-16”, “2019”, “03”, “16”, index: 0, input: “2019-03-16”]r.test(s); // RegExp.$1 => “2019” RegExp.$2 => “03” RegExp.$3 => “16"s.search(r);// RegExp.$1 => “2019” RegExp.$2 => “03” RegExp.$3 => “16"替换// yyyy-mm-dd 替换成 yyyy/mm/dd"2019-03-16”.replace(/-/g, “/”);2. 相关API注意search 和 match 参数问题这两个方法会把字符串转换成正则,所以要加转义let s = “2019.03.16”;s.search(’.’); // 0s.search(’\.’); // 4s.search(/./); // 4s.match(’.’); // [“2”, index: 0, input: “2019.03.16”]s.match(’\.’); // [”.", index: 4, input: “2019.03.16”]s.match(/./); // [".", index: 4, input: “2019.03.16”]// 其他不用转义s.split(’.’);s.replace(’.’, ‘/’);match 返回结果的格式问题match 参数有 g 会返回所有匹配的内容,没有 g 则返回标准匹配格式:let s = “2019.03.16”;s.match(/\b(\d+)\b/); // [“2019”, “2019”, index: 0, input: “2019.03.16”]s.match(/\b(\d+)\b/g); // [“2019”, “03”, “16”]test 整体匹配时需要使用 ^ 和 $/123/.test(“a123b”); // true/^123$/.test(“a123b”); // false/^123$/.test(“123”); // truesplit 的注意点split 第二个参数是 结果数组的最大长度:“leo,pingan,pingan8787”.split(/,/, 2); // [“leo”, “pingan”]使用正则分组,会包含分隔符:“leo,pingan,pingan8787”.split(/(,)/); // [“leo”, “,”, “pingan”, “,”, “pingan8787”]修饰符修饰符描述g全局匹配,即找到所有匹配的,单词是 global。i忽略字母大小写,单词是 ingoreCase。m多行匹配,只影响 ^ 和 $,二者变成行的概念,即行开头和行结尾。单词是 multiline。文章到这结束,感谢阅读,也感谢老姚大佬的这本书Author王平安E-mailpingan8787@qq.com博 客www.pingan8787.com微 信pingan8787每日文章推荐https://github.com/pingan8787…ES小册js.pingan8787.com微信公众号 ...

March 16, 2019 · 5 min · jiezi

靓号等级生成器,正则+VBA代码

昨天收到一个需求,根据要求生成每个号码的等级(图片)下面是最后做出的效果下面是VBA代码’作者:梁茂业 2019年3月7日Sub replacePhone1() ‘定义起始行 START_ROW = 2 ‘定义等级 Dim Rng2 Dim level Dim level_1 Dim level_2 Dim level_3 level_1 = Array(1, 2, 1, 2, 3, 3, 4, 5, 4, 5, 7) level_2 = Array(0, 1, 1, 2, 3, 1, 2, 3, 3, 4, 5) level_3 = Array(0, 0, 1, 2, 3, 1, 2, 3, 3, 4, 5) Set regx = CreateObject(“vbscript.regexp”) regx.Global = True Set Rng = Range(“a3:a” & Cells(Rows.Count, 1).End(xlUp).Row) Rng2 = Sheet2.Range(“a2:g” & Sheet2.Cells(Rows.Count, 1).End(xlUp).Row) For Each rn In Rng n = n + 1 ‘基础匹配 是否是手机号 regx.Pattern = “^1\d{10}$” If regx.Test(rn.Value) Then ‘判断号码级别 level = level_3 regx.Pattern = “^(1380772|1380782|1390772|1390782|1980772|1987720|1987722|1987723|1987724|1987725|1987726|1987727|1987728|1987729)” ‘第一级 If regx.Test(rn.Value) Then level = level_1 End If regx.Pattern = “^(1350772|1360772|1387720|1387721|1387722|1387723|1387724|1387725|1387726|1387727|1387728|1387729|1387820|1387821|1387822|1387823|1387824|1387825|1387826|1387827|1387828|1387829|1397720|1397721|1397722|1397723|1397724|1397725|1397726|1397727|1397728|1397729|1397820|1397821|1397822|1397823|1397824|1397825|1397826|1397827|1397828|1397829|)” ‘第二级 If regx.Test(rn.Value) Then level = level_2 End If ‘判断局向 n2 = 0 For i = 1 To UBound(Rng2, 1) n2 = n2 + 1 n3 = Rng2(n2, 3) n4 = Rng2(n2, 4) If rn.Value >= Rng2(n2, 3) And rn.Value <= Rng2(n2, 4) Then Cells(n + START_ROW, 3) = Rng2(n2, 7) GoTo area End If Next area: ‘尾数顺位9位 regx.Pattern = “(?:0(?=1)|1(?=2)|2(?=3)|3(?=4)|4(?=5)|5(?=6)|6(?=7)|7(?=8)|8(?=9)){8}\d” If regx.Test(Right(rn.Value, 9)) Then Cells(n + START_ROW, 4) = “尾数顺位9位” Cells(n + START_ROW, 2) = “99” GoTo break End If ‘尾数顺位8位 regx.Pattern = “(?:0(?=1)|1(?=2)|2(?=3)|3(?=4)|4(?=5)|5(?=6)|6(?=7)|7(?=8)|8(?=9)){7}\d” If regx.Test(Right(rn.Value, 8)) Then Cells(n + START_ROW, 4) = “尾数顺位8位” Cells(n + START_ROW, 2) = “99” GoTo break End If ‘尾数顺位7位 regx.Pattern = “(?:0(?=1)|1(?=2)|2(?=3)|3(?=4)|4(?=5)|5(?=6)|6(?=7)|7(?=8)|8(?=9)){6}\d” If regx.Test(Right(rn.Value, 7)) Then Cells(n + START_ROW, 4) = “尾数顺位7位” Cells(n + START_ROW, 2) = “99” GoTo break End If ‘尾数连号9位 regx.Pattern = “([\d])\1{8,}” If regx.Test(Right(rn.Value, 9)) Then Cells(n + START_ROW, 4) = “尾数连号9位” Cells(n + START_ROW, 2) = “99” GoTo break End If ‘尾数连号8位 regx.Pattern = “([\d])\1{7,}” If regx.Test(Right(rn.Value, 8)) Then Cells(n + START_ROW, 4) = “尾数连号8位” Cells(n + START_ROW, 2) = “99” GoTo break End If ‘尾数连号7位 regx.Pattern = “([\d])\1{6,}” If regx.Test(Right(rn.Value, 7)) Then Cells(n + START_ROW, 4) = “尾数连号7位” Cells(n + START_ROW, 2) = “99” GoTo break End If ‘尾数连号6位 尾号6、8、9 regx.Pattern = “([6|8|9])\1{5}” If regx.Test(Right(rn.Value, 6)) Then Cells(n + START_ROW, 4) = “尾数连号6位 尾号6、8、9” Cells(n + START_ROW, 2) = “89” GoTo break End If ‘尾数连号6位 尾号非6、8、9 regx.Pattern = “([\d])\1{5,}” If regx.Test(Right(rn.Value, 6)) Then Cells(n + START_ROW, 4) = “尾数连号6位 尾号非6、8、9” Cells(n + START_ROW, 2) = “79” GoTo break End If ‘尾数顺位6位 regx.Pattern = “(?:0(?=1)|1(?=2)|2(?=3)|3(?=4)|4(?=5)|5(?=6)|6(?=7)|7(?=8)|8(?=9)){5}\d” If regx.Test(Right(rn.Value, 6)) Then Cells(n + START_ROW, 4) = “尾数顺位6位” Cells(n + START_ROW, 2) = “79” GoTo break End If ‘尾数连号5位 尾号6、8、9 regx.Pattern = “([6|8|9])\1{4}” If regx.Test(Right(rn.Value, 5)) Then Cells(n + START_ROW, 4) = “尾数连号5位 尾号6、8、9” Cells(n + START_ROW, 2) = “69” GoTo break End If ‘尾数连号6位 尾号非6、8、9 regx.Pattern = “([\d])\1{4,}” If regx.Test(Right(rn.Value, 5)) Then Cells(n + START_ROW, 4) = “尾数连号5位 尾号非6、8、9” Cells(n + START_ROW, 2) = “59” GoTo break End If ‘尾数顺位5位 regx.Pattern = “(?:0(?=1)|1(?=2)|2(?=3)|3(?=4)|4(?=5)|5(?=6)|6(?=7)|7(?=8)|8(?=9)){4}\d” If regx.Test(Right(rn.Value, 5)) Then Cells(n + START_ROW, 4) = “尾数顺位5位” Cells(n + START_ROW, 2) = “59” GoTo break End If ‘尾数连号4位 尾号6、8、9 regx.Pattern = “([6|8|9])\1{3}” If regx.Test(Right(rn.Value, 4)) Then Cells(n + START_ROW, 4) = “尾数连号4位 尾号6、8、9” Cells(n + START_ROW, 2) = “49” GoTo break End If ‘尾数连号4位 尾号非6、8、9 regx.Pattern = “([\d])\1{3,}” If regx.Test(Right(rn.Value, 4)) Then Cells(n + START_ROW, 4) = “尾数连号4位 尾号非6、8、9” Cells(n + START_ROW, 2) = “39” GoTo break End If ‘尾数顺位4位 regx.Pattern = “(?:0(?=1)|1(?=2)|2(?=3)|3(?=4)|4(?=5)|5(?=6)|6(?=7)|7(?=8)|8(?=9)){3}\d” If regx.Test(Right(rn.Value, 4)) Then Cells(n + START_ROW, 4) = “尾数顺位4位” Cells(n + START_ROW, 2) = “39” GoTo break End If ‘尾数连号3位 尾号6、8、9 regx.Pattern = “([6|8|9])\1{2}” If regx.Test(Right(rn.Value, 3)) Then Cells(n + START_ROW, 4) = “尾数连号3位 尾号6、8、9” Cells(n + START_ROW, 2) = “29” GoTo break End If ‘尾数连号3位 尾号非6、8、9 regx.Pattern = “([\d])\1{2,}” If regx.Test(Right(rn.Value, 3)) Then Cells(n + START_ROW, 4) = “尾数连号3位 尾号非6、8、9” Cells(n + START_ROW, 2) = “19” GoTo break End If ‘AABBCCDD AABBAABB regx.Pattern = “(.)\1{1}(.)\2{1}(.)\3{1}(.)\4{1}” If regx.Test(Right(rn.Value, 8)) Then Cells(n + START_ROW, 4) = “AABBCCDD AABBAABB” Cells(n + START_ROW, 2) = “7” GoTo break End If ‘中段5连号以上,且号码无4 regx.Pattern = “[0-35-9]+([0-35-9])\1{4}[0-35-9]*” If regx.Test(rn.Value) Then Cells(n + START_ROW, 4) = “中段5连号以上,且号码无4” Cells(n + START_ROW, 2) = “7” GoTo break End If ‘未4位和前4位一样 regx.Pattern = “([\d]{4})\1” If regx.Test(Right(rn.Value, 8)) Then Cells(n + START_ROW, 4) = “未4位和前4位一样” Cells(n + START_ROW, 2) = “6” GoTo break End If ‘尾号AABBCC C!=4 regx.Pattern = “([\d])\1{1}([\d])\2{1}([0-35-9])\3{1}” If regx.Test(Right(rn.Value, 6)) Then Cells(n + START_ROW, 4) = “尾号AABBCC C!=4” Cells(n + START_ROW, 2) = “6” GoTo break End If ‘尾数4位顺降 DCBA A!=4 regx.Pattern = “(?:9(?=8)|8(?=7)|7(?=6)|6(?=5)|5(?=4)|4(?=3)|3(?=2)|2(?=1)|1(?=0)){3}\d” If Right(rn.Value, 1) <> 4 Then If regx.Test(Right(rn.Value, 4)) Then Cells(n + START_ROW, 4) = “尾数4位顺降 DCBA A!=4” Cells(n + START_ROW, 2) = “6” GoTo break End If End If ‘============================================================== ‘尾数ABAB A或B等于4 regx.Pattern = “4” If regx.Test(Right(rn.Value, 4)) Then regx.Pattern = “(\d{2})\1” If regx.Test(Right(rn.Value, 4)) Then Cells(n + START_ROW, 4) = “尾数ABAB A或B等于4” Cells(n + START_ROW, 2) = level(8) GoTo break End If End If ‘尾数AABB A或B等于4 regx.Pattern = “4” If regx.Test(Right(rn.Value, 4)) Then regx.Pattern = “(\d)\1{1}(\d)\2{1}” If regx.Test(Right(rn.Value, 4)) Then Cells(n + START_ROW, 4) = “尾数AABB A或B等于4” Cells(n + START_ROW, 2) = level(8) GoTo break End If End If ‘尾数AAAAB A或B等于4 regx.Pattern = “4” If regx.Test(Right(rn.Value, 5)) Then regx.Pattern = “(\d)\1{3}\d+” If regx.Test(Right(rn.Value, 5)) Then Cells(n + START_ROW, 4) = “尾数AAAAB A或B等于4” Cells(n + START_ROW, 2) = level(8) GoTo break End If End If ‘尾数AAAB A或B等于4 regx.Pattern = “4” If regx.Test(Right(rn.Value, 4)) Then regx.Pattern = “(\d)\1{2}\d+” If regx.Test(Right(rn.Value, 4)) Then Cells(n + START_ROW, 4) = “尾数AAAB A或B等于4” Cells(n + START_ROW, 2) = level(8) GoTo break End If End If ‘============================================================== ‘尾数ABAB A或B=6 8 9 regx.Pattern = “6|8|9” If regx.Test(Right(rn.Value, 4)) Then regx.Pattern = “(\d{2})\1” If regx.Test(Right(rn.Value, 4)) Then Cells(n + START_ROW, 4) = “尾数ABAB A或B=6 8 9” Cells(n + START_ROW, 2) = level(10) GoTo break End If End If ‘尾数AABB A或B=6 8 9 regx.Pattern = “6|8|9” If regx.Test(Right(rn.Value, 4)) Then regx.Pattern = “(\d)\1{1}(\d)\2{1}” If regx.Test(Right(rn.Value, 4)) Then Cells(n + START_ROW, 4) = “尾数AABB A或B=6 8 9” Cells(n + START_ROW, 2) = level(10) GoTo break End If End If ‘尾数AAAAB A或B=6 8 9 regx.Pattern = “6|8|9” If regx.Test(Right(rn.Value, 5)) Then regx.Pattern = “(\d)\1{3}\d+” If regx.Test(Right(rn.Value, 5)) Then Cells(n + START_ROW, 4) = “尾数AAAAB A或B=6 8 9” Cells(n + START_ROW, 2) = level(10) GoTo break End If End If ‘尾数AAAB A或B=6 8 9 regx.Pattern = “6|8|9” If regx.Test(Right(rn.Value, 4)) Then regx.Pattern = “(\d)\1{2}\d+” If regx.Test(Right(rn.Value, 4)) Then Cells(n + START_ROW, 4) = “尾数AAAB A或B=6 8 9” Cells(n + START_ROW, 2) = level(0) GoTo break End If End If ‘============================================================ ‘尾数ABAB A或B不等于4 6 8 9 regx.Pattern = “\d” If regx.Test(Right(rn.Value, 4)) Then regx.Pattern = “(\d{2})\1” If regx.Test(Right(rn.Value, 4)) Then Cells(n + START_ROW, 4) = “尾数ABAB A或B不等于4 6 8 9 " Cells(n + START_ROW, 2) = level(9) GoTo break End If End If ‘尾数AABB A或B不等于4 6 8 9 regx.Pattern = “\d” If regx.Test(Right(rn.Value, 4)) Then regx.Pattern = “(\d)\1{1}(\d)\2{1}” If regx.Test(Right(rn.Value, 4)) Then Cells(n + START_ROW, 4) = “尾数AABB A或B不等于4 6 8 9 " Cells(n + START_ROW, 2) = level(9) GoTo break End If End If ‘尾数AAAAB A或B不等于4 6 8 9 regx.Pattern = “\d” If regx.Test(Right(rn.Value, 5)) Then regx.Pattern = “(\d)\1{3}\d+” If regx.Test(Right(rn.Value, 5)) Then Cells(n + START_ROW, 4) = “尾数AAAAB A或B不等于4 6 8 9 " Cells(n + START_ROW, 2) = level(9) GoTo break End If End If ‘尾数AAAB A或B不等于4 6 8 9 regx.Pattern = “\d” If regx.Test(Right(rn.Value, 4)) Then regx.Pattern = “(\d)\1{2}\d+” If regx.Test(Right(rn.Value, 4)) Then Cells(n + START_ROW, 4) = “尾数AAAB A或B不等于4 6 8 9 " Cells(n + START_ROW, 2) = level(9) GoTo break End If End If ‘尾号AA A= 4 regx.Pattern = “(4)\1” If regx.Test(Right(rn.Value, 2)) Then Cells(n + START_ROW, 4) = “尾号AA A=4” Cells(n + START_ROW, 2) = level(5) GoTo break End If ‘尾号AA A= 6 8 9 regx.Pattern = “([6|8|9])\1” If regx.Test(Right(rn.Value, 2)) Then Cells(n + START_ROW, 4) = “尾号AA A= 6 8 9” Cells(n + START_ROW, 2) = level(7) GoTo break End If ‘尾号AA A不等于 4 6 8 9 regx.Pattern = “(\d)\1” If regx.Test(Right(rn.Value, 2)) Then Cells(n + START_ROW, 4) = “尾号AA A不等于 4 6 8 9” Cells(n + START_ROW, 2) = level(6) GoTo break End If ‘尾数3位正顺号 ABC C不等于4 regx.Pattern = “(?:0(?=1)|1(?=2)|2(?=3)|3(?=4)|4(?=5)|5(?=6)|6(?=7)|7(?=8)|8(?=9)){2}\d” If Right(rn.Value, 1) <> 4 Then If regx.Test(Right(rn.Value, 3)) Then Cells(n + START_ROW, 4) = “尾数3位正顺号 ABC C不等于4” Cells(n + START_ROW, 2) = level(4) GoTo break End If End If ‘尾号末两位 18 58 68 98 regx.Pattern = “18|58|68|98” If regx.Test(Right(rn.Value, 2)) Then Cells(n + START_ROW, 4) = “尾号末两位 18 58 68 98” Cells(n + START_ROW, 2) = level(3) GoTo break End If ‘尾号一个8 regx.Pattern = “8” If regx.Test(Right(rn.Value, 1)) Then Cells(n + START_ROW, 4) = “尾号一个8” Cells(n + START_ROW, 2) = level(2) GoTo break End If ‘后四位带4 regx.Pattern = “4” If regx.Test(Right(rn.Value, 4)) Then Cells(n + START_ROW, 4) = “后四位带4” Cells(n + START_ROW, 2) = “100分” Cells(n + START_ROW, 2) = level(0) GoTo break End If ‘后四位不带4 regx.Pattern = “[0-35-9]” If regx.Test(Right(rn.Value, 4)) Then Cells(n + START_ROW, 4) = “后四位不带4” Cells(n + START_ROW, 2) = level(1) GoTo break End If Else Cells(n + START_ROW, 4) = “手机号格式不正确” Cells(n + START_ROW, 2) = “错误!!!” GoTo break End If break: NextEnd Sub ...

March 7, 2019 · 7 min · jiezi

基于 NFA 实现正则表达式引擎

回想起第一次看到正则表达式的时候,眼睛里大概都是 $7^(0^=]W-^*d+,心里我是拒绝的。不过在后面的日常工作里,越来越多地开始使用到正则表达式,正则表达式也逐渐成为一个很常用的工具。要掌握一种工具除了了解它的用法,了解它的原理也是同样重要的,一般来说,正则引擎可以粗略地分为两类:DFA(Deterministic Finite Automata)确定性有穷自动机和 NFA (Nondeterministic Finite Automata)不确定性有穷自动机。使用 NFA 的工具包括 .NET、PHP、Ruby、Perl、Python、GNU Emacs、ed、sec、vi、grep 的多数版本,甚至还有某些版本的 egrep 和 awk。而采用 DFA 的工具主要有 egrep、awk、lex 和 flex。也有些系统采用了混合引擎,它们会根据任务的不同选择合适的引擎(甚至对同一表达式中的不同部分采用不同的引擎,以求得功能与速度之间的最佳平衡)。—— Jeffrey E.F. Friedl《精通正则表达式》DFA 与 NFA 都称为有穷自动机,两者有很多相似的地方,自动机本质上是与状态转换图类似的图。(注:本文不会严格给自动机下定义,深入理解自动机可以阅读《自动机理论、语言和计算导论》。)NFA一个 NFA 分为以下几个部分:一个初始状态一个或多个终结状态状态转移函数上图是一个具有两个状态 q0 和 q1 的 NFA,初始状态为 q0(没有前序状态),终结状态为 q1(两层圆圈标识)。在 q0 上有一根箭头指向 q1,这代表当 NFA 处在 q0 状态时,接受输入 a,会转移到状态 q1。当要接受一个串时,我们会将 NFA 初始化为初始状态,然后根据输入来进行状态转移,如果输入结束后 NFA 处在结束状态,那就意味着接受成功,如果输入的符号没有对应的状态转移,或输入结束后 NFA 没有处在结束状态,则意味着接受失败。由上可知这个 NFA 能接受且仅能接受字符串 a。那为什么叫做 NFA 呢,因为 对于同一个状态与同一个输入符号,NFA 可以到达不同的状态,如下图:在 q0 上时,当输入为 a,该 NFA 可以继续回到 q0 或者到达 q1,所以该 NFA 可以接受 abb(q0 -> q1 -> q2 -> q3),也可以接受 aabb(q0 -> q0 -> q1 -> q2 -> q3),同样接受 ababb、aaabbbabababb 等等,你可能已经发现了,这个 NFA 表示的正则表达式正是 (a|b)abb-NFA除了能到达多个状态之外,NFA 还能接受空符号 ,如下图:这是一个接受 (a+|b+) 的 NFA,因为存在路径 q0 –> q1 -a-> q2 -a-> q2, 代表空串,在连接时移除,所以这个路径即代表接受 aa。你可能会觉得为什么不直接使用 q0 通过 a 连接 q2,通过 b 连接到 q4,这是因为 主要起连接的作用,介绍到后面会感受到这点。DFA介绍完了不确定性有穷自动机,确定性有穷自动机就容易理解了,DFA 和 NFA 的不同之处就在于:没有 转移对于同一状态和同一输入,只会有一个转移那么 DFA 要比 NFA 简单地多,为什么不直接使用 DFA 实现呢?这是因为对于正则语言的描述,构造 NFA 往往要比构造 DFA 容易得多,比如上文提到的 (a|b)abb,NFA 很容易构造和理解:但直接构造与之对应的 DFA 就没那么容易了,你可以先尝试构造一下,结果大概就是这样:所以 NFA 容易构造,但是因为其不确定性很难用程序实现状态转移逻辑;NFA 不容易构造,但是因为其确定性很容易用程序来实现状态转移逻辑,怎么办呢?神奇的是每一个 NFA 都有对应的 DFA,所以我们一般会先根据正则表达式构建 NFA,然后可以转化成对应的 DFA,最后进行识别。McMaughton-Yamada-Thompson 算法McMaughton-Yamada-Thompson 算法可以将任何正则表达式转变为接受相同语言的 NFA。它分为两个规则:基本规则对于表达式 ,构造下面的 NFA:对于非 ,构造下面的 NFA:归纳规则假设正则表达式 s 和 t 的 NFA 分别为 N(s) 和 N(t),那么对于一个新的正则表达式 r,则如下构造 N(r):并当 r = s|t,N(r) 为连接当 r = st,N(r) 为闭包当 r = s,N(r) 为其他的 +,? 等限定符可以类似实现。本文所需关于自动机的知识到此就结束了,接下来就可以开始构建 NFA 了。基于 NFA 实现1968 年 Ken Thompson 发表了一篇论文 Regular Expression Search Algorithm,在这篇文章里,他描述了一种正则表达式编译器,并催生出了后来的 qed、ed、grep 和 egrep。论文相对来说比较难懂,implementing-a-regular-expression-engine 这篇文章同样也是借鉴 Thompson 的论文进行实现,本文一定程度也参考了该文章的实现思路。添加连接符在构建 NFA 之前,我们需要对正则表达式进行处理,以 (a|b)abb 为例,在正则表达式里是没有连接符号的,那我们就没法知道要连接哪两个 NFA 了。所以首先我们需要显式地给表达式添加连接符,比如 ·,可以列出添加规则:左边符号 / 右边符号()并字母❌✅❌❌✅(❌❌❌❌❌)❌✅❌❌✅并❌❌❌❌❌字母❌✅❌❌✅(a|b)abb 添加完则为 (a|b)·a·b·b,实现如下:中缀表达式转后缀表达式如果你写过计算器应该知道,中缀表达式不利于分析运算符的优先级,在这里也是一样,我们需要将表达式从中缀表达式转为后缀表达式。在本文的具体过程如下:如果遇到字母,将其输出。如果遇到左括号,将其入栈。如果遇到右括号,将栈元素弹出并输出直到遇到左括号为止。左括号只弹出不输出。如果遇到限定符,依次弹出栈顶优先级大于或等于该限定符的限定符,然后将其入栈。如果读到了输入的末尾,则将栈中所有元素依次弹出。在本文实现范围中,优先级从小到大分别为连接符 ·闭包 并 |实现如下:如 (a|b)·c 转为后缀表达式 ab|*c·构建 NFA由后缀表达式构建 NFA 就容易多了,从左到右读入表达式内容:如果为字母 s,构建基本 NFA N(s),并将其入栈如果为 |,弹出栈内两个元素 N(s)、N(t),构建 N(r) 将其入栈(r = s|t)如果为 ·,弹出栈内两个元素 N(s)、N(t),构建 N(r) 将其入栈(r = st)如果为 ,弹出栈内一个元素 N(s),构建 N(r) 将其入栈(r = s)代码见 automata.ts构建 DFA有了 NFA 之后,可以将其转为 DFA。NFA 转 DFA 的方法可以使用 子集构造法,NFA 构建出的 DFA 的每一个状态,都是包含原始 NFA 多个状态的一个集合,比如原始 NFA 为这里我们需要使用到一个操作 -closure(s),这个操作代表能够从 NFA 的状态 s 开始只通过 转换到达的 NFA 的状态集合,比如 -closure(q0) = {q0, q1, q3},我们把这个集合作为 DFA 的开始状态 A。那么 A 状态有哪些转换呢?A 集合里有 q1 可以接受 a,有 q3 可以接受 b,所以 A 也能接受 a 和 b。当 A 接受 a 时,得到 q2, 那么 -closure(q2) 则作为 A 状态接受 a 后到达的状态 B。同理,A 状态接受 b 后到达的 -closure(q4) 为状态 C。而状态 B 还可以接受 a,到达的同样是 -closure(q2),那我们说状态 B 接受 a 还是到达了状态 B。同样,状态 C 接受 b 也会回到状态 C。这样,构造出的 DFA 为DFA 的开始状态即包含 NFA 开始状态的状态,终止状态亦是如此。搜索其实我们并不用显式构建 DFA,而是用这种思想去遍历 NFA,这本质上是一个图的搜索,实现代码如下:getClosure 代码如下:总结总的来说,基于 NFA 实现简单的正则表达式引擎,我们一共经过了这么几步:添加连接符转换为后缀表达式构建 NFA判断 NFA 是否接受输入串完整代码见 github ...

February 23, 2019 · 2 min · jiezi

Golang 正则表达式(regexp)

Go内置了(regexp包)对正则表达式的支持,这里是一般的正则表达式常规用法的例子。示例: package mainimport ( “bytes” “fmt” “regexp”)func main() { //是否匹配字符串 // .匹配任意一个字符 ,匹配零个或多个 ,优先匹配更多(贪婪) match, _ := regexp.MatchString(“H(.)d!”, “Hello World!”) fmt.Println(match) //true //或 match, _ = regexp.Match(“H(.)d!”, []byte(“Hello World!”)) fmt.Println(match) //true //或通过Compile来使用一个优化过的正则对象 r, _ := regexp.Compile(“H(.)d!”) fmt.Println(r.MatchString(“Hello World!”)) //true // 这个方法返回匹配的子串 fmt.Println(r.FindString(“Hello World! world”)) //Hello World! //同上 fmt.Println(string(r.Find([]byte(“Hello World!”)))) //Hello World! // 这个方法查找第一次匹配的索引 // 的起始索引和结束索引,而不是匹配的字符串 fmt.Println(r.FindStringIndex(“Hello World! world”)) //[0 12] // 这个方法返回全局匹配的字符串和局部匹配的字符,比如 // 这里会返回匹配H(.*)d!的字符串 // 和匹配(.*)的字符串 fmt.Println(r.FindStringSubmatch(“Hello World! world”)) //[Hello World! ello Worl] // 和上面的方法一样,不同的是返回全局匹配和局部匹配的 // 起始索引和结束索引 fmt.Println(r.FindStringSubmatchIndex(“Hello World! world”)) //[0 12 1 10] // 这个方法返回所有正则匹配的字符,不仅仅是第一个 fmt.Println(r.FindAllString(“Hello World! Held! world”, -1)) //[Hello World! Held!] // 这个方法返回所有全局匹配和局部匹配的字符串起始索引,只匹配最大的串 // 和结束索引 fmt.Println(r.FindAllStringSubmatchIndex(“Hello World! world”, -1)) //[[0 12 1 10]] fmt.Println(r.FindAllStringSubmatchIndex(“Hello World! Held! world”, -1)) //[[0 18 1 16]] // 为这个方法提供一个正整数参数来限制匹配数量 res, _ := regexp.Compile(“H([a-z]+)d!”) fmt.Println(res.FindAllString(“Hello World! Held! Hellowrld! world”, 2)) //[Held! Hellowrld!] fmt.Println(r.FindAllString(“Hello World! Held! world”, 2)) //[Hello World! Held!] //注意上面两个不同,第二参数是一最大子串为单位计算。 // regexp包也可以用来将字符串的一部分替换为其他的值 fmt.Println(r.ReplaceAllString(“Hello World! Held! world”, “html”)) //html world // Func变量可以让你将所有匹配的字符串都经过该函数处理 // 转变为所需要的值 in := []byte(“Hello World! Held! world”) out := r.ReplaceAllFunc(in, bytes.ToUpper) fmt.Println(string(out)) // 在 b 中查找 reg 中编译好的正则表达式,并返回第一个匹配的位置 // {起始位置, 结束位置} b := bytes.NewReader([]byte(“Hello World!”)) reg := regexp.MustCompile(\w+) fmt.Println(reg.FindReaderIndex(b)) //[0 5] // 在 字符串 中查找 r 中编译好的正则表达式,并返回所有匹配的位置 // {{起始位置, 结束位置}, {起始位置, 结束位置}, …} // 只查找前 n 个匹配项,如果 n < 0,则查找所有匹配项 fmt.Println(r.FindAllIndex([]byte(“Hello World!”), -1)) //[[0 12]] //同上 fmt.Println(r.FindAllStringIndex(“Hello World!”, -1)) //[[0 12]] // 在 s 中查找 re 中编译好的正则表达式,并返回所有匹配的内容 // 同时返回子表达式匹配的内容 // { // {完整匹配项, 子匹配项, 子匹配项, …}, // {完整匹配项, 子匹配项, 子匹配项, …}, // … // } // 只查找前 n 个匹配项,如果 n < 0,则查找所有匹配项 reg = regexp.MustCompile((\w)(\w)+) //[[Hello H o] [World W d]] fmt.Println(reg.FindAllStringSubmatch(“Hello World!”, -1)) //[[Hello H o] [World W d]] // 将 template 的内容经过处理后,追加到 dst 的尾部。 // template 中要有 $1、$2、${name1}、${name2} 这样的“分组引用符” // match 是由 FindSubmatchIndex 方法返回的结果,里面存放了各个分组的位置信息 // 如果 template 中有“分组引用符”,则以 match 为标准, // 在 src 中取出相应的子串,替换掉 template 中的 $1、$2 等引用符号。 reg = regexp.MustCompile((\w+),(\w+)) src := []byte(“Golang,World!”) // 源文本 dst := []byte(“Say: “) // 目标文本 template := []byte(“Hello $1, Hello $2”) // 模板 m := reg.FindSubmatchIndex(src) // 解析源文本 // 填写模板,并将模板追加到目标文本中 fmt.Printf("%q”, reg.Expand(dst, template, src, m)) // “Say: Hello Golang, Hello World” // LiteralPrefix 返回所有匹配项都共同拥有的前缀(去除可变元素) // prefix:共同拥有的前缀 // complete:如果 prefix 就是正则表达式本身,则返回 true,否则返回 false reg = regexp.MustCompile(Hello[\w\s]+) fmt.Println(reg.LiteralPrefix()) // Hello false reg = regexp.MustCompile(Hello) fmt.Println(reg.LiteralPrefix()) // Hello true text := Hello World! hello world // 正则标记“非贪婪模式”(?U) reg = regexp.MustCompile((?U)H[\w\s]+o) fmt.Printf("%q\n”, reg.FindString(text)) // Hello // 切换到“贪婪模式” reg.Longest() fmt.Printf("%q\n", reg.FindString(text)) // Hello Wo // 统计正则表达式中的分组个数(不包括“非捕获的分组”) fmt.Println(r.NumSubexp()) //1 //返回 r 中的“正则表达式”字符串 fmt.Printf("%s\n", r.String()) // 在 字符串 中搜索匹配项,并以匹配项为分割符,将 字符串 分割成多个子串 // 最多分割出 n 个子串,第 n 个子串不再进行分割 // 如果 n < 0,则分割所有子串 // 返回分割后的子串列表 fmt.Printf("%q\n", r.Split(“Hello World! Helld! hello”, -1)) //["" " hello"] // 在 字符串 中搜索匹配项,并替换为 repl 指定的内容 // 如果 rep 中有“分组引用符”($1、$name),则将“分组引用符”当普通字符处理 // 全部替换,并返回替换后的结果 s := “Hello World, hello!” reg = regexp.MustCompile((Hell|h)o) rep := “${1}” fmt.Printf("%q\n", reg.ReplaceAllLiteralString(s, rep)) //"${1} World, hello!" // 在 字符串 中搜索匹配项,然后将匹配的内容经过 repl 处理后,替换 字符串 中的匹配项 // 如果 repb 的返回值中有“分组引用符”($1、$name),则将“分组引用符”当普通字符处理 // 全部替换,并返回替换后的结果 ss := []byte(“Hello World!”) reg = regexp.MustCompile("(H)ello") repb := []byte("$0$1") fmt.Printf("%s\n", reg.ReplaceAll(ss, repb)) // HelloH World! fmt.Printf("%s\n", reg.ReplaceAllFunc(ss, func(b []byte) []byte { rst := []byte{} rst = append(rst, b…) rst = append(rst, “$1”…) return rst })) // Hello$1 World!}小结:1、r, _ := regexp.Compile(“H(.)d!")可用一下代替r := regexp.MustCompile(“H(.)d!")两者区别 MustCompile 少一个返回值err看源码// Compile parses a regular expression and returns, if successful,// a Regexp object that can be used to match against text.//…// For POSIX leftmost-longest matching, see CompilePOSIX.func Compile(expr string) (*Regexp, error) { return compile(expr, syntax.Perl, false)}// MustCompile is like Compile but panics if the expression cannot be parsed.// It simplifies safe initialization of global variables holding compiled regular// expressions.func MustCompile(str string) *Regexp { regexp, err := Compile(str) if err != nil { panic(regexp: Compile( + quote(str) + ): + err.Error()) } return regexp}2、regexp的处理byte的方法都有个string方法对应,两者功能一样。例如:regexp.Match()和regexp.MatchString()links目录 ...

February 22, 2019 · 3 min · jiezi

re模块与正则表达式

正则表达式简介正则表达式(英语:Regular Expression,在代码中常简写为regex、regexp或RE),又称正规表示式、正规表示法、正规表达式、规则表达式、常规表示法,是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些匹配某个模式的文本。正则表达式规则,单字符匹配字符功能正则表达式示例符合匹配示例.匹配任意一个字符(除了n)b.bbab,b2b[ ]匹配来自[]中字符集的任意一个字符i [abCde]mi am\d匹配任何十进制数字,与[0-9]一致w\dcschoolw3cschool\D匹配非数字,即不是数字mou\Dhmouth\s匹配任何空格字符,与[\n\t\r\v\f]相同i\slikei like\S匹配任何非空白字符,与\s相反n\Senoe,n3e\w匹配任何字母数字字符,与[A-Za-z0-9_]相同[A-Za-z]w+\W匹配非单词字符[0-9]\W[A-Z]3 A表示数量匹配字符功能正则表达式示例符合匹配的示例匹配0次或者多次前面出现的正则表达式,即可有可无aaaa+匹配前一个字符出现1次或者无限次,即至少有一次a+aaa?匹配前一个字符出现1次或者0次,要么有一次,要么没有a?a或者b {m}匹配前一个字符出现m次[0-9]{5}12345{m.}匹配前一个字符至少出现m次a{5.}aaaaa{m,n}匹配前一个字符出现从m到n次a{2,6}aaa表示边界匹配字符功能正则表达式示例^匹配字符串起始部分^Dear$匹配字符串终止部分fi$b匹配任何单词的边界\bThe\bB匹配非单词边界.*\Bver\匹配分组字符功能 \匹配左右任意一个表达式(ab)将括号中字符作为一个分组\num引用分组num匹配到的字符串(?P<name>)分组起别名(?P=name)引用别名为name分组匹配到的字符串re模块在python中,可以使用内置的re模块来使用正则表达式re模块常见的函数和方法核心函数说明compile(pattern,flags=0)使用任何可选的标记来编译正则表达式的模式,然后返回一个正则表达式对象re模块函数和正则表达式对象方法说明match(pattern,string,flags=0)尝试使用带有可选的标记的正则表达式的模式来匹配字符串。如果匹配成功,就返回匹配对象; 如果失败,就返回 Nonesearch(pattern,string,flags=0)使用可选标记搜索字符串中第一次出现的正则表达式模式。 如果匹配成功,则返回匹配对象; 如果失败,则返回 Nonefindall(pattern,string,[,flags])查找字符串中所有出现的正则表达式,并返回一个列表split(pattern,string,max=0)根据正则表达式的模式分隔符,spilt函数将字符串分割为列表,然后返回成功匹配的列表,分割最多操作max次(默认分割所有匹配成功的位置)sub(pattern,repl,string,count=0)使用repl替换所有正则表达式的模式在字符串中出现的位置,除非定义count,否则就将替换所有出现的位置常用的匹配对象方法说明group(num=0)默认返回整个匹配对象或者返回编号为num的特定子组groups(default=None)返回一个包含所有匹配子组的元组,如果没有成功匹配,返回一个空元组span() 常用的模块属性,大多数用于对正则表达式函数的修饰说明re.I使匹配对大小写不敏感(忽略大小写)re.S.(点号)匹配除了n之外的所有字符,re.S标记表示.(点号)能够匹配全部字符re.M多行匹配,影响^和$re.U根据Unicode字符集解析字符。影响\w,\W,\b和\Bre.X该标志通过给予你更灵活的格式以便你讲正则表达式写得更易于理解re模块一般的使用方法使用 compile() 函数将正则表达式的字符串形式编译为一个正则表达式对象;通过正则表达式对象提供的一系列方法(如:match())对文本进行匹配查找,获得匹配结果,一个Match对象;最后使用Match对象提供的属性和方法(例如:group())获得信息,根据需要进行其他的操作。re模块使用示例导入模块import recompile()函数compile 函数用于编译正则表达式,生成一个 Pattern 对象,它的一般使用形式如下:import re# 将正则表达式编译成pattern对象pattern = re.compile(r’\d+’)编译成正则表达式对象后,就可以使用上面所说的正则表达式对象方法了。match()方法match 方法用于查找字符串的头部(也可以指定起始位置),它是一次匹配,只要找到了一个匹配的结果就返回,而不是查找所有匹配的结果。它的一般使用形式如下:match(string[, pos[, endpos]])其中,string是待匹配的字符串,pos 和 endpos 是可选参数,指定字符串的起始和终点位置,默认值分别是 0 和 len (字符串长度)。因此,当你不指定 pos 和 endpos 时,match 方法默认匹配字符串的头部。当匹配成功时,返回一个 Match 对象,如果没有匹配上,则返回 None。>>> import re>>> >>> pattern = re.compile(r’\d+’) # 正则表达式表示匹配至少一个数字>>> >>> m = pattern.match(“one2three4”) # match默认从开头开始匹配,开头是字母o,所以没有匹配成功>>> print(m) # 匹配失败返回NoneNone>>> >>> m = pattern.match(“1two3four”) # 开头字符是数字,匹配成功>>> print(m)<_sre.SRE_Match object; span=(0, 1), match=‘1’>>>> >>> m.group() # group()方法获取匹配成功的字符'1’>>> m = pattern.match(“onetwo3four56”,6,12) # 指定match从数字3开始查找,第一个是数字3,匹配成功>>> print(m)<_sre.SRE_Match object; span=(6, 7), match=‘3’>>>> m.group()‘3’ ...

February 20, 2019 · 1 min · jiezi

正则表达式在 ES2018 中的新写法

翻译:疯狂的技术宅原文:https://www.smashingmagazine….本文首发微信公众号:jingchengyideng欢迎关注,每天都给你推送新鲜的前端技术文章摘要:如果你曾用 JavaScript 做过复杂的文本处理和操作,那么你将会对 ES2018 中引入的新功能爱不释手。 在本文中,我们将详细介绍第 9 版标准如何提高 JavaScript 的文本处理能力。有一个很好的理由能够解释为什么大多数编程语言都支持正则表达式:它们是用于处理文本的极其强大的工具。 通常一行正则表达式代码就能完成需要几十行代码才能搞定的文本处理任务。 虽然大多数语言中的内置函数足以对字符串进行一般的搜索和替换操作,但更加复杂的操作(例如验证文本输入)通常需要使用正则表达式。自从 1999 年推出 ECMAScript 标准第 3 版以来,正则表达式已成为 JavaScript 语言的一部分。ECMAScript 2018(简称ES2018)是该标准的第 9 版,通过引入四个新功能进一步提高了JavaScript的文本处理能力:后行断言命名捕获组s (dotAll) flagUnicode属性转义下面详细介绍这些新功能。后行断言能够根据之后或之前的内容匹配一系列字符,使你可以丢弃可能不需要的匹配。 当你需要处理大字符串并且意外匹配的可能性很高时,这个功能非常有用。 幸运的是,大多数正则表达式都为此提供了 lookbehind 和 lookahead 断言。在 ES2018 之前,JavaScript 中只提供了先行断言。 lookahead 允许你在一个断言模式后紧跟另一个模式。先行断言有两种版本:正向和负向。 正向先行断言的语法是 (?=…)。 例如,正则表达式 /Item(?= 10)/ 仅在后面跟随有一个空格和数字 10 的时候才与 Item 匹配:const re = /Item(?= 10)/;console.log(re.exec(‘Item’));// → nullconsole.log(re.exec(‘Item5’));// → nullconsole.log(re.exec(‘Item 5’));// → nullconsole.log(re.exec(‘Item 10’));// → [“Item”, index: 0, input: “Item 10”, groups: undefined]此代码使用 exec() 方法在字符串中搜索匹配项。 如果找到匹配项, exec() 将返回一个数组,其中第一个元素是匹配的字符串。 数组的 index 属性保存匹配字符串的索引, input 属性保存搜索执行的整个字符串。 最后,如果在正则表达式中使用了命名捕获组,则将它们放在 groups 属性中。 在代码中, groups 的值为 undefined ,因为没有被命名的捕获组。负向先行的构造是 (?!…) 。 负向先行断言的模式后面没有特定的模式。 例如, /Red(?!head)/ 仅在其后不跟随 head 时匹配 Red :const re = /Red(?!head)/;console.log(re.exec(‘Redhead’));// → nullconsole.log(re.exec(‘Redberry’));// → [“Red”, index: 0, input: “Redberry”, groups: undefined]console.log(re.exec(‘Redjay’));// → [“Red”, index: 0, input: “Redjay”, groups: undefined]console.log(re.exec(‘Red’));// → [“Red”, index: 0, input: “Red”, groups: undefined]ES2018 为 JavaScript 补充了后行断言。 用 (?<=…) 表示,后行断言允许你在一个模式前面存在另一个模式时进行匹配。假设你需要以欧元检索产品的价格但是不捕获欧元符号。 通过后行断言,会使这项任务变得更加简单:const re = /(?<=)\d+(.\d*)?/;console.log(re.exec(‘199’));// → nullconsole.log(re.exec(’$199’));// → nullconsole.log(re.exec(‘199’));// → [“199”, undefined, index: 1, input: “199”, groups: undefined]注意:先行(Lookahead)和后行(lookbehind)断言通常被称为“环视”(lookarounds)。后行断言的反向版本由 (?<!…) 表示,使你能够匹配不在lookbehind中指定的模式之前的模式。 例如,正则表达式 /(?<!\d{3}) meters/ 会在 三个数字不在它之前 匹配单词“meters”如果:const re = /(?<!\d{3}) meters/;console.log(re.exec(‘10 meters’));// → [" meters", index: 2, input: “10 meters”, groups: undefined]console.log(re.exec(‘100 meters’)); // → null与前行断言一样,你可以连续使用多个后行断言(负向或正向)来创建更复杂的模式。下面是一个例子:const re = /(?<=\d{2})(?<!35) meters/;console.log(re.exec(‘35 meters’));// → nullconsole.log(re.exec(‘meters’));// → nullconsole.log(re.exec(‘4 meters’));// → nullconsole.log(re.exec(‘14 meters’));// → [“meters”, index: 2, input: “14 meters”, groups: undefined]此正则表达式仅匹配包含“meters”的字符串,如果它前面紧跟 35 之外的任何两个数字。正向后行确保模式前面有两个数字,同时负向后行能够确保该数字不是 35。命名捕获组你可以通过将字符封装在括号中的方式对正则表达式的一部分进行分组。 这可以允许你将规则限制为模式的一部分或在整个组中应用量词。 此外你可以通过括号来提取匹配值并进行进一步处理。下列代码给出了如何在字符串中查找带有 .jpg 并提取文件名的示例:const re = /(\w+).jpg/;const str = ‘File name: cat.jpg’;const match = re.exec(str);const fileName = match[1];// The second element in the resulting array holds the portion of the string that parentheses matchedconsole.log(match);// → [“cat.jpg”, “cat”, index: 11, input: “File name: cat.jpg”, groups: undefined]console.log(fileName);// → cat在更复杂的模式中,使用数字引用组只会使本身就已经很神秘的正则表达式的语法更加混乱。 例如,假设你要匹配日期。 由于在某些国家和地区会交换日期和月份的位置,因此会弄不清楚究竟哪个组指的是月份,哪个组指的是日期:const re = /(\d{4})-(\d{2})-(\d{2})/;const match = re.exec(‘2020-03-04’);console.log(match[0]); // → 2020-03-04console.log(match[1]); // → 2020console.log(match[2]); // → 03console.log(match[3]); // → 04ES2018针对此问题的解决方案名为捕获组,它使用更具表现力的 (?<name>…) 形式的语法:const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;const match = re.exec(‘2020-03-04’);console.log(match.groups); // → {year: “2020”, month: “03”, day: “04”}console.log(match.groups.year); // → 2020console.log(match.groups.month); // → 03console.log(match.groups.day); // → 04因为生成的对象可能会包含与命名组同名的属性,所以所有命名组都在名为 groups 的单独对象下定义。许多新的和传统的编程语言中都存在类似的结构。 例如Python对命名组使用 (?P<name>) 语法。 Perl支持与 JavaScript 相同语法的命名组( JavaScript 已经模仿了 Perl 的正则表达式语法)。 Java也使用与Perl相同的语法。除了能够通过 groups 对象访问命名组之外,你还可以用编号引用访问组—— 类似于常规捕获组:const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;const match = re.exec(‘2020-03-04’);console.log(match[0]); // → 2020-03-04console.log(match[1]); // → 2020console.log(match[2]); // → 03console.log(match[3]); // → 04新语法也适用于解构赋值:const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;const [match, year, month, day] = re.exec(‘2020-03-04’);console.log(match); // → 2020-03-04console.log(year); // → 2020console.log(month); // → 03console.log(day); // → 04即使正则表达式中不存在命名组,也始终创建 groups 对象:const re = /\d+/;const match = re.exec(‘123’);console.log(‘groups’ in match); // → true如果可选的命名组不参与匹配,则 groups 对象仍将具有命名组的属性,但该属性的值为 undefined:const re = /\d+(?<ordinal>st|nd|rd|th)?/;let match = re.exec(‘2nd’);console.log(‘ordinal’ in match.groups); // → trueconsole.log(match.groups.ordinal); // → ndmatch = re.exec(‘2’);console.log(‘ordinal’ in match.groups); // → trueconsole.log(match.groups.ordinal); // → undefined你可以稍后在模式中引用常规捕获的组,并使用 \1 的形式进行反向引用。 例如以下代码使用在行中匹配两个字母的捕获组,然后在模式中调用它:console.log(/(\w\w)\1/.test(‘abab’)); // → true// if the last two letters are not the same // as the first two, the match will failconsole.log(/(\w\w)\1/.test(‘abcd’)); // → false要在模式中稍后调用命名捕获组,可以使用 /\k<name>/ 语法。 下面是一个例子:const re = /\b(?<dup>\w+)\s+\k<dup>\b/;const match = re.exec(“I’m not lazy, I’m on on energy saving mode”); console.log(match.index); // → 18console.log(match[0]); // → on on此正则表达式在句子中查找连续的重复单词。 如果你愿意,还可以用带编号的后引用来调用命名的捕获组:const re = /\b(?<dup>\w+)\s+\1\b/;const match = re.exec(“I’m not lazy, I’m on on energy saving mode”); console.log(match.index); // → 18console.log(match[0]); // → on on 也可以同时使用带编号的后引用和命名后向引用:const re = /(?<digit>\d):\1:\k<digit>/;const match = re.exec(‘5:5:5’); console.log(match[0]); // → 5:5:5与编号的捕获组类似,可以将命名的捕获组插入到 replace() 方法的替换值中。 为此,你需要用到 $<name> 构造。 例如:const str = ‘War & Peace’;console.log(str.replace(/(War) & (Peace)/, ‘$2 & $1’)); // → Peace & Warconsole.log(str.replace(/(?<War>War) & (?<Peace>Peace)/, ‘$<Peace> & $<War>’)); // → Peace & War如果要使用函数执行替换,则可以引用命名组,方法与引用编号组的方式相同。 第一个捕获组的值将作为函数的第二个参数提供,第二个捕获组的值将作为第三个参数提供:const str = ‘War & Peace’;const result = str.replace(/(?<War>War) & (?<Peace>Peace)/, function(match, group1, group2, offset, string) { return group2 + ’ & ’ + group1;});console.log(result); // → Peace & Wars (dotAll) Flag默认情况下,正则表达式模式中的点 (.) 元字符匹配除换行符 (\n) 和回车符 (\r)之外的所有字符:console.log(/./.test(’\n’)); // → falseconsole.log(/./.test(’\r’)); // → false尽管有这个缺点,JavaScript 开发者仍然可以通过使用两个相反的速记字符类来匹配所有字符,例如[ w W],它告诉正则表达式引擎匹配一个字符(\w)或非单词字符(\W):console.log(/[\w\W]/.test(’\n’)); // → trueconsole.log(/[\w\W]/.test(’\r’)); // → trueES2018旨在通过引入 s (dotAll) 标志来解决这个问题。 设置此标志后,它会更改点 (.)元字符的行为以匹配换行符:console.log(/./s.test(’\n’)); // → trueconsole.log(/./s.test(’\r’)); // → trues 标志可以在每个正则表达式的基础上使用,因此不会破坏依赖于点元字符的旧行为的现有模式。 除了 JavaScript 之外, s 标志还可用于许多其他语言,如 Perl 和 PHP。Unicode 属性转义ES2015中引入的新功能包括Unicode感知。 但是即使设置了 u 标志,速记字符类仍然无法匹配Unicode字符。请考虑以下案例:const str = ‘????’;console.log(/\d/.test(str)); // → falseconsole.log(/\d/u.test(str)); // → false????被认为是一个数字,但 \d 只能匹配ASCII [0-9],因此 test() 方法返回 false。 因为改变速记字符类的行为会破坏现有的正则表达式模式,所以决定引入一种新类型的转义序列。在ES2018中,当设置 u 标志时,Unicode属性转义(由 \p{…} 表示)在正则表达式中可用。 现在要匹配任何Unicode 数字,你只需使用 \p{Number},如下所示:const str = ‘????’;console.log(/\p{Number}/u.test(str)); // → true要匹配 Unicode 字符,你可以使用\p{Alphabetic}:const str = ‘漢’;console.log(/\p{Alphabetic}/u.test(str)); // → true// the \w shorthand cannot match 漢console.log(/\w/u.test(str)); // → false\P{…} 是 \p{…} 的否定版本,并匹配 \p{…} 没有的所有字符:console.log(/\P{Number}/u.test(’????’)); // → falseconsole.log(/\P{Number}/u.test(‘漢’)); // → trueconsole.log(/\P{Alphabetic}/u.test(’????’)); // → trueconsole.log(/\P{Alphabetic}/u.test(‘漢’)); // → false当前规范提案中提供了受支持属性的完整列表。请注意,使用不受支持的属性会导致 SyntaxError:console.log(/\p{undefined}/u.test(‘漢’)); // → SyntaxError兼容性列表桌面浏览器 ChromeFirefoxSafariEdge后行断言62XXX命名捕获组64X11.1Xs (dotAll) Flag62X11.1XUnicode 属性转义64X11.1X移动浏览器 Chrome For AndroidFirefox For AndroidiOS SafariEdge MobileSamsung InternetAndroid Webview后行断言62XXX8.262命名捕获组64X11.3XX64s (dotAll) Flag62X11.3X8.262Unicode 属性转义64X11.3XX64NODE.JS8.3.0 (需要 –harmony 运行时标志)8.10.0 (支持 s (dotAll) flag 和后行断言)10.0.0 (完全支持)总结通过使正则表达式得到增强,ES2018 继续了以前版本ECMAScript的工作。新功能包括后行断言,命名捕获组, s (dotAll) flag 和 Unicode属性转义。 后行断言允许你在一个模式前面存在另一个模式进行匹配。与常规捕获组相比,命名捕获组使用了更具表现力的语法。 s (dotAll) flag 通过更改点(.)元字符的行为来匹配换行符。最后,Unicode 属性转义在正则表达式中提供了一种新类型的转义序列。在构建复杂的模式时,使用正则表达式测试程序通常很有帮助。一个好的测试器会提供一个接口来对字符串的正则表达式进行测试,并显示引擎所做的每一步,这在你理解其他人编写的表达式时非常有帮助。它还可以检测正则表达式中可能出现的语法错误。 Regex101 和 RegexBuddy 是两个值得一试的正则表达式测试程序。除此之外你能推荐其他的工具吗?欢迎在评论中分享!本文首发微信公众号:jingchengyideng欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章 ...

February 19, 2019 · 4 min · jiezi

精读《正则 ES2018》

引言本周精读的文章是 regexp-features-regular-expressions。这篇文章介绍了 ES2018 正则支持的几个重要特性:Lookbehind assertions - 后行断言Named capture groups - 命名捕获组s (dotAll) Flag - . 匹配任意字符Unicode property escapes - Unicode 属性转义2. 概述还在用下标匹配内容吗?匹配任意字符只有 [\w\W] 吗?现在正则有更简化的写法了,事实上正则正在变得更加易用,是时候更新对正则的认知了。2.1. Lookbehind assertions完整的断言定义分为:正/负向断言 与 先/后行断言 的笛卡尔积组合,在 ES2018 之前仅支持先行断言,现在终于支持了后行断言。解释一下这四种断言:正向先行断言 (?=…) 表示之后的字符串能匹配 pattern。const re = /Item(?= 10)/;console.log(re.exec(“Item”));// → nullconsole.log(re.exec(“Item5”));// → nullconsole.log(re.exec(“Item 5”));// → nullconsole.log(re.exec(“Item 10”));// → [“Item”, index: 0, input: “Item 10”, groups: undefined]负向先行断言 (?!…) 表示之后的字符串不能匹配 pattern。const re = /Red(?!head)/;console.log(re.exec(“Redhead”));// → nullconsole.log(re.exec(“Redberry”));// → [“Red”, index: 0, input: “Redberry”, groups: undefined]console.log(re.exec(“Redjay”));// → [“Red”, index: 0, input: “Redjay”, groups: undefined]console.log(re.exec(“Red”));// → [“Red”, index: 0, input: “Red”, groups: undefined]在 ES2018 后,又支持了两种新的断言方式:正向后行断言 (?<=…) 表示之前的字符串能匹配 pattern。先行时字符串放前面,pattern 放后面;后行时字符串放后端,pattern 放前面。先行匹配以什么结尾,后行匹配以什么开头。const re = /(?<=)\d+(.\d*)?/;console.log(re.exec(“199”));// → nullconsole.log(re.exec("$199"));// → nullconsole.log(re.exec(“199”));// → [“199”, undefined, index: 1, input: “199”, groups: undefined]负向后行断言 (?<!…) 表示之前的字符串不能匹配 pattern。注:下面的例子表示 meters 之前 不能匹配 三个数字。const re = /(?<!\d{3}) meters/;console.log(re.exec(“10 meters”));// → [" meters", index: 2, input: “10 meters”, groups: undefined]console.log(re.exec(“100 meters”));// → null文中给了一个稍复杂的例子,结合了 正向后行断言 与 负向后行断言:注:下面的例子表示 meters 之前 能匹配 两个数字,且 之前 不能匹配 数字 35.const re = /(?<=\d{2})(?<!35) meters/;console.log(re.exec(“35 meters”));// → nullconsole.log(re.exec(“meters”));// → nullconsole.log(re.exec(“4 meters”));// → nullconsole.log(re.exec(“14 meters”));// → [“meters”, index: 2, input: “14 meters”, groups: undefined]2.2. Named Capture Groups命名捕获组可以给正则捕获的内容命名,比起下标来说更可读。其语法是 ?<name>:const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;const [match, year, month, day] = re.exec(“2020-03-04”);console.log(match); // → 2020-03-04console.log(year); // → 2020console.log(month); // → 03console.log(day); // → 04也可以在正则表达式中,通过下标 \1 直接使用之前的捕获组,比如:解释一下,\1 代表 (\w\w) 匹配的内容而非 (\w\w) 本身,所以当 (\w\w) 匹配了 ‘ab’ 后,\1 表示的就是对 ‘ab’ 的匹配了。console.log(/(\w\w)\1/.test(“abab”)); // → true// if the last two letters are not the same// as the first two, the match will failconsole.log(/(\w\w)\1/.test(“abcd”)); // → false对于命名捕获组,可以通过 \k<name> 的语法访问,而不需要通过 \1 这种下标:下标和命名可以同时使用。const re = /\b(?<dup>\w+)\s+\k<dup>\b/;const match = re.exec(“I’m not lazy, I’m on on energy saving mode”);console.log(match.index); // → 18console.log(match[0]); // → on on2.3. s (dotAll) Flag虽然正则中 . 可以匹配任何字符,但却无法匹配换行符。因此聪明的开发者们用 [\w\W] 巧妙的解决了这个问题。然而这终究是个设计缺陷,在 ES2018 支持了 /s 模式,这个模式下,. 等价于 [\w\W]:console.log(/./s.test("\n")); // → trueconsole.log(/./s.test("\r")); // → true2.4. Unicode Property Escapes正则支持了更强大的 Unicode 匹配方式。在 /u 模式下,可以用 \p{Number} 匹配所有数字:u 修饰符可以识别所有大于 0xFFFF 的 Unicode 字符。const regex = /^\p{Number}+$/u;regex.test(“²³¹¼½¾”); // trueregex.test(“㉛㉜㉝”); // trueregex.test(“ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ”); // true\p{Alphabetic} 可以匹配所有 Alphabetic 元素,包括汉字、字母等:const str = “漢”;console.log(/\p{Alphabetic}/u.test(str)); // → true// the \w shorthand cannot match 漢console.log(/\w/u.test(str)); // → false终于有简便的方式匹配汉字了。2.5. 兼容表可以到 原文 查看兼容表,总体上只有 Chrome 与 Safari 支持,Firefox 与 Edge 都不支持。所以大型项目使用要再等几年。3. 精读文中列举的四个新特性是 ES2018 加入到正则中的。但正如兼容表所示,这些特性基本还都不能用,所以不如我们再温习一下 ES6 对正则的改进,找一找与 ES2018 正则变化的结合点。3.1. RegExp 构造函数优化当 RegExp 构造函数第一个参数是正则表达式时,允许指定第二个参数 - 修饰符(ES5 会报错):new RegExp(/book(?=s)/giu, “iu”);不痛不痒的优化,,毕竟大部分时间构造函数不会这么用。3.2. 字符串的正则方法将字符串的 match()、replace()、search、split 方法内部调用时都指向到 RegExp 的实例方法上,比如String.prototype.match 指向 RegExp.prototype[Symbol.match]。也就是正则表达式原本应该由正则实例触发,但现在却支持字符串直接调用(方便)。但执行时其实指向了正则实例对象,让逻辑更为统一。举个例子:“abc”.match(/abc/g) / // 内部执行时,等价于 abc / ;3.3. u 修饰符概述中,Unicode Property Escapes 就是对 u 修饰符的增强,而 u 修饰符是在 ES6 中添加的。u 修饰符的含义为 “Unicode 模式”,用来正确处理大于 \uFFFF 的 Unicode 字符。同时 u 修饰符还会改变以下正则表达式的行为:点字符原本支持单字符,但在 u 模式下,可以匹配大于 0xFFFF 的 Unicode 字符。将 \u{61} 含义由匹配 61 个 u 改编为匹配 Unicode 编码为 61 号的字母 a。可以正确识别非单字符 Unicode 字符的量词匹配。\S 可以正确识别 Unicode 字符。u 模式下,[a-z] 还能识别 Unicode 编码不同,但是字型很近的字母,比如 \u212A 表示的另一个 K。基本上,在 u 修饰符模式下,所有 Unicode 字符都可以被正确解读,而在 ES2018,又新增了一些 u 模式的匹配集合来匹配一些常见的字符,比如 \p{Number} 来匹配 ¼。3.4. y 修饰符y 修饰符是 “粘连”(sticky)修饰符。y 类似 g 修饰符,都是全局匹配,也就是从上次成功匹配位置开始,继续匹配。y 的区别是,必须是上一次匹配成功后的下一个位置就立即匹配才算成功。比如:/a+/g.exec(“aaa_aa_a”); // [“aaa”]3.5. flags通过 flags 属性拿到修饰符:const regex = /[a-z]*/gu;regex.flags; // ‘gu'4. 总结本周精读借着 regexp-features-regular-expressions 这篇文章,一起理解了 ES2018 添加的正则新特性,又顺藤摸瓜的整理了 ES6 对正则做的增强。如果你擅长这种扩散式学习方式,不妨再进一步温习一下整个 ES6 引入的新特性,笔者强烈推荐阮一峰老师的 ECMAScript 6 入门 一书。ES2018 引入的特性还太新,单在对 ES6 特性的使用应该和对 ES3 一样熟练。如果你身边的小伙伴还对 ES6 特性感到惊讶,请把这篇文章分享给他,防止退化为 “只剩项目经验的 JS 入门者”。讨论地址是:精读《正则 ES2018》 · Issue #127 · dt-fe/weekly如果你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。

February 18, 2019 · 3 min · jiezi

javascript replace高级用法

在前端与后台交互的时候我们通常都需要将后台传递的数据绑定到html中,这个绑定数据的方式我们通常是使用jQuery或者使用原生的innerHTML进行绑定,当然也可以使用artTemplate模板来绑定数据,那么artTemplate模板它绑定数据的原理是什么呢?其实它就是利用了replace()方法。对于正则replace约定了一个特殊标记符”$”:1)、$i(i取值范围1~99):表示从左到右正则子表达式所匹配的文本2)、$&:表示与正则表达式匹配的全部文本3)、$(:1旁边的那个键):表示匹配字符串的左边文本4)、$’(’:单引号):表示匹配字符串的右边文本5)、$$:表示$转移1、replace基本用法<script type=“text/javascript”> /要求将字符串中所有的a全部用A代替/ var str = “javascript is great script language!”; //只会将第一个匹配到的a替换成A console.log(str.replace(“a”,“A”)); //只会将第一个匹配到的a替换成A。因为没有在全局范围内查找 console.log(str.replace(/a/,“A”)); //所有a都被替换成了A console.log(str.replace(/a/g,“A”));</script>1.1、replace基本用法之替换移除指定class类<script type=“text/javascript”> /要求将下面这个元素中的unabled类移除掉/ <div class=”confirm-btn unabled mb-10” id=”j_confirm_btn”>提交</div> var classname = document.getElementById(“j_confirm_btn”).className; /(^|\s)表示匹配字符串开头或字符串前面的空格,(\s|$)表示匹配字符串结尾或字符串后面的空格/ var newClassName = classname.replace(/(^|\s)unabled(\s|$)/,””); document.getElementById(“j_confirm_btn”).className = newClassName;</script>2、replace高级用法之 —- $i2.1、简单的$i用法<script> /要求:将字符串中的双引号用"-“代替/ var str = ‘“a”, “b”’; console.log(str.replace(/”[^"]"/g,"-$1-")); //输出结果为:-$1-, -$1- /解释:$1就是前面正则(/"[^"]"/g)所匹配到的每一个字符。/</script>2.2、$i与分组结合使用<script> /要求:将下面字符串替换成:javascript is fn.it is a good script language/ var str = “javascript is a good script language”; console.log(str.replace(/(javascript)\s*(is)/g,"$1 $2 fn.it $2")); /解释:每一对括号都代表一个分组,从左往右分别代表第一个分组,第二个分组…;如上"(javascript)“为第一个分组,"(is)“为第二个分组。$1就代表第一个分组匹配的内容,$2就代表第二个分组匹配的内容,依此类推…/</script>2.3、$i与分组结合使用—-关键字高亮显示当我们使用谷歌搜索的时候我们会发现我们搜索的关键字都被高亮显示了,那么这种效果用JavaScript能否显示呢?答案是可以的,使用replace()很轻松就搞定了。<script> /要求:将下列字符串中的"java"用红色字体显示/ var str = “Netscape在最初将其脚本语言命名为LiveScript,后来Netscape在与Sun合作之后将其改名为JavaScript。JavaScript最初受Java启发而开始设计的,目的之一就是“看上去像Java”,因此语法上有类似之处,一些名称和命名规范也借自Java。但JavaScript的主要设计原则源自Self和Scheme。”; document.write(str.replace(/(java)/gi,’<span style=“color: red;font-weight: 800;">$1</span>’)); /解释:必须要开启全局搜索和忽略大小写,否则匹配不到所有的”java”字符/</script>2.4、反向分组—-分组的反向引用在正则中,当我们需要匹配两个或多个连续的相同的字符的时候,就需要用到反向引用了,查找连续重复的字符是反向引用最简单却也是最有用的应用之一。上面的”$i”也是反向分组的一种形式,这里再介绍另一种反向分组。<script type=“text/javascript”> / /ab(cd)\1e/ 这里的 \1 表示把第1个分组的内容重复一遍*/ console.log(/ab(cd)\1e/.test(“abcde”));//false console.log(/ab(cd)\1e/.test(“abcdcde”));//true /要求:将下列字符串中相领重复的部分删除掉”/ var str = “abbcccdeee”; var newStr = str.replace(/(\w)\1+/g,"$1”); console.log(newStr); // abcde</script>3、replace高级用法之参数二为函数replace函数的第二个参数不仅可以是一个字符,还可以是一个函数!3.1、参数二为函数之参数详解<script> var str = “bbabc”; var newStr = str.replace(/(a)(b)/g,function (){ console.log(arguments);//[“ab”, “a”, “b”, 2, “bbabc”] /*参数依次为: 1、整个正则表达式所匹配到的字符串—-“ab” 2、第一个分组匹配到的字符串,第二个分组所匹配到的字符串….依次类推一直 到最后一个分组—-“a,b” 3、此次匹配在源字符串中的下标,返回的是第一个匹配到的字符的下标—-2 4、源字符串—-“bbabc” */ })</script>3.2、参数二为函数之首字母大写案例<script> /要求:将下列字符串中的所有首字母大写/ var str = “Tomorrow may not be better, but better tomorrow will surely come!”; var newStr = str.replace(/\b\w+\b/gi,function (matchStr){ console.log(matchStr);//匹配到的字符 return matchStr.substr(0,1).toUpperCase() + matchStr.substr(1); }); console.log(newStr);</script>3.3、参数二为函数之绑定数据—-artTemplate模板核心<h1>周星驰喜剧电影:</h1><div id=“content”></div><script type=“text/javascript”> var data = { name: “功夫”, protagonist: “周星驰” }, domStr = ‘<div><span>名称:</span><span>{{name}}</span></div><div><span>导演:</span><span>{{protagonist}}</span> </div>’; document.getElementById(“content”).innerHTML = formatString(domStr,data); /绑定数据的核心就是使用正则进行匹配/ function formatString(str,data){ return str.replace(/{{(\w+)}}/g,function (matchingStr,group1){ return data[group1]; }); }</script>4、replace高级用法之获取与正则表达式匹配的文本4.1、replace高级用法之获取与正则表达式进行匹配的源字符串<script> var str = “i am a good man”; var newStr = str.replace(/good/g,"$&”); console.log(newStr);//结果:输出i am a good man /解释:在这里”$&”就是与正则表达式进行匹配的那个源字符串/</script>4.2、replace高级用法之获取正则表达式匹配到的字符<script> /*要求:将"i am a good man"替换成"i am a good-gond man" / var str = “i am a good man”; var newStr = str.replace(/good/g,"$&-$&"); console.log(newStr); /解释:在这里”$&”可以获取到前面正则表达式匹配的内容,如上面的”$&”就是正则表达式匹配到的”good”/</script>5、replace高级用法之获取正则匹配的左边的字符<script> /要求:将下列字符串替换成"java-java is a good script"/ var str = “javascript is a good script”; var newStr = str.replace(/script/,"-$"); console.log(newStr) /*解释:"$“获取的是正则左边的内容,如上正则中"script"字符前面的是"java”,"-$"就是"-java","-$“会把script替换掉。/</script>6、replace高级用法之获取正则匹配的右边的字符<script> /要求:将下列字符替换成"java is a good language!it is a good script is a good script”/ var str = “javascript is a good script”; var newStr = str.replace(/script/," is a good language!it$’"); console.log(newStr) /解释:"$’“获取的就是str右边的内容,如上正则中”$’“就是” is a good script"。 " is a good language!it$’“会把正则匹配到的"script"替换掉/</script> ...

February 16, 2019 · 2 min · jiezi

Python正则表达式简记和re库

正则表达式是定义搜索模式的字符序列。通常这种模式被字符串搜索算法用于字符串上的“查找”或“查找和替换”操作,或者用于输入验证。1. 正则表达式的语法. 表示任何单个字符[] 字符集,对单个字符给出取值范围[^] 非字符集,对单个字符给出排除范围前一个字符0次或者无限次扩展+前一个字符1次或无限次扩展?前一个字符0次或1次扩展|左右表达式任意一个{m}扩展前一个字符m次{m,n}扩展前一个字符m至n次^匹配字符串开头$匹配字符串结尾()分组标记,内部只能使用|操作符d数字,等价于[0-9]w单词字符,等价于[A-Z,a-z,0-9]2. python中re库的使用Re库是python的标准库,主要用于字符串匹配,调用方法:import re2.1. 正则表达式字符串的类型re库采用raw string类型来表示正则表达式,表示为r’text’raw string是不包含对转义符的再次转义的字符串,总而言就是string会对字符转义,而raw string不会,因为在正则表达中会出现转义符号,所以避免繁琐我们使用raw string2.2. Re库主要功能函数re.search()在一个字符串中搜索正则表达式的第一个位置,返回match对象re.match()从一个字符串的开始位置起匹配正则表达式,返回match对象re.findall()搜索字符串,以列表类型返回全部能匹配的子串re.split()将一个字符串按照正则表达式匹配结果进行分割,返回列表类型re.finditer()搜索字符串,返回一个匹配结果的迭代类型,每个迭代元素是match对象re.sub()在一个字符串中替换所有匹配正则表达式的子串,返回替换后的字符串2.2.1. re.search(pattern, string, flags=0)在一个字符串中搜索正则表达式的第一个位置,返回match对象pattern : 正则表达式的字符串或原生字符串表示string : 待匹配字符串flags : 正则表达式使用时的控制标记re.I re.IGNORECASE 忽略正则表达式的大小写,[A‐Z]能够匹配小写字符re.M re.MULTILINE 正则表达式中的^操作符能够将给定字符串的每行当作匹配开始re.S re.DOTALL 正则表达式中的.操作符能够匹配所有字符,默认匹配除换行外的所有字符举例说明:import rematch = re.search(r’[1-9]\d{5}’, ‘BIT 100081’)if match: print(match.group(0))结果为1000812.2.2. re.match(pattern, string, flags=0)从一个字符串的开始位置起匹配正则表达式,返回match对象参数同search函数举例说明:import rematch = re.match(r’[1-9]\d{5}’, ‘BIT 100081’)print(match.group(0))结果会报错,match为空,因为match函数是从字符串开始位置开始匹配,因为从开始位置没有匹配到,所以为空2.2.3. re.findall(pattern, string, flags=0)搜索字符串,以列表类型返回全部能匹配的子串参数同search举例说明:import rels=re.findall(r’[1-9]\d{5}’, ‘BIT100081 TSU100084’)print(ls)结果为[‘100081’, ‘100084’]2.2.4. re.split(pattern, string, maxsplit=0, flags=0)将一个字符串按照正则表达式匹配结果进行分割返回列表类型maxsplit : 最大分割数,剩余部分作为最后一个元素输出举例说明 :import rere.split(r’[1-9]\d{5}’, ‘BIT100081 TSU100084’)结果[‘BIT’, ’ TSU’, ’ ‘]re.split(r’[1-9]\d{5}’, ‘BIT100081 TSU100084’, maxsplit=1)结果[‘BIT’, ’ TSU100081’]2.2.5. re.finditer(pattern, string, maxsplit=0, flags=0)搜索字符串,返回一个匹配结果的迭代类型,每个迭代元素是match对象参数同search举例说明 :import refor m in re.finditer(r’[1-9]\d{5}’, ‘BIT100081 TSU100084’): if m: print(m.group(0))结果为1000811000842.2.6. re.sub(pattern, repl, string, count=0, flags=0)在一个字符串中替换所有匹配正则表达式的子串返回替换后的字符串repl : 替换匹配字符串的字符串count : 匹配的最大替换次数举例说明:import rere.sub(r’[1-9]\d{5}’, ‘:zipcode’, ‘BIT100081 TSU100084’)结果为’BIT:zipcode TSU:zipcode'2.3 Re库的另一种等价用法(面向对象)rst=re.search(r’[1-9]\d{5}’, ‘BIT 100081’)函数式的调用,一次性操作pat=re.compile(r’[1-9]\d{5}’)rst=pat.search(‘BIT 100081’)编译后多次操作regex=re.complie(pattern,flags=0)regex也有以上六种用法2.4 Re库的Match对象Match对象是是一次匹配的结果,包含匹配的很多信息以下是Match对象的属性.string 待匹配的文本.re 匹配时使用的patter对象(正则表达式).pos 正则表达式搜索文本的开始位置.endpos 正则表达式搜索文本的结束位置以下是Match对象的方法.group(0) 获得匹配后的字符串.start() 匹配字符串在原始字符串的开始位置.end() 匹配字符串在原始字符串的结束位置.span() 返回(.start(), .end())2.5 Re库的贪婪匹配和最小匹配当正则表达式可以匹配长短不同的多项时,返回哪一个呢?Re库默认采用贪婪匹配,即返回匹配最长的子串最小匹配? 前一个字符0次或无限次扩展,最小匹配+? 前一个字符1次或无限次扩展,最小匹配?? 前一个字符0次或1次扩展,最小匹配{m,n}? 扩展前一个字符m至n次(含n),最小匹配只要长度输出可能不同的,都可以通过在操作符后增加?变成最小匹配 ...

February 2, 2019 · 1 min · jiezi

正则全攻略使用手册,你确定不进来看看吗

前言正则表达式是软件领域为数不多的伟大创作。与之相提并论是分组交换网络、Web、Lisp、哈希算法、UNIX、编译技术、关系模型、面向对象等。正则自身简单、优美、功能强大、妙用无穷。学习正则表达式,语法并不难,稍微看些例子,多可照葫芦画瓢。但三两篇快餐文章,鲜能理解深刻。再遇又需一番查找,竹篮打水一场空。不止正则,其他技术点同样,需要系统的学习。多读经典书籍,站在巨人肩膀前行。这里涉及的东西太多,我就着重讲日常开发中可能会用到的内容,如果像深入理解的话推荐翻阅书籍《精通正则表达式》(所以简单来说,学习正则就是投入高,收益低)(起初一看简单易懂,深入了解过后感叹正则的强大)全文略长,可以选择感兴趣的部分看1、介绍正则正则表达式严谨来讲,是一种描述字符串结构模式的形式化表达方法。起始于数学领域,流行于 Perl 正则引擎。JavaScript 从 ES 3 引入正则表达式,ES 6 扩展对正则表达式支持。正则原理对于固定字符串的处理,简单的字符串匹配算法(类KMP算法)相较更快;但如果进行复杂多变的字符处理,正则表达式速度则更胜一筹。那正则表达式具体匹配原理是什么?这就涉及到编译原理的知识(编译原理着实是我大三里面最头疼的课程了)正则表达式引擎实现采用一种特殊理论模型:有穷自动机(Finite Automata)也叫有限状态自动机(finite-state machine)具体的细节见文章底部的参考文档字符组字符组含义[ab]匹配 a 或 b[0-9]匹配 0 或 1 或 2 … 或 91匹配 除 a、b 任意字符字符组含义d表示 [0-9],数字字符D表示 [^0-9],非数字字符w表示 [0-9a-zA-Z],单词字符,注意下划线W表示 [^0-9a-zA-Z],非单词字符s表示 [ tvnrf],空白符S表示 [^ tvnrf],非空白符.表示 [^nru2028u2029]。通配符,匹配除换行符、回车符、行分隔符、段分隔符外任意字符量词匹配优先量词忽略优先量词含义{m,n}{m,n}?表示至少出现 m 次,至多 n 次{m,}{m,}?表示至少出现 m 次{m}{m}?表示必须出现 m 次,等价 {m,m}???等价 {0,1}++?等价 {1,}**?等价 {0,}锚点与断言正则表达式中有些结构并不真正匹配文本,只负责判断在某个位置左/右侧的文本是否符合要求,被称为锚点。常见锚点有三类:行起始/结束位置、单词边界、环视。在 ES5 中共有 6 个锚点。锚点含义^匹配开头,多行匹配中匹配行开头$匹配结尾,多行匹配中匹配行结尾b单词边界,w 与 W 之间位置B非单词边界(?=p)该位置后面字符要匹配 p(?!p)该位置后面字符不匹配 p需要注意,\b 也包括 \w 与 ^ 之间的位置,以及 \w 与 $ 之间的位置。如图所示。修饰符修饰符是指匹配时使用的模式规则。ES5 中存在三种匹配模式:忽略大小写模式、多行模式、全局匹配模式,对应修饰符如下。修饰符含义i不区分大小写匹配m允许匹配多行g执行全局匹配uUnicode 模式,用来正确处理大于\uFFFF的 Unicode 字符,处理四个字节的 UTF-16 编码。y粘连模式,和g相似都是全局匹配,但是特点是:后一次匹配都从上一次匹配成功的下一个位置开始,必须从剩余的第一个位置开始,这就是“粘连”的涵义。sdotAll 模式,大部分情况是用来处理行终止符的2、正则的方法字符串对象共有 4 个方法,可以使用正则表达式:match()、replace()、search()和split()。ES6 将这 4 个方法,在语言内部全部调用RegExp的实例方法,从而做到所有与正则相关的方法,全都定义在RegExp对象上。String.prototype.match 调用 RegExp.prototype[Symbol.match]String.prototype.replace 调用 RegExp.prototype[Symbol.replace]String.prototype.search 调用 RegExp.prototype[Symbol.search]String.prototype.split 调用 RegExp.prototype[Symbol.split]String.prototype.matchString.prototype.replace字符串的replace方法,应该是我们最常用的方法之一了,这里我给详细的说一下其中的各种使用攻略。replace函数的第一个参数可以是一个正则,或者是一个字符串(字符串没有全局模式,仅匹配一次),用来匹配你想要将替换它掉的文本内容第二个参数可以是字符串,或者是一个返回字符串的函数。这里请注意,如果使用的是字符串,JS 引擎会给你一些 tips 来攻略这段文本:变量名代表的值$$插入一个 “$"。$&插入匹配的子串。$插入当前匹配的子串左边的内容。$'插入当前匹配的子串右边的内容。$n假如第一个参数是 RegExp对象,并且 n 是个小于100的非负整数,那么插入第 n 个括号匹配的字符串。提示:索引是从1开始,注意这里的捕获组规则如果你不清楚捕获组的顺序,给你一个简单的法则:从左到右数 &gt;&gt;&gt; 第几个 '(' 符号就是第几个捕获组(特别适用于捕获组里有捕获组的情况)(在函数模式里,解构赋值时会特别好用)$:就是相当于正则匹配到的内容的左侧文本$’:就是相当于正则匹配到的内容右侧文本$&:正则匹配到的内容$1 - $n :对应捕获组如果参数使用的是函数,则可以对匹配的内容进行一些过滤或者是补充下面是该函数的参数:变量名代表的值match匹配的子串。(对应于上述的$&。)p1,p2, …假如replace()方法的第一个参数是一个RegExp 对象,则代表第n个括号匹配的字符串。(对应于上述的$1,$2等。)例如, 如果是用 /(\a+)(\b+)/这个来匹配, p1就是匹配的 \a+, p2 就是匹配的 \b+。offset匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是“abcd”,匹配到的子字符串是“bc”,那么这个参数将是1)string被匹配的原字符串。一个示例,从富文本里面,匹配到里面的图片标签的地址可以说,使用函数来替换文本的话,基本上你想干嘛就干嘛String.prototype.searchString.prototype.splitRegExp.prototype.test和String.prototype.search 的功能很像,但是这个是返回布尔值,search返回的是下标,这个从语义化角度看比较适合校检RegExp.prototype.exec3、正则常见的使用主要内容是ES6 里新增的修饰符(u,y,s)(g,m,i 就不说了)、贪婪和非贪婪模式、先行/后行断言’u’ 修饰符ES6 对正则表达式添加了u修饰符,含义为“Unicode 模式”,用来正确处理大于\uFFFF的 Unicode 字符。也就是说,会正确处理四个字节的 UTF-16 编码。少说废话,看图但是很可惜的是 MDN给出的浏览器兼容性如下:(截止至2019.01.24),所以离生产环境上使用还是有点时间’y’ 修饰符除了u修饰符,ES6 还为正则表达式添加了y修饰符,叫做“粘连”(sticky)修饰符。y修饰符的作用与g修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始。不同之处在于,g修饰符只要剩余位置中存在匹配就可,而y修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。var s = ‘aaa_aa_a’;var r1 = /a+/g;var r2 = /a+/y;r1.exec(s) // [“aaa”]r2.exec(s) // [“aaa”]r1.exec(s) // [“aa”]r2.exec(s) // null上面代码有两个正则表达式,一个使用g修饰符,另一个使用y修饰符。这两个正则表达式各执行了两次,第一次执行的时候,两者行为相同,剩余字符串都是_aa_a。由于g修饰没有位置要求,所以第二次执行会返回结果,而y修饰符要求匹配必须从头部开始,所以返回null。如果改一下正则表达式,保证每次都能头部匹配,y修饰符就会返回结果了。var s = ‘aaa_aa_a’;var r = /a+/y;r.exec(s) // [“aaa"]r.exec(s) // [“aa_"]上面代码每次匹配,都是从剩余字符串的头部开始。使用lastIndex属性,可以更好地说明y修饰符。const REGEX = /a/g;// 指定从2号位置(y)开始匹配REGEX.lastIndex = 2;// 匹配成功const match = REGEX.exec(‘xaya’);// 在3号位置匹配成功match.index // 3// 下一次匹配从4号位开始REGEX.lastIndex // 4// 4号位开始匹配失败REGEX.exec(‘xaya’) // null上面代码中,lastIndex属性指定每次搜索的开始位置,g修饰符从这个位置开始向后搜索,直到发现匹配为止。y修饰符同样遵守lastIndex属性,但是要求必须在lastIndex指定的位置发现匹配。const REGEX = /a/y;// 指定从2号位置开始匹配REGEX.lastIndex = 2;// 不是粘连,匹配失败REGEX.exec(‘xaya’) // null// 指定从3号位置开始匹配REGEX.lastIndex = 3;// 3号位置是粘连,匹配成功const match = REGEX.exec(‘xaya’);match.index // 3REGEX.lastIndex // 4实际上,y修饰符号隐含了头部匹配的标志^。/b/y.exec(‘aba’)// null上面代码由于不能保证头部匹配,所以返回null。y修饰符的设计本意,就是让头部匹配的标志^在全局匹配中都有效。下面是字符串对象的replace方法的例子。const REGEX = /a/gy;‘aaxa’.replace(REGEX, ‘-’) // ‘–xa’上面代码中,最后一个a因为不是出现在下一次匹配的头部,所以不会被替换。单单一个y修饰符对match方法,只能返回第一个匹配,必须与g修饰符联用,才能返回所有匹配。‘a1a2a3’.match(/a\d/y) // [“a1”]‘a1a2a3’.match(/a\d/gy) // [“a1”, “a2”, “a3”]y修饰符的一个应用,是从字符串提取 token(词元),y修饰符确保了匹配之间不会有漏掉的字符。const TOKEN_Y = /\s*(+|[0-9]+)\s*/y;const TOKEN_G = /\s*(+|[0-9]+)\s*/g;tokenize(TOKEN_Y, ‘3 + 4’)// [ ‘3’, ‘+’, ‘4’ ]tokenize(TOKEN_G, ‘3 + 4’)// [ ‘3’, ‘+’, ‘4’ ]function tokenize(TOKEN_REGEX, str) { let result = []; let match; while (match = TOKEN_REGEX.exec(str)) { result.push(match[1]); } return result;}上面代码中,如果字符串里面没有非法字符,y修饰符与g修饰符的提取结果是一样的。但是,一旦出现非法字符,两者的行为就不一样了。tokenize(TOKEN_Y, ‘3x + 4’)// [ ‘3’ ]tokenize(TOKEN_G, ‘3x + 4’)// [ ‘3’, ‘+’, ‘4’ ]上面代码中,g修饰符会忽略非法字符,而y修饰符不会,这样就很容易发现错误。很遗憾,这个的浏览器兼容性也不咋地但是,如果你的项目里有集成了babel,就可以使用以上的两个修饰符了,他们分别是@babel-plugin-transform-es2015-sticky-regex@babel-plugin-transform-es2015-unicode-regex’s’ 修饰符正则表达式中,点(.)是一个特殊字符,代表任意的单个字符,但是有两个例外。一个是四个字节的 UTF-16 字符,这个可以用u修饰符解决;另一个是行终止符(line terminator character)。所谓行终止符,就是该字符表示一行的终结。以下四个字符属于”行终止符“。U+000A 换行符(\n)U+000D 回车符(\r)U+2028 行分隔符(line separator)U+2029 段分隔符(paragraph separator)虽然这个浏览器兼容性也很差,但是我们有方法来模拟它的效果,只是语义化上有点不友好/foo.bar/.test(‘foo\nbar’) // false/foo[^]bar/.test(‘foo\nbar’) // true/foo[\s\S]bar/.test(‘foo\nbar’) // true 我喜欢这种贪婪模式和非贪婪模式(惰性模式)贪婪模式:正则表达式在匹配时会尽可能多地匹配,直到匹配失败,默认是贪婪模式。非贪婪模式:让正则表达式仅仅匹配满足表达式的内容,即一旦匹配成功就不再继续往下,这就是非贪婪模式。在量词后面加?即可。在某些情况下,我们需要编写非贪婪模式场景下的正则,比如捕获一组标签或者一个自闭合标签这时捕获到了一组很奇怪的标签,如果我们的目标是只想捕获img标签的话,显然是不理想的,这时非贪婪模式就可以用在这里了只需要在量词后加 ? 就会启用非贪婪模式,在特定情况下是特别有效的先行/后行(否定)断言有时候,我们会有些需求,具体是:匹配xxx前面/后面的xxx。很尴尬的是,在很久之前,只支持先行断言(lookahead)和先行否定断言(negative lookahead),不支持后行断言(lookbehind)和后行否定断言(negative lookbehind),在ES2018 之后才引入后行断言名称正则含义先行断言/want(?=asset)/匹配在asset前面的内容先行否定断言/want(?!asset)/want只有不在asset前面才匹配后行断言/(?<=asset)want/匹配在asset后面的内容后行否定断言/(?<!asset)want/want只有不在asset后面才匹配老实说,根据我的经验,后行断言的使用场景会更多,因为js 有很多的数据存储是名值对的形式保存,所以很多时候我们想要通过"name=“来取到后面的值,这时候是后行断言的使用场景了先行断言:只匹配 在/不在 百分号之前的数字后行断言:这里引例 @玉伯也叫射雕 的一篇 博文的内容这里可以用后行断言(?<=^|(第.+[章集])).?(?=$|(第.+[章集]))“后行断言”的实现,需要先匹配/(?<=y)x/的x,然后再回到左边,匹配y的部分。这种“先右后左”的执行顺序,与所有其他正则操作相反,导致了一些不符合预期的行为。首先,后行断言的组匹配,与正常情况下结果是不一样的。/(?<=(\d+)(\d+))$/.exec(‘1053’) // [””, “1”, “053”]/^(\d+)(\d+)$/.exec(‘1053’) // [“1053”, “105”, “3”]上面代码中,需要捕捉两个组匹配。没有“后行断言”时,第一个括号是贪婪模式,第二个括号只能捕获一个字符,所以结果是105和3。而“后行断言”时,由于执行顺序是从右到左,第二个括号是贪婪模式,第一个括号只能捕获一个字符,所以结果是1和053。其次,“后行断言”的反斜杠引用,也与通常的顺序相反,必须放在对应的那个括号之前。/(?<=(o)d\1)r/.exec(‘hodor’) // null/(?<=\1d(o))r/.exec(‘hodor’) // [“r”, “o”]上面代码中,如果后行断言的反斜杠引用(\1)放在括号的后面,就不会得到匹配结果,必须放在前面才可以。因为后行断言是先从左到右扫描,发现匹配以后再回过头,从右到左完成反斜杠引用。另外,需要提醒的是,断言部分是不计入返回结果的。具名组匹配ES2018 引入了具名组匹配(Named Capture Groups),允许为每一个组匹配指定一个名字,既便于阅读代码,又便于引用。上面代码中,“具名组匹配”在圆括号内部,模式的头部添加“问号 + 尖括号 + 组名”(?<year>),然后就可以在exec方法返回结果的groups属性上引用该组名。同时,数字序号(matchObj[1])依然有效。具名组匹配等于为每一组匹配加上了 ID,便于描述匹配的目的。如果组的顺序变了,也不用改变匹配后的处理代码。如果具名组没有匹配,那么对应的groups对象属性会是undefined。具名组匹配 × 解构赋值具名组引用如果要在正则表达式内部引用某个“具名组匹配”,可以使用\k<组名>的写法。4、常用正则我这里比较推荐一个正则可视化的网站:https://regexper.com/ 在上面贴上你的正则,会以图形化的形式展示出你的正则匹配规则,之后我们就可以大致上判断我们的正则是否符合预期(貌似需要科学上网)如果想通过字符串来生成正则对象的话,有两种方式,一种是字面量方式,另一种是构造函数构造函数:new Regexp(‘content’, ‘descriptor’)字面量模式(请做好try-catch处理):const input = ‘/123/g’const regexp = eval(input)校验密码强度密码的强度必须是包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间。^(?=.\d)(?=.[a-z])(?=.[A-Z]).{8,10}$非全数字 全字母的 6-15位密码 先行否定断言/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,15}$/校验中文字符串仅能是中文。^[\u4e00-\u9fa5]{0,}$校验身份证号码15位^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$18位^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$校验日期“yyyy-mm-dd“ 格式的日期校验,已考虑平闰年。^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$提取URL链接下面的这个表达式可以筛选出一段文本中的URL。^(f|ht){1}(tp|tps)://([\w-]+.)+[\w-]+(/[\w- ./?%&=])?提取图片标签的地址假若你想提取网页中所有图片信息,可以利用下面的表达式。/<img [^>]?src=”(.?)”[^>]?>/g;*[img][^&gt;][src] = ["']{0,1}([^"'\ >])5、注意事项使用非捕获型括号如果不需要引用括号内文本,请使用非捕获型括号 (?:…)。这样不但能够节省捕获的时间,而且会减少回溯使用的状态数量。消除不必要括号非必要括号有时会阻止引擎优化。比如,除非需要知道 . 匹配的最后一个字符,否则请不要使用 (.) 。不要滥用字符组避免单个字符的字符组。例如 [.] 或 [],可以通过转义转换为 . 和 *\。使用起始锚点除非特殊情况,否则以 . 开头的正则表达式都应该在最前面添加 ^。如果表达式在字符串的开头不能匹配,显然在其他位置也不能匹配。从量词中提取必须元素用 xx* 替代 x+ 能够保留匹配必须的 “x”。同样道理,-{5,7} 可以写作 —–{0,2}。(可读性可能会差点)提取多选结构开头的必须元素用 th(?:is|at) 替代 (?:this|that),就能暴露除必须的 “th”。忽略优先还是匹配优先?通常,使用忽略优先量词(惰性)还是匹配优先量词(贪婪)取决于正则表达式的具体需求。举例来说,/^.:/ 不同于 ^.?:,因为前者匹配到最后的冒号,而后者匹配到第一个冒号。总的来说,如果目标字符串很长,冒号会比较接近字符串的开头,就是用忽略优先。如果在接近字符串末尾位置,就是用匹配优先量词。拆分正则表达式有时候,应用多个小正则表达式的速度比单个正则要快的多。“大而全”的正则表达式必须在目标文本中的每个位置测试所有表达式,效率较为低下。典型例子可以参考前文, 去除字符串开头和结尾空白。将最可能匹配的多选分支放在前头多选分支的摆放顺序非常重要,上文有提及。总的来说,将常见匹配分支前置,有可能获得更迅速更常见的匹配。避免指数级匹配从正则表达式角度避免指数级匹配,应尽可能减少 + * 量词叠加,比如 ([^\"]+)* 。从而减少可能匹配情形,加快匹配速度。6、小结正则表达式想要用好,需要一定的经验,个人经验来看,需要把你想法中的需要写出来,然后通过搭积木的形式,把一个个小的匹配写出来,然后再组合出你想要的功能,这是比较好的一种实现方法。如果说遇到了晦涩难懂的正则,也可以贴到上面提到的正则可视化网站里,看下它的匹配机制。对于前端来说,正则的使用场景主要是用户输入的校检,富文本内容的过滤,或者是对一些url或者src的过滤,还有一些标签的替换之类的,掌握好了还是大有裨益的,起码以前雄霸前端的 jQ 的选择器 sizzle 就是用了大量正则。最后,如果大家觉得我有哪里写错了,写得不好,有其它什么建议(夸奖),非常欢迎大家指出和斧正。也非常欢迎大家跟我一起讨论和分享!写在最后感谢以下参考文档的作者的分享精通正则表达式(第三版)前端正则二三事 @代码君的自白ES6 入门 – 正则的拓展 @阮一峰ab ↩ ...

January 28, 2019 · 2 min · jiezi

正则表达式

RegExp对象Regular Experssion使用单个字符串来描述、匹配一系列符合某个句法规则的字符串测试网站:https://regexper.com创建方式字面量var reg = /\bis\b/g"he is a boy".replace(reg,‘IS’)//“he IS a boy"构造函数var reg = new RegExp(’\bis\b’,‘g’);“he is a boy”.replace(reg,‘IS’)//“he IS a boy"元字符正则表达式由两种基本字符类型组成:原义文本字符元字符元字符是在正则表达式中有特殊含义的非字母字符* + ? $ ^ . | \ () {} []字符类一般情况下正则表达式一个字符对应字符串一个字符表达式ab\t的含义是我们也可以使用元字符[]来构建一个简单的类所谓类是指符合某些特征的对象,一个泛指,而不是特指某个字符表达式[abc]把字符a或b或c归为一类,表达式可以匹配这类的字符,‘a1b2c3d3’.replace(/[abc]/g,‘X’)“X1X2X3d3"字符串取反使用元字符^创建反向类/负向类反向类的意思是不属于某类的内容表达式[^abc]表示不是字符a或b或c的内容范围类正则表达式还提供了范围类可以用[a-z]来连接两个字符表示从a到z的任意字符这是个闭区间,也就是包含a和z本身’a1b2c3d4x5z6’.replace(/[a-z]/g,‘X’)//“X1X2X3X4X5X6"在[]组成的类内部是可以连写的[a-zA-Z]‘a1b2c3d4x5z6DSFDAFDAFD’.replace(/[a-zA-Z]/g,‘X’)//“X1X2X3X4X5X6DSFDAFDAFD"这种情况下如何匹配到’-’?只需要在后面补上一个’-‘就行了’a1b2-c-3d4x5z6DS-F-DAFDAFD’.replace(/[a-zA-Z-]/g,‘X’)//“X1X2XXX3X4X5X6XXXXXXXXXXXX"JS预定义类及边界预定类:“This is a boy”.replace(/\Bis\b/,‘0’)//“Th0 is a boy"几个常用的边界匹配字符:示例一:“This is a boy”.replace(/is/g,‘0’)//“Th0 0 a boy"只想替换第一个’is"This is a boy”.replace(/\Bis\b/,‘0’)//“Th0 is a boy"只想替换第二个"This is a boy”.replace(/\bis\b/g,‘0’)//“This 0 a boy"示例二:"@123@345@".replace(/@./g,‘Q’)//“Q23Q45@“只想替换第一个”@123@345@".replace(/^@./g,‘Q’)//“Q23@345@“只替换最后一个”@123@345@".replace(/.@$/g,‘Q’)//"@123@34Q"var mulstr = “@123 @456 @678”; mulStr.replace(/^@\d/gm,‘x’) // X123 X456 X678量词我们希望匹配一个连续出现20次数字的字符串:显然不能用这中方式:\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d?+JS正则贪婪模式与非贪婪模式贪婪模式正则表达式在匹配的时候会尽可能多的匹配,直到匹配失败"12345678”.replace(/\d{3,6}/g,‘X’)//“X78”//123456 -> X非贪婪模式让正则表达式尽可能少的匹配,也就是说一旦成功匹配不再继续尝试就是非贪婪模式做法很简单,在量词后面加上?即可"12345678”.replace(/\d{3,6}?/g,‘X’)//“XX78”//123,456 -> X,X分组()匹配字符串Byron连续出现三次的场景,可能会用Byron{3}并不能达到我们期待的结果,只会处理紧挨着的,用()可以达到分组的功能,使量词作用域分组:‘a1b2c3d4’.replace(/[a-z]\d{3}/g,‘X’)//“a1b2c3d4”‘a1b2c3d4’.replace(/([a-z]\d){3}/g,‘X’)//“Xd4"或使用|达到效果Byron|Casper’ByronCasper’.replace(/Byron|Casper/g,‘X’)// “XX”‘ByronsperByrCasper’.replace(/Byr(on|Ca)sper/g,‘X’)//“XX"反向引用2015-12-25 –> 12/25/2015'2015-12-25’.replace(/(\d{4})-(\d{2})-(\d{2})/g,’$2/$3/$1’)//“12/25/2015"忽略分组不希望捕获某些分组,只需要在分组内加上?:就可以(?:Byron).(ok)不让捕获分组前瞻正则表达式从文本头部向尾部开始解析,文本尾部方向前瞻就是在正则表达式匹配到规则的时候,向前检查是否符合断言,后顾/后瞻方向相反javascript不支持后顾符合和不符合特定断言称为肯定/正向匹配和否定/负向匹配前瞻’a23’.replace(/\w(?=\d)/g,‘X’)//“X2*3"JS对象属性对象属性g:global全文搜索,不添加,搜索到第一个匹配停止i:ignore case忽略大小写,默认大小写敏感m:multiple lines多行搜索lastIndex:是当前表达式匹配内容的最后一个字符的下一个位置source:正则表达式的文本内容框var reg1 = /\w/var reg2 = /\w/gimreg1.ignoreCase//falsereg1.multiline//falsereg1.global//falsereg2.ignoreCase//truereg2.multiline//truereg2.global//truetest和exec方法testRegExp.prototype.test(str)用于测试字符串参数中是否存在匹配正则表达式模式的字符串如果存在则返回true,不存在则返回falsevar reg1 = /\w/var reg2 = /\w/gwhile(reg2.test(‘ab’)){ console.log(reg2.lastIndex)}//1//2execRegExp.prototype.exec(str)使用正则表达式模式对字符串执行搜索,并将更新全局RegExp对象的属性以反映匹配结果如果没有匹配的文本则返回null,否则返回一个结果数组index声明匹配文本的第一个字符的位置input存放被检索的字符串string非全局调用调用非全局的RegExp对象的exec()时,返回数组第一个元素是与正则表达式相匹配的文本第二个元素是与RegExpObject的第一个子表达式相匹配的文本(如果有的话)第三个元素是与RegExp对象的第二个子表达式相匹配的文本(如果有的话),以此类推var reg3 = /\d(\w)(\w)\d/var reg4 = /\d(\w)(\w)\d/gvar ts = ‘1az2bb3cy4dd5cc’var ret = reg3.exec(ts)console.log(reg3.lastIndex+’\t’+ret.index+’\t’+ret.toString())//“0 0 1az2,a,z”//非全局lastIndex不生效,reg3.lastIndex并未生效console.log(reg3.lastIndex+’\t’+ret.index+’\t’+ret.toString())//“0 0 1az2,a,z"while(ret = reg4.exec(ts)){ console.log(reg4.lastIndex+’\t’+ret.index+’\t’+ret.toString())}//“4 0 1az2,a,z”//“10 6 3cy4,c,y"字符串对象方法String.prototype.search(reg)search()方法用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串方法返回第一个匹配结果index,查找不到返回-1search()方法不执行全局匹配,它将忽略标志g,并且总是从字符串的开始进行检索’dafd1afd’.search(/1/g)//4//输入数字也能匹配到,尝试往正则表达式转String.prototype.match(reg)match()方法将检索字符串,以找到一个或多个与regexp匹配的文本regexp是否具有标志g对结果影响很大非全局调用如果没有regexp没有标志g,那么match()方式就只能在字符串中执行一次匹配如果没有找到任何匹配的文本,将返回null找到了它将返回一个数组,其中存放了与它找到的匹配文本有关的信息返回数组的第一个元素存放的是匹配文本,而其余的元素存放的是与正则表达式的子表达式匹配的文本除了常规的数组元素之外,返回的数组还包含了两个对象属性index声明匹配文本的起始字符,在字符串的位置input声明,对stringObject的引用var reg3 = /\d(\w)\d/var reg4 = /\d(\w)\d/gvar ts = ‘$1a2b3c4d5e’var ret = ts.match(reg3)console.log(ret)console.log(ret.index+’\t’+reg3.lastIndex)//[“1a2”, “a”, index: 1, input: “$1a2b3c4d5e”, groups: undefined]// 1 0 全局调用如果regexp具有标志g则match()方法将执行全局检索,找到字符串中的所有匹配字符串没有找到任何匹配的字符串,则返回null如果找到了一个或多个匹配字符串,则返回一个数组数组元素中存放的是字符串中所有的匹配子串,而且也没有index属性或input属性var reg3 = /\d(\w)\d/var reg4 = /\d(\w)\d/gvar ts = ‘$1a2b3c4d5e’var ret = ts.match(reg4)console.log(ret)console.log(ret.index+’\t’+reg4.lastIndex)//[“1a2”, “3c4”]//undefined 0String.prototype.split(reg)我们经常使用split方法将字符串分割为字符数组’a,b,c,d’.split(’,’) //[“a”,“b”,“c”,“d”]在一些复杂的分割情况下我们可以使用正则表达式解决’a1b2c3d’.split(/\d/);//[“a”, “b”, “c”, “d”]‘a1.b|2c3d’.split(/.||/);//[“a1”, “b”, “2c3d”]String.prototype.replace()String.prototype.replace(str,replaceStr)‘a1b1c1’.replace(‘1’,2)//“a2b1c1"String.prototype.replace(reg,replaceStr)‘a1b1c1’.replace(/1/,2)//“a2b1c1"String.prototype.replace(reg,function)function会在每次匹配替换的时候调用,有四个参数1、匹配字符串2、正则表达式分组内容,没有分组则没有该参数3、匹配项在字符串中的index4、原字符串’a1b2c3d4’ => ‘a2b3c4d5’‘a1b2c3d4e5’.replace(/\d/g,function(match,index,origin){ console.log(index) return parseInt(match) + 1})// 1//3//5//7//9//“a2b3c4d5e6”‘a1b2c3d4e5’.replace(/(\d)(\w)(\d)/g,function(match,group1,group2,group3,index,origin){ console.log(index) return parseInt(group1)+parseInt(group3)})// 1//5//“a3c7e5” ...

January 28, 2019 · 1 min · jiezi

正则表达式-温故而知新

正则表达式重新整理学习,为了加深印象,发现了之前遗漏的一个非常重要的知识点优先选择最左端的匹配结果,这个规则js上创建正则的方式直接字面量的创建const r = /xx/; //通过双斜杠,在中间添加匹配的规则,这样就是一个正则表达式了通过构造函数创建const r = new RegExp(‘xx’, g) //通过构造函数来创建正则对象,第一个参数为匹配规则字符串,第二个参数可以添加修饰符,例如g,i,m,y正则的实例方法常用的exec,可以返回一个类数组,信息比较全const r = /a/gr.exec(‘asd’) //返回[“a”, index: 0, input: “asd”, groups: undefined]0: “a"groups: undefinedindex: 0input: “asd"length: 1__proto__: Array(0)可以看看返回的信息有些什么?一个元素代表:整体匹配返回的结果,如果正则中有子组匹配,那么会在index元素前返回,例如/(a)s/g.exec(‘afasg’) // 返回 [“as”, “a”, index: 2, input: “afasg”, groups: undefined]index就是匹配的索引位置信息input就是目标字符串是什么,groups这边返回undefined,因为正则中没用到;它是返回自定义名字的子匹配信息,例如/(?<groupone>a)s/g.exec(‘afasg’)//返回 [“as”, “a”, index: 2, input: “afasg”, groups: {groupone: “a”}]不常用的方法test,返回Boolean值,表示是否有匹配的字符串正则的重要规则比较全的规则,在这我讲讲几个比较重要的,常用的修饰符:i,m,g,y,修饰符之间可以同时使用i就是忽略大小写,例如验证码上的引用 => /a/i.test(‘A’) //truem就是能识别换行,能将判断出行末/a$/.test(‘a\nb’) //false /a$/m.test(‘a\nb’) //trueg是能进行连续的匹配,当匹配成功一次后还能继续匹配直到没有为止,并且可以通过lastIndex可以查看下一次匹配的开始的索引但是只对同一个正则有效,能连续指定,不然都是从零开始,这里的同一个是指内存地址相等,不是内容相等就够了const r = /a/g //这里通过变量r定义这样用r进行后面的操作都会是用一个r.lastIndex //0r.test(‘asdasd’) //truer.lastIndex //1y一般和g连用,y的作用是必须从一开始就匹配上,不然就false;相当于加了个^/a/y.test(‘ba’) //false/a/y.test(‘ab’) //true ,一般和g连用,进行连续头部匹配是否成功\b,\B用于匹配是否是词的边界,不存在/wbw/它的意思就是是否存在一个是词的边,但前面又有一个w,明显是不存在的(w等价于[A-Za-z0-9_])/\ba/g.test(‘ad’) //true /\ba/g.test(‘bad’) //false/\ba/g.test(‘c-ad’) //true \B就是取反不是词的边界[],()之间的区别,这两个分成常用[]仅仅代表一个字符串,不管里面写多少规划,最终还是会去匹配一个字符串 常用的有[xyz] //代表匹配x或者y或者z[^] //等价于[^’’],任意字符串[\S\s] //任意字符串 ,\s是匹配空格,换行(一些带有空格的u码,详细可以看链接) \S就是匹配除了\s意外的字符串()是用于取子匹配的值,又叫元组;有时候我们整体匹配上了,但又想在里面取某个值就可以加上个括号,结果中就会帮你返回,在将exec时以及提到了,这边不举例了可以和字符串的replace方法一起使用,进行负复杂的操作const r = /he(b)/g’afdhjbkhbgd’.replace(r, (match, $1) => { //match 是整体匹配的结果,$1是第一个元组返回值,当多个元组存在时是有外到里的 return xxxx}).代表一个字符,除了空格,类似换行;还有一些大于大于0xFFFF的字符,他会认为这是两个字符,所以用于简单的任意字符串匹配,可用.代表量词a{n}可以用来代表去匹配n个a,还可以写区间{1,3}匹配1到3个+,,?这些事贪婪匹配的量词,就是数量往大了取,不固定,但又范围+ //[1, Infinity] * //[0, Infinity] ? //[0, 1]如果在其后面再添加个?就是非贪婪匹配了,取值就尽量往小了取非贪婪匹配的作用在哪,可以通过一个简单的例子看一下//如果要匹配字符换’caabaaab’ ,如果只想取aab就会用到非贪婪匹配 通过/a+?b/g来匹配;用?来指定非贪婪匹配,加上b的限制,不会取到1;这边用+还是是一样的强大的断言可以理解成你是用于匹配的规则但不是我想要输出的内容非捕获组 用?:表示;说直白点就是:还是这么匹配但是我不返回括号里的内容,所以我们进行split的组匹配时可以进行修改console.log(“abc”.split(/(?:b)/)); //[“a”, “c”]先行断言 用x(?=y)表示,意思是匹配一个x,x必须在y前面,结果中不返回y先行否定断言 用x(?!y)表示,意思是匹配一个x,x后面不能是y,结果中不返回y后行断言 用(?<=y)x表示,意思是匹配一个x,x前面必须是y,结果中不返回y后行否定断言 用(?<!=y)x表示,意思是匹配一个x,x前面不能是y,结果中不返回yconsole.log(/(?<!=b)a/.exec(“bcaba”)); //[“a”, index: 2, input: “bcaba”, groups: undefined]console.log(/(?<=b)a/.exec(“bcaba”)); //[“a”, index: 4, input: “bcaba”, groups: undefined]最重要最优先的规则优先选择最左端的匹配结果 可以通过几个例子自证一下/a+?b/.exec(‘baaabab’) //结果[“aaab”, index: 1, input: “baaabab”, groups: undefined],因为先匹配上了aaab,所以忽略非贪婪原则不输出ab或者b/a*?/.exec(‘baaa’)//返回[””, index: 0, input: “baaa”, groups: undefined] 因为先匹配上了空串所以结果为空串常见的正则例子去除首尾空格 ’ fds df ‘.replace(/^\s*|\s*$/g, ‘’)去除首尾空格 ’ fds df ‘.replace(/\s*(?=\S*)/g, ‘’)千分位数字格式化 ‘1234567890’.replace(/\B(?=((\d{3})+$))/g, ‘.’); 如果有小数, (‘1234567890.123’).replace(/\B(?=((\d{3})+.))/g, ‘,’) ...

January 28, 2019 · 1 min · jiezi

正则整理

一年没写过文章了,一年来对前端也有了更多的了解。正则表达式,通用于很多语言,使用正则可以少走很多弯路。1、正则实际上就是一个字符串。es6里面有一个拼接字符串的写法 是: string${变量名字}let name = ‘测试’; hello_name = hello ${name};等同于hello_name = ‘hello’ + name;这个和正则的写法很类似正则也是各种常量与变量的拼接比如想匹配一个 ‘my age is 数字’ 的 字符串 ,那么可以写成/my age is (\d{1, 2})//my age is (\d{1, 2})/.test(‘my age is 12’) // true/my age is (\d{1, 2})/.test(‘my age are 12’) // false 这里 \d 表示 数字, {1, 2}表示前一位(也就是\d)的个数在1~2之间 不过类似于 {1, 2} 这样的的位数匹配符 是默认和他的前一位捆绑的 所以 ()可以省略 那么就是写成 /my age is \d{1, 2}/但是如果你只需要‘my age is 数字’这句话,不需要像my age is 33456 hhhh ,有其他多余的内容那么就需要一些限定符号 /^my age is \d{1, 2}$/ /^my age is \d{1, 2}$/.test(‘my age is 12’) // true /^my age is \d{1, 2}$/.test(‘my age is 123’) // false /^my age is \d{1, 2}/.test(‘my age is 123456’) // true 这个没用$ ^表示起始 $表示结尾 说明这句话是以m开头,以一到俩位数的年龄结尾类似于 \d的符号 还有 很多 ,常用的有 \d \s \w 等,具体的表示含义可以参考文档2、正则括号的使用。正则中() {} [] 都会用到,没一个的含义都不一样()多用于 合并正则 默认 都是 相互独立比如 /ab{3}/ 那么他只会匹配3次b ,但是只匹配1次a如果写成 /(ab){3}/那么就会匹配3次 ‘ab’[]多用于 筛选比如 \d 实际上可以写成 [0123456789]正则会从[]的集合中去匹配,只要符合其中的一个,就算匹配成功/^[ab]c$/ 匹配 ac 和 bc 都会成功 ,但是匹配 abc 不会成功[]中可以在最前面加一个^表示取反比如 [^ab]表示匹配非 ab 的字符/[^ab]/.test(‘a’) //false/[^ab]/.test(‘x’) //true比如 [^\d] 可以表示 匹配 非数字(当然也可以写\D 来表示 匹配非数字){}多用于位数限定 {1} 表示限定 为 一个 {1, 2} 表示限定 为 1到2个这些目前就是正则的最基本用法如果有需要帮忙指导的可以下面留言。 ...

January 22, 2019 · 1 min · jiezi

正则表达式

校验基本日期格式:var reg = /^(\d{1,4})(-|/)(\d{1,2})\2(\d{1,2})$/;var r = fieldValue.match(reg);if (r==null) { alert(‘Date format error!’);}1 . 校验密码强度密码的强度必须是包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间。var reg = /^(?=.\d)(?=.[a-z])(?=.[A-Z]).{8,10}$/;校验中文字符串仅能是中文。var reg = /^[\u4e00-\u9fa5]{0,}$/;由数字、26个英文字母或下划线组成的字符串var reg = /^\w+$/;校验E-Mail 地址同密码一样,下面是E-mail地址合规性的正则检查语句。var reg = /[\w!#$%&’+/=?^_{|}~-]+(?:\.[\w!#$%&’+/=?^_{|}~-]+)@(?:\w?.)+\w?/;校验身份证号码下面是身份证号码的正则校验。15 或 18位。15位:var reg = /^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$/;18位:var reg = /^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/;校验日期“yyyy-mm-dd“ 格式的日期校验,已考虑平闰年。var reg = /^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|2468|13579)|(?:0[48]|2468|13579)00)-02-29)$/;校验金额金额校验,精确到2位小数。var reg = /^[0-9]+(.[0-9]{2})?$/;校验手机号下面是国内 13、15、18开头的手机号正则表达式。(可根据目前国内收集号扩展前两位开头号码)var reg = /^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$/;判断IE的版本IE目前还没被完全取代,很多页面还是需要做版本兼容,下面是IE版本检查的表达式。var reg = /^.MSIE 5-8?(?!.Trident/[5-9].0).$/;校验IP-v4地址IP4 正则语句。var reg = /\b(?:(?:25[0-5]|20-4|[01]?0-9?).){3}(?:25[0-5]|20-4|[01]?0-9?)\b/;校验IP-v6地址IP6 正则语句。var reg = /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/;检查URL的前缀应用开发中很多时候需要区分请求是HTTPS还是HTTP,通过下面的表达式可以取出一个url的前缀然后再逻辑判断。var reg = /^[a-zA-Z]+:///;if (!s.match(/^[a-zA-Z]+:///)) { s = ‘http://’ + s;}提取URL链接下面的这个表达式可以筛选出一段文本中的URL。var reg = /^(f|ht){1}(tp|tps)://([\w-]+.)+[\w-]+(/[\w- ./?%&=])?/;文件路径及扩展名校验验证windows下文件路径和扩展名(下面的例子中为.txt文件)var reg = /^([a-zA-Z]:|\)\(1+\)2+.txt(l)?$/;提取Color Hex Codes有时需要抽取网页中的颜色代码,可以使用下面的表达式。var reg = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;提取网页图片假若你想提取网页中所有图片信息,可以利用下面的表达式。var reg = /&lt; [img]3[src] = ["']{0,1}(4)/;提取页面超链接提取html中的超链接。var reg = /(])(href=“https?://)((?!(?:(?:www.)?’.implode(’|(?:www.)?’, $follow_list).’))5+)”((?!.\brel=)6)(?:6)>/;查找CSS属性通过下面的表达式,可以搜索到相匹配的CSS属性。var reg = /^\s[a-zA-Z-]+\s[:]{1}\s[a-zA-Z0-9\s.#]+[;]{1}/;抽取注释如果你需要移除HMTL中的注释,可以使用如下的表达式。var reg = /<!–(.?)–>/;匹配HTML标签通过下面的表达式可以匹配出HTML中的标签属性。var reg = /</?\w+((\s+\w+(\s=\s(?:".?"|’.?’|[^’">\s]+))?)+\s|\s)/?>/;\ ↩/:?"<>| ↩\> ↩"'\ > ↩" ↩> ↩ ...

January 17, 2019 · 1 min · jiezi

Nunjucks使用正则表达式示例

我在使用egg.js时,他用的模板引擎是Nunjucks,其中有个地方需要用到正则,可是官方文档基本上写了跟没写一样,官方的正则表达式。于是我便去找例子了。正则表达式在Nunjucks中使用正则表达式的示例:{% set regExp = r/^foo.*/g %}{% if regExp.test(‘foo’) %} Foo in the house!{% endif %}那么这个就会被正常显示。其他的表达式也是可以的。例如:<!– 有个后台存储的未验证的手机号码(mobile)在前端显示,如果格式正确则显示,不正确则显示“暂无” –>{% set regExp = r/^\d{11}$/g %}<span>号码:{{mobile if regExp.test(mobile) else ‘暂无’}}</span>这两个例子应该看得懂吧。正则这块我并没有看源码,因为搜索出来了,我这里参考的regex exmaple?后来发现其实很多方法文档并没有写出来,这时候可能真的需要看看源码了,有兴趣的话可以阅读下filter的源码https://github.com/mozilla/nu…

January 15, 2019 · 1 min · jiezi

搞定PHP面试 - 正则表达式知识点整理

一、简介1. 什么是正则表达式正则表达式(Regular Expression)就是用某种模式去匹配一类字符串的一种公式。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。正则表达式是繁琐的,但它是强大的,学会之后的应用会让你除了提高效率外,会给你带来绝对的成就感。只要认真阅读本教程,加上应用的时候进行一定的参考,掌握正则表达式不是问题。许多程序设计语言都支持利用正则表达式进行字符串操作。2. 正则表达式的作用分割,查找,匹配,替换字符串3. PHP中的正则表达式在PHP中有两套正则表达式函数库,两者功能相似,只是执行效率略有差异:一套是由 PCRE(Perl Compatible Regular Expression) 库提供的。使用“preg_”为前缀命名的函数;一套由 POSIX(Portable Operating System Interface of Unix )扩展提供的。使用以“ereg_”为前缀命名的函数;PCRE来源于Perl语言,而Perl是对字符串操作功能最强大的语言之一,PHP的最初版本就是由Perl开发的产品。PCRE语法支持更多特性,比POSIX语法更强大。因此,本文主要介绍 PCRE 语法的正则表达式4. 正则表达式的组成在PHP中,一个正则表达式分为三个部分:分隔符、表达式和模式修饰符。分隔符分隔符可以使用除字母、数字、反斜线()和空白字符之外的任意 ascii 字符。最常用的分隔符有正斜线(/)、hash符号(#) 以及取反符号()。表达式有一些特殊字符和非特殊的字符串组成。是决定正则表达式匹配规则的主要部分。模式修饰符用于开启和关闭某些特定的功能/模式。二、分隔符1. 分隔符的选择当使用 PCRE 函数的时候,正则表达式必须由分隔符闭合包裹。分隔符可以使用除字母、数字、反斜线()和空白字符之外的任意 ascii 字符。最常用的分隔符有正斜线(/)、hash符号(#) 以及取反符号()。/foo bar/ (合法)#^[^0-9]$# (合法)+php+ (合法)%[a-zA-Z0-9_-]% (合法)#[a-zA-Z0-9_-]/ (非法,两边的分隔符不同)a[a-zA-Z0-9_-]a (非法,分隔符不能是字母)[a-zA-Z0-9_-]\ (非法,分隔符不能是反斜线(\))除了上面提到的分隔符,也可以使用括号样式的分隔符,左括号和右括号分别作为开始和结束 分隔符。{this is a pattern}2. 分隔符的使用如果分隔符 在正则表达式中使用,它必须使用反斜线()进行转义。果分隔符经常在正则表达式内出现, 最好使用其他分隔符来提高可读性。/http:///#http://#需要将一个字符串放入正则表达式中使用时,可以用 preg_quote() 函数对其进行转义。 它的第二个参数(可选)可以用于指定需要被转义的分隔符。//在这个例子中,preg_quote($word) 用于保持星号和正斜杠(/)原文涵义,使其不使用正则表达式中的特殊语义。$textBody = “This book is /very/ difficult to find.”;$word = “/very/”;$reg = “/” . preg_quote($word, ‘/’) . “/";echo $reg; // 输出 ‘/*/very/*/’echo preg_replace ($reg, “<i>” . $word . “</i>”, $textBody); // 输出 ‘This book is <i>/very/</i> difficult to find.‘可以在结束分隔符后面增加模式修饰符来影响匹配效果。下面的例子是一个大小写不敏感的匹配#[a-z]#i三、元字符1. 转义符字符描述\将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用。例如,’n’ 匹配字符 “n”。’n’ 匹配一个换行符。序列 ‘' 匹配 "” 而 “(” 则匹配 “("。2. 定位符字符描述^匹配输入字符串的开始位置 (或在多行模式下是行首)$匹配输入字符串的结束位置 (或在多行模式下是行尾)\b匹配一个单词边界,即字与空格间的位置\B非单词边界匹配3. 限定符字符描述匹配前面的子表达式零次或多次。例如,zo 能匹配 “z” 以及 “zoo”。 等价于{0,}。+匹配前面的子表达式一次或多次。例如,‘zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}。?当该字符作为量词,表示匹配前面的子表达式零次或一次。例如,“do(es)?” 可以匹配 “do” 或 “does” 。? 等价于 {0,1}。{n}n 是一个非负整数。匹配确定的 n 次。例如,‘o{2}’ 不能匹配 “Bob” 中的 ‘o’,但是能匹配 “food” 中的两个 o。{n,}n 是一个非负整数。至少匹配n 次。例如,‘o{2,}’ 不能匹配 “Bob” 中的 ‘o’,但能匹配 “foooood” 中的所有 o。‘o{1,}’ 等价于 ‘o+’。‘o{0,}’ 则等价于 ‘o’。{n,m}m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,“o{1,3}” 将匹配 “fooooood” 中的前三个 o。‘o{0,1}’ 等价于 ‘o?’。请注意在逗号和两个数之间不能有空格。4. 通用字符字符描述\d匹配一个数字字符。等价于 [0-9]。\D匹配一个非数字字符。等价于 [^0-9]。\w匹配字母、数字、下划线。等价于 [A-Za-z0-9_]。\W匹配非字母、数字、下划线。等价于 [^A-Za-z0-9_]。\s匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。\S匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。.匹配除换行符(n、r)之外的任何单个字符。要匹配包括 ’n’ 在内的任何字符,请使用像”(.n)“的正则表达式。5. 非打印字符字符描述\n匹配一个换行符。等价于 x0a 和 cJ。\r匹配一个回车符。等价于 x0d 和 cM。\t匹配一个制表符。等价于 x09 和 cI。6. 多选分支符字符描述|竖线字符 | 可以匹配多选一的情况。例如,‘z|food’ 能匹配 “z” 或 “food”。’(z|f|g)ood’ 则匹配 “zood”、“food"或 “good”。7. 字符组字符描述[x|y]匹配 x 或 y。例如,‘z|food’ 能匹配 “z” 或 “food”。’(z|f)ood’ 则匹配 “zood” 或 “food”。[xyz]字符集合。匹配所包含的任意一个字符。例如, [abc] 可以匹配 “plain” 中的 ‘a’。[^xyz]负值字符集合。匹配未包含的任意字符。例如, [^abc] 可以匹配 “plain” 中的’p’、’l’、‘i’、’n’。[a-z]字符范围。匹配指定范围内的任意字符。例如,[a-z] 可以匹配 ‘a’ 到 ‘z’ 范围内的任意小写字母字符。[^a-z]负值字符范围。匹配任何不在指定范围内的任意字符。例如,[^a-z] 可以匹配任何不在 ‘a’ 到 ‘z’ 范围内的任意字符。8. 非贪婪匹配符字符描述?当该字符紧跟在任何一个其他限制符 (, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 “oooo”,‘o+?’ 将匹配单个 “o”,而 ‘o+’ 将匹配所有 ‘o’。9. ( )分组字符描述(pattern)匹配 pattern 并获取这一匹配。要匹配圆括号字符,请使用 ( 或 )。(?:pattern)匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 “或” 字符 (|) 来组合一个正则表达式的各个部分是很有用。例如, ‘industr(?:y|ies) 就是一个比 ‘industry|industries’ 更简略的表达式。(?=pattern)正向肯定预查(look ahead positive assert),在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000)“能匹配"Windows2000"中的"Windows”,但不能匹配"Windows3.1"中的"Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。(?!pattern)正向否定预查(negative assert),在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如"Windows(?!95|98|NT|2000)“能匹配"Windows3.1"中的"Windows”,但不能匹配"Windows2000"中的"Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。(?<=pattern)反向(look behind)肯定预查,与正向肯定预查类似,只是方向相反。例如,”(?<=95|98|NT|2000)Windows"能匹配"2000Windows"中的"Windows",但不能匹配"3.1Windows"中的"Windows"。(?<!pattern)反向否定预查,与正向否定预查类似,只是方向相反。例如"(?<!95|98|NT|2000)Windows"能匹配"3.1Windows"中的"Windows",但不能匹配"2000Windows"中的"Windows"。四、模式修饰符1. i(不区分大小写)如果设置了这个修饰符,正则表达式中的字母会进行大小写不敏感匹配。2. m(多行模式)默认情况下,PCRE 认为目标字符串是由单行字符组成的(然而实际上它可能会包含多行)。“行首"元字符 (^) 仅匹配字符串的开始位置, 而"行末"元字符 ($) 仅匹配字符串末尾, 或者最后的换行符(除非设置了 D 修饰符)。当这个修饰符设置之后,“行首”元字符 (^) 和“行末”元字符 ($) 就会匹配目标字符串中任意换行符之前或之后,另外,还分别匹配目标字符串的最开始和最末尾位置。如果目标字符串 中没有 “n” 字符,或者正则表达式中没有出现 ^ 或 $,设置这个修饰符不产生任何影响。3. s(点号通配模式)默认情况下,点号(.)不匹配换行符。如果设置了这个修饰符,正则表达式中的点号元字符匹配所有字符,包含换行符。4. U(贪婪模式)这个修饰符与前面提到的 ? 作用相同,使正则表达式默认为非贪婪匹配,通过量词后紧跟 ? 的方式可以使其转为贪婪匹配。在非贪婪模式,通常不能匹配超过 pcre.backtrack_limit 的字符。贪婪模式$str = ‘<b>abc</b><b>def</b>’;$pattern = ‘/<b>.</b>/’;preg_replace($pattern, ‘\1’, $str);.会匹配 abc</b><b>def非贪婪模式方法一、使用 ? 转为非贪婪模式$str = ‘<b>abc</b><b>def</b>’;$pattern = ‘/<b>.?</b>/’;preg_replace($pattern, ‘\1’, $str);.会分别匹配 abc,def方法二、使用修饰符 U 转为非贪婪模式$str = ‘<b>abc</b><b>def</b>’;$pattern = ‘/<b>.</b>/U’;preg_replace($pattern, ‘\1’, $str);5. u(支持UTF-8转义表达)此修正符使正则表达式和目标字符串都被认为是 utf-8 编码。无效的目标字符串会导致 preg_* 函数什么都匹配不到;无效的正则表达式字符串会导致 E_WARNING 级别的错误。$str = ‘中文’;$pattern = ‘/^[\x{4e00}-\x{9fa5}]+$/u’;if (preg_match($pattern, $str)) { echo ‘该字符串全是中文’;} else { echo ‘该字符串不全是中文’;}6. D(结尾限制)默认情况下,如果使用 $ 限制结尾字符,当字符串以一个换行符结尾时, $符号还会匹配该换行符(但不会匹配之前的任何换行符)。如果设置这个修饰符,正则表达式中的 $ 符号仅匹配目标字符串的末尾。如果设置了修饰符 m,这个修饰符被忽略。7. x如果设置了这个修饰符,正则表达式中的没有经过转义的或不在字符类中的空白数据字符总会被忽略, 并且位于一个未转义的字符类外部的#字符和下一个换行符之间的字符也被忽略。8. A如果设置了这个修饰符,正则表达式被强制为"锚定"模式,也就是说约束匹配使其仅从 目标字符串的开始位置搜索。9. S当一个正则表达式需要多次使用的时候,为了得到匹配速度的提升,值得花费一些时间对其进行一些额外的分析。如果设置了这个修饰符,这个额外的分析就会执行。当前,这种对一个正则表达式的分析仅仅适用于非锚定模式的匹配(即没有单独的固定开始字符)。五、反向引用使用 ( ) 标记的开始和结束的多个原子,不仅是一个独立的单元,也是一个子表达式。在一个 ( ) 中的子表达式外面,反斜线紧跟一个大于 0 的数字,就是对之前出现的某个子表达式的后向引用。后向引用用于重复搜索前面某个 ( ) 中的子表达式匹配的文本。1. 在正则表达式中使用反向引用(sens|respons)e and \1ibility 将会匹配 ”sense and sensibility” 和 ”response and responsibility”, 而不会匹配 ”sense and responsibility”2. 在PCRE函数中使用反向引用<?php$str = ‘<b>abc</b><b>def</b>’;$pattern = ‘/<b>(.)</b><b>(.)</b>/’;$replace = preg_replace($pattern, ‘\1’, $str);echo $replace . “\n”;$replace = preg_replace($pattern, ‘\2’, $str);echo $replace . “\n”;输出:abcdef六、正则表达式常用PCRE函数PHP官网的讲解已经很详细了,这里不再做多余的论述执行正则表达式匹配 preg_match()执行正则表达式全局匹配 preg_match_all()执行一个正则表达式的搜索和替换 preg_replace()执行一个正则表达式搜索并且使用一个回调进行替换 preg_replace_callback()执行多个正则表达式搜索并且使用对应回调进行替换 preg_replace_callback_array()通过一个正则表达式分隔字符串 preg_split()七、应用实践1. 正则表达式匹配中文UTF-8汉字编码范围是 0x4e00-0x9fa5在ANSI(GB2312)环境下,0xb0-0xf7,0xa1-0xfeUTF-8要使用 u模式修正符 使模式字符串被当成 UTF-8在ANSI(GB2312)环境下,要使用chr将Ascii码转换为字符UTF-8<?php$str = ‘中文’;$pattern = ‘/[\x{4e00}-\x{9fa5}]/u’;preg_match($pattern, $str, $match);var_dump($match);ANSI(GB2312)<?php$str = ‘中文’;$pattern = ‘/[’.chr(0xb0).’-’.chr(0xf7).’][’.chr(0xa1).’-’.chr(0xfe).’]/’;preg_match($pattern, $str, $match);var_dump($match);2. 正则表达式匹配页面中所有img标签中的src的值。<?php$str = ‘<img alt=“高清大图” id=“color” src=“color.jpg” />’;$pattern = ‘/<img.?src=”(.?)".*?/?>/i’;preg_match($pattern, $str, $match);var_dump($match); ...

January 8, 2019 · 2 min · jiezi

【剑指offer】2.替换空格

题目描述请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。题目说的不太严谨:1.能不能允许连续出现多个空格?2.若有可能连续多个空格,用多个还是单个20%进行替换?分三种情况解答1.不会出现连续多个空格:直接用空格将字符串切割成数组,在用20%进行连接。function replaceSpace(str){ return str.split(’ ‘).join(’%20’);}2.允许出现多个空格,每个空格均用一个20%替换:用正则表达式找到所有空格依次替换function replaceSpace(str){ return str.replace(/\s/g,’%20’);}3.允许出现多个空格,多个空格用一个20%替换:用正则表达式找到连续空格进行替换function replaceSpace(str){ return str.replace(/\s+/g,’%20’);}

January 8, 2019 · 1 min · jiezi

sublime 查找所有超链接 正则替换

sublime正则批量替换.find-and-replace-sublime<a href="(.*?)">

December 28, 2018 · 1 min · jiezi

JS正则表达式入门

什么是正则表达式?正则表达式其实就是,在一个字符串序列中,按照你自己想要的匹配模式,将字符串搜索或替换的过程正则表达式结构/正则表达式主体/修饰符(可选)//实例如下:const patr = /china/i解析: /china/i 是一个正则表达式,其中china是一个正则表达式主体,i 是一个修饰符(搜索不区分大小写)使用正则表达式方法search方法检索字符串中指定的字符串,并且返回子串的起始位置const str = “hello world!";const n = str.search(/hello/);console.log(n); //输出结果为0replace方法在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串const str = “hello world!";const newstr = str.replace(/hello/, ‘hi’);console.log(newstr); //输出结果为hi world!match方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配,但是它返回指定的值,而不是字符串的位置 const str = ‘hello world!’; const arr = str.match(/o/g); console.log(arr); //输出结果[‘o’, ‘o’]test方法用于检测一个字符串是否匹配某个模式,如果字符串中含有匹配的文本,则返回 true,否则返回 false const str = ‘hello word!’; const regx = /o/; const flag = regx.test(str); console.log(flag); //输出结果trueexec方法返回一个数组,其中存放匹配的结果。如果未找到匹配,则返回值为 null const str = ‘abc123abc321’; const pattern = /^abc/g; const flag = pattern.exec(str); console.log(flag); //[“abc”, index: 0, input: “abc123abc321”, groups: undefined]常见的正则表达式修饰符i 不区分(ignore)大小写/abc/i 可以匹配 abc、aBC、Abc g 全局(global)匹配如果不带g,从左往右搜索,找到第一个符合条件就返回;如果带g,则从左往右,找到每个符合条件的都记录下来,如下: const str = ‘hello world!’; const n1 = str.match(/o/g); console.log(n1); //输出结果[“o”, “o”] const n2 = str.match(/o/); console.log(n2); //输出结果[“o”, index: 4, input: “hello world!"]m 多(more)行匹配如果字符串中存在n并且有开始^或结束$符的情况下,会默认作为一个换行符,g只匹配第一行,加上m则可以匹配多行,如下: const str = ‘hello\nworld\nhello world\ngood hello’; const n1 = str.match(/^hello/gm); console.log(n1); //输出结果:[“hello”, “hello”] const n2 = str.match(/world$/g); console.log(n2); //输出结果:nulln2输出为null,是因为没有m则只匹配第一行,第一行中没有与之匹配的字符串,所有输出null正则表达式语法定位符^ 符:匹配字符串开始的位置 const str = ‘hello world!’; const pattr1 = /^hello/; const flag1 = pattr1.test(str); console.log(flag1); //输出结果:true const pattr2 = /^world/; const flag2 = pattr2.test(str); console.log(flag2); //输出结果:false$ 符:匹配字符串结束的位置 const str = ‘hello world’; const pattr1 = /hello$/; const flag1 = pattr1.test(str); console.log(flag1); //输出结果:false const pattr2 = /world$/; const flag2 = pattr2.test(str); console.log(flag2); //trueb 符:单词边界匹配,匹配单词的开头或结尾的位置,匹配的是一个位置 const str = ‘word’; const pattern1 = /word\b/; const flag1 = pattern1.test(str); console.log(flag1); //true const pattern2 = /\bword/; const flag2 = pattern2.test(str); console.log(flag2); //true const str1 = ‘hello,world’ const pattern3 = /hello\b/; const flag3 = pattern3.test(str1); console.log(flag3); //true const pattern4 = /hello\bworld/; const flag4 = pattern4.test(str1); console.log(flag4); //false如果b在模式的开头和结尾那没有问题,但是如果b在两个单词之间,则得都符合b左右匹配的模式B 符:非单词边界匹配,其实就是b的相反,b匹配的是单词,而B匹配的是非单词 const str = ‘hello, 你好!我是小明!’; const pattern1 = /\B/; const arr1 = str.split(pattern1); console.log(arr1); // 输出结果:[“h”, “e”, “l”, “l”, “o,”, " “, “你”, “好”, “!”, “我”, “是”, “小”, “明”, “!”] const pattern2 = /\b/; const arr2 = str.split(pattern2); console.log(arr2); // 输出结果:[“hello”, “, 你好!我是小明!"]从上面例子中可以看出,b是可以把单词匹配出来,而B不管是什么都分隔,如果在一个字符串中,有中英文,符合等等组成,如果要把里面的单词单独提取出来,则可以使用b来提取单词特殊字符. 符:匹配除换行符以外的任意单字符 const str = ‘\n’; const pattern = /./gm; const flag1 = pattern.test(str); console.log(flag1); //falsew 符:匹配字母或数字或下划线(等价于’[A-Za-z0-9_]’) const str = ‘^^##hello, 123’; const pattern = /^\w/; const flag1 = pattern.test(str); console.log(flag1); //false const str = ‘你好hello!’; const pattern = /^\w/; const flag = pattern.test(str); console.log(flag); // false如果头部是汉字或者符号(除下划线),则返回falseW:用于匹配所有与w不匹配的字符s 符:匹配任意的空白符(等价于 [ fnrtv]) const str = ‘abc’; const pattern = /\s/; const flag1 = pattern.test(str);; console.log(flag1); // false const str1 = ‘abc cde’; const flag2 = pattern.test(str1); console.log(flag2); // trueS 符:匹配除单个空格符之外的所有字符(非s)d 符:匹配数字 const str = ‘123’; const pattern = /\d/; const flag1 = pattern.test(str); console.log(flag1); // true const str1 = ‘abc’; const flag2 = pattern.test(str1); console.log(flag2); // false限定符* 符:匹配前面的子表达式重复出现0次或多次(可有可无) const str = ‘123abc###’; const pattern = /(\d)/; const flag1 = pattern.test(str); console.log(flag1); // true+ 符:匹配前面的子表达式重复出现一次或更多次(至少一次) const str = ‘abc###’; const pattern = /(\d)+/; const flag1 = pattern.test(str); console.log(flag1); // false const str2 = ‘123’; const flag2 = pattern.test(str2); console.log(flag2); // true? 符:匹配前面的子表达式重复出现零次或一次 const str = ’eee123’; const pattern = /h+?/; const flag = pattern.test(str); console.log(flag); //false当?跟在+后面的时,则此时必须出现一次,?一般都是跟在限定符后面的{n} 符:匹配确定的 n 次(n为非负数整数) const str = ’type’; const pattern = /t{2}/; const flag = pattern.test(str); console.log(flag); // falset得要有两个才能匹配,这里不满足2个,所有返回false{n,} 符:至少匹配n 次(n为非负整数) const str = ’ttypet’; const pattern = /t{2,}/; const flag = pattern.test(str); console.log(flag); // true这里t至少得重复出现2次才可以被匹配{n,m} 符:最少匹配 n 次且最多匹配 m 次(n<m) const str = ‘food’; const pattern = /o{1,4}/; const flag = pattern.test(str); console.log(flag); // true //o出现2次,1<2<4 const str = ‘food’; const pattern = /o{3,4}/; const flag = pattern.test(str); console.log(flag); // false //o出现2次,不属于3到4的范围方括号[abc]:查找方括号之间的任何字符 const str = ‘my name is lucy!’; const pattern = /^[lpo]/; const flag = pattern.test(str); console.log(flag); // falseconst str = ‘my name is lucy!’;const pattern = /^[mpo]/;const flag = pattern.test(str);console.log(flag); // true1:查找任何不在方括号之间的字符 const str = ‘my name is lucy!’; const pattern = /^[^mpo]/; const flag = pattern.test(str); console.log(flag); // false const str = ‘my name is lucy!’; const pattern = /^[^lpo]/; const flag = pattern.test(str); console.log(flag); // true[0-9]:查找任何从 0 至 9 的数字 const str = ‘hello,123’; const pattern = /[0-9]$/; const flag = pattern.test(str); console.log(flag); // true[a-z]:查找任何从小写 a 到小写 z 的字符 const str = ‘hello,123’; const pattern = /^[a-z]/; const flag = pattern.test(str); console.log(flag); // true[A-Z]:查找任何从大写 A 到大写 Z 的字符 const str = ‘hello,123’; const pattern = /^[A-Z]/; const flag = pattern.test(str); console.log(flag); // false简单正式表达式实例1.只能是数字或英文 const str1 = ‘123456’; const str2 = ‘你好,123’; const str3 = “”; const pattern = /^[a-zA-Z0-9]+$/; const flag1 = pattern.test(str1); const flag2 = pattern.test(str2); const flag3 = pattern.test(str3); console.log(flag1); // true console.log(flag2); // false console.log(flag3); // false2.中英文开头 const str1 = ‘中文+++123’; const str2 = ‘word123你好’; const str3 = ‘321wrod’; const pattern = /^[\u4e00-\u9fa5A-Za-z]+/; const flag1 = pattern.test(str1); const flag2 = pattern.test(str2); const flag3 = pattern.test(str3); console.log(flag1); // true console.log(flag2); // true console.log(flag3); // false这里开头以中英文开头,则^[\u4e00-\u9fa5A-Za-z]+除了开头要中英文,后面部分随意都可以,没有限制3.校验数值最多保留两位小数 const str1 = 45; const str2 = 45.5; const str3 = 45.55; const str4 = 0.111; const pattern = /^[0-9]+(.[0-9]{0,2})?$/; const flag1 = pattern.test(str1); const flag2 = pattern.test(str2); const flag3 = pattern.test(str3); const flag4 = pattern.test(str4); console.log(flag1); // true console.log(flag2); // true console.log(flag3); // true console.log(flag4); // false这里可以分成两部分,一部分是整数部分,如下:^[0-9]+再一部分是小数部分,如下:(.[0-9]{0,2})?小数可以保留一位或者两位或者不保, ? 表示0次或者一次4.网址URL验证 const str1 = ‘www.baidu.com?id=123'; const str2 = ‘cnds.tang-123/china’; const str3 = ‘www.baidu.com\311’; const pattern = /^([\w-]+.)+[\w-]+([\w-./?%&=])$/; const flag1 = pattern.test(str1); const flag2 = pattern.test(str2); const flag3 = pattern.test(str3); console.log(flag1); // true console.log(flag2); // true console.log(flag3); // false开头只能是数字或字母或下划线或-至少得一个,则:^([\w-]+.)+中间部分也至少得有一次[\w-]+到后面的那部分随意,可有可无([\w-./?%&=]*)本章节主要就是简单介绍了正则表达式的一些常用的东西,有不对的地方大家可以指出哈abc ↩ ...

December 18, 2018 · 4 min · jiezi

29.22分钟学会书写正则(2)

写在前面的一些废话没有看过上一篇文章的盆友有福了!今天!没错!就是现在!我将免费!all f*cking FREE!免费将上篇文章的链接发出来! 这里是上篇上回说了怎么写出正则,这次展示下在js中使用正则的场景!正则对象属性javascript的正则对象有以下几个属性,其中前面三个也叫修饰符(也就是/表达式/两条杠后面的字符,比如上一篇文章出现的 /\bis\b/g 的这个g)。global:是否全文搜索,默认falseignoreCase:是否大小写敏感,默认false,即不忽略大小写multiline:是否多行搜索,默认falselastIndex:当前表达式匹配内容的最后一个字符的下一个位置source:正则表达式的文本字符串,也就是“/表达式/修饰符”中的表达式,var reg=/\bis\b/g; reg.source就是\bis\b正则相关的方法js中,RegExp对象有两个内置方法testexec此外,还有一些String对象的方法也支持正则表达式,它们是searchmatchsplitreplacetest()test() 方法用于检测一个字符串是否匹配某个模式,返回true或者false.var reg = /\w/;reg.test(‘abc’);//truereg.test(‘abc’);//truereg.test(‘abc’);//truereg.test(’@#$%’)//falsereg.test(’@#$%’)//false//为什么要多执行几遍呢?你可能会以为楼主lu多了导致老眼昏花多输入了几遍。but NO!多执行几遍是为了和下面作对比。当我们的正则表达式加上了g修饰符以后,这个方法出现了一些不同var reg = /\w/g;reg.test(‘abc’);//truereg.test(‘abc’);//truereg.test(‘abc’);//truereg.test(‘abc’);//false为什么会这样呢?因为当我们加上全局搜索这个修饰符后,test()方法会返回结果,并且更新reg对象的属性(lastIndex),他会在上一次lastIndex的位置开始往后查找,而不是从头开始。所以这个方法建议不要加g,如果你这个人比较倔强,非要加的话,你可以每次都重新初始化一个正则对象,因为它第一次的结果是和没有加g的时候是一样的。Like this。var reg = /\w/g;reg.test(‘abc’);//true//每次都初始化正则对象,把这两行写在一行里比较好复制,因为分开复制一不小心就出现了上面的问题exec()exec()方法用于使用正则表达式对字符串执行搜索,并将更新全局RegExp对象的属性以反应匹配结果如果没有匹配到文本则返回null,否则返回一个结果数组:数组第一个元素是与正则表达式相匹配的文本第二个元素是与正则表达式的第一个子表达式(也就是分组1)相匹配的文本(如果有分组1的话)第三个元素是与正则表达式的第二个子表达式(分组2)相匹配的文本(如果有分组2的话)除了数组元素和 length 属性之外,exec() 方法还返回两个属性。index 属性声明的是匹配文本的第一个字符的位置。input 属性则存放的是被检索的字符串 string。var reg = /(\d)(\w)/ //上篇文章已经介绍过分组了,这里的两个括号分别是分组1和分组2reg.exec(‘1a2b3c4d5e’);当我们的正则表达式加上了g修饰符以后,这个方法又出现了一些不同var reg = /(\d)(\w)/greg.exec(‘1a2b3c4d5e’);还是上图片比较简单这个表现跟test是一个尿性的,即非全局调用(不加g)的时候不会更新lastIndex(lastIndex不生效),全局调用的时候会更新lastIndex说完了比较复杂的有分组的情况,我们来看看没有分组的情况,言简意赅,你作为这么优秀的一个人,应该能随便看懂吧。var reg = /\d\w/ reg.exec(‘1a2b3c4d5e’);对于这个方法呢,如果我们只需要查找第一个匹配结果的话可以不加g,如果需要返回所有匹配结果的话,需要循环执行reg.exec(),并且需要加上g。search()search()方法用于检索字符串中制定的子字符串或者检索与正则表达式相匹配的子字符串。方法返回第一个匹配结果的index,查找不到返回-1search()方法会忽略g标志,总是从字符串的开头进行检索当我们传入的参数s字符串时,它会转换成正则表达式var str = “abcd1234"str.search(‘1’) //4str.search(/1/) //4str.search(‘hello’) //-1str.search(/hello/) //-1so easy下一个match()非全局调用情况下(不加g):这个方法类似于exec(),返回值是一毛一样的,one hair one style。match()方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配如果没有匹配到文本则返回null,否则返回一个结果数组:数组第一个元素是与正则表达式相匹配的文本第二个元素是与正则表达式的第一个子表达式(也就是分组1)相匹配的文本(如果有分组1的话)第三个元素是与正则表达式的第二个子表达式(分组2)相匹配的文本(如果有分组2的话)除了数组元素和 length 属性之外,match() 方法还返回两个属性。index 属性声明的是匹配文本的第一个字符的位置。input 属性则存放的是被检索的字符串 string。var str = “1a2b3c4d”;var reg = /(\d)(\w)/;str.match(reg);当我们的正则表达式加上了g修饰符以后,这个方法又出现了一些不同,我为什么要说‘又’match()方法的返回改变了,变化害…害挺大的,跟前面的exec()和test()方法又有不同如果没有匹配到文本则返回null,否则返回一个结果数组:数组元素为与正则表达式匹配的文本var str = “1a2b3c4d”;var reg = /(\d)(\w)/g;str.match(reg);你有没有发现,即使我已经贴了图,却还是写了代码,为什么?因为作为一个这么sweet 和 warm的人,我有必要为你节省你自己输入代码的时间,你现在只需要ctrl C 然后ctrl V就可以在浏览器控制疯狂验证我的图片,疯狂测试这些方法!split()split()方法用于把一个字符串分割成字符串数组。//这个也可以输入字符串作为参数,类似于search(),它会转换成正则var str = “a,b,c,d"str.split(’,’); //[“a”,“b”,“c”,“d”]str.split(/,/); //[“a”,“b”,“c”,“d”]//一般情况都是使用字符串居多,比较复杂的情况就可以使用正则var str =“a1b2c3”;str.split(/\d/); //[“a”, “b”, “c”, “”]//注意:如果字符串最后的子字符串刚好符合参数的正则,那么就会多了一个空的元素,像上面一样replace()来了来了,上篇文章中使用最多的replace()终于来了!!!AV8D shout with me! RRRRRRRRrrrrrrrrrrrrrrEEEEEEEPPPPPLLLLAAAACCCCCEEEEE。该方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。1、 一般用法,这里是一般用法啊,replace(‘找谁’,‘替换成谁’)该用法的全局与非全局调用的差别是‘替换第一个匹配项’和‘替换所有匹配项’。举个常用的例子//这个‘找谁’同样也可以是字符串或者是正则,类似于split(),search()var str = “hello I am leelei”;str.replace(’leelei’,‘岭南吴彦祖’); //“hello I am 岭南吴彦祖"str.replace(/leelei/,‘岭南吴彦祖’); //“hello I am 岭南吴彦祖"好吧,这个对于你们来说并不常用,可能是只有我在用而已,抱歉。写个你们常用的例子吧var str = “2018-11-26”;str.replace(/-/,’/’); //“2018/11-26”//看到没有,这里只替换了一个-str.replace(/-/g,’/’); //“2018/11/26”//只有全局调用的时候,才会替换所有的匹配文本2、 进阶用法,replace(‘找谁’,回调函数),每次匹配替换的时候调用,有4个参数1、匹配的字符串2、正则表达式分组内容,没有分组就没有这个这个参数,几个分组就几个该参数3、匹配项在字符串中的index4、原字符串,replace()方法不会改变原字符串的哦。//当没有分组的时候var str =“a5b6c7d8”;//可以根据下面的截图对照上面的参数来理解记忆。//这里是给每一个匹配的数字+1.str.replace(/\d/g,function(match,index,origin){ console.log(match,index,origin); return match-0+1;});//当有分组的时候//为字符串中的某些字段更换样式,完整的demo就不写了,大家应该都能看懂吧。//将第一个分组匹配的内容替换掉//为什么要分组? 因为我们不想给’个’这个字添加样式,但是又需要用’个’来判断,我们只更改’个’前面的数字的样式,不更改其他数字。var str = “第1点,这里有4个橘子,5个橙子,9个苹果,我们需要为这几个数量更改样式.";str.replace(/(\d)个/g,function(match,$1,index,origin){ console.log(match,$1); return “<span style=‘color:red’>"+$1+"<span>个”;})//需要注意的是,这个回调函数的return值会覆盖match的值,因此要在return的时候加回’个’字。ok应该足够清楚了吧最后虽然标题是29.22分钟,但是看完两篇文章好像就不止了。我不管,30分钟内没看完的好好检讨下自己,是不是没有戴眼镜,是不是没睡好觉,看那么慢呢!如果有建议或者意见,请在评论区指出,非常感谢~ ...

November 26, 2018 · 1 min · jiezi

Data Lake Analytics + OSS数据文件格式处理大全

前言Data Lake Analytics是Serverless化的云上交互式查询分析服务。用户可以使用标准的SQL语句,对存储在OSS、TableStore上的数据无需移动,直接进行查询分析。目前该产品已经正式登陆阿里云,欢迎大家申请试用,体验更便捷的数据分析服务。请参考https://help.aliyun.com/document_detail/70386.html 进行产品开通服务申请。在上一篇教程中,我们介绍了如何分析CSV格式的TPC-H数据集。除了纯文本文件(例如,CSV,TSV等),用户存储在OSS上的其他格式的数据文件,也可以使用Data Lake Analytics进行查询分析,包括ORC, PARQUET, JSON, RCFILE, AVRO甚至ESRI规范的地理JSON数据,还可以用正则表达式匹配的文件等。本文详细介绍如何根据存储在OSS上的文件格式使用Data Lake Analytics (下文简称 DLA)进行分析。DLA内置了各种处理文件数据的SerDe(Serialize/Deserilize的简称,目的是用于序列化和反序列化)实现,用户无需自己编写程序,基本上能选用DLA中的一款或多款SerDe来匹配您OSS上的数据文件格式。如果还不能满足您特殊文件格式的处理需求,请联系我们,尽快为您实现。1. 存储格式与SerDe用户可以依据存储在OSS上的数据文件进行建表,通过STORED AS 指定数据文件的格式。例如,CREATE EXTERNAL TABLE nation ( N_NATIONKEY INT, N_NAME STRING, N_REGIONKEY INT, N_COMMENT STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY ‘|’ STORED AS TEXTFILE LOCATION ‘oss://test-bucket-julian-1/tpch_100m/nation’;建表成功后可以使用SHOW CREATE TABLE语句查看原始建表语句。mysql> show create table nation;+———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————–+| Result |+———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————–+| CREATE EXTERNAL TABLE nation( n_nationkey int, n_name string, n_regionkey int, n_comment string)ROW FORMAT DELIMITED FIELDS TERMINATED BY ‘|‘STORED AS TEXTFILELOCATION ‘oss://test-bucket-julian-1/tpch_100m/nation’|+———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————–+1 row in set (1.81 sec)下表中列出了目前DLA已经支持的文件格式,当针对下列格式的文件建表时,可以直接使用STORED AS,DLA会选择合适的SERDE/INPUTFORMAT/OUTPUTFORMAT。在指定了STORED AS 的同时,还可以根据具体文件的特点,指定SerDe (用于解析数据文件并映射到DLA表),特殊的列分隔符等。后面的部分会做进一步的讲解。2. 示例2.1 CSV文件CSV文件,本质上还是纯文本文件,可以使用STORED AS TEXTFILE。列与列之间以逗号分隔,可以通过ROW FORMAT DELIMITED FIELDS TERMINATED BY ‘,’ 表示。普通CSV文件例如,数据文件oss://bucket-for-testing/oss/text/cities/city.csv的内容为Beijing,China,010ShangHai,China,021Tianjin,China,022建表语句可以为CREATE EXTERNAL TABLE city ( city STRING, country STRING, code INT) ROW FORMAT DELIMITED FIELDS TERMINATED BY ‘,’ STORED AS TEXTFILE LOCATION ‘oss://bucket-for-testing/oss/text/cities’;使用OpenCSVSerde__处理引号__引用的字段OpenCSVSerde在使用时需要注意以下几点:用户可以为行的字段指定字段分隔符、字段内容引用符号和转义字符,例如:WITH SERDEPROPERTIES (“separatorChar” = “,”, “quoteChar” = “`”, “escapeChar” = "" );不支持字段内嵌入的行分割符;所有字段定义STRING类型;其他数据类型的处理,可以在SQL中使用函数进行转换。例如,CREATE EXTERNAL TABLE test_csv_opencsvserde ( id STRING, name STRING, location STRING, create_date STRING, create_timestamp STRING, longitude STRING, latitude STRING) ROW FORMAT SERDE ‘org.apache.hadoop.hive.serde2.OpenCSVSerde’with serdeproperties(“separatorChar”=",",“quoteChar”=""",“escapeChar”="\")STORED AS TEXTFILE LOCATION ‘oss://test-bucket-julian-1/test_csv_serde_1’;自定义分隔符需要自定义列分隔符(FIELDS TERMINATED BY),转义字符(ESCAPED BY),行结束符(LINES TERMINATED BY)。需要在建表语句中指定ROW FORMAT DELIMITED FIELDS TERMINATED BY ‘\t’ ESCAPED BY ‘\’ LINES TERMINATED BY ‘\n’忽略CSV文件中的HEADER在csv文件中,有时会带有HEADER信息,需要在数据读取时忽略掉这些内容。这时需要在建表语句中定义skip.header.line.count。例如,数据文件oss://my-bucket/datasets/tpch/nation_csv/nation_header.tbl的内容如下:N_NATIONKEY|N_NAME|N_REGIONKEY|N_COMMENT0|ALGERIA|0| haggle. carefully final deposits detect slyly agai|1|ARGENTINA|1|al foxes promise slyly according to the regular accounts. bold requests alon|2|BRAZIL|1|y alongside of the pending deposits. carefully special packages are about the ironic forges. slyly special |3|CANADA|1|eas hang ironic, silent packages. slyly regular packages are furiously over the tithes. fluffily bold|4|EGYPT|4|y above the carefully unusual theodolites. final dugouts are quickly across the furiously regular d|5|ETHIOPIA|0|ven packages wake quickly. regu|相应的建表语句为:CREATE EXTERNAL TABLE nation_header ( N_NATIONKEY INT, N_NAME STRING, N_REGIONKEY INT, N_COMMENT STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY ‘|’ STORED AS TEXTFILE LOCATION ‘oss://my-bucket/datasets/tpch/nation_csv/nation_header.tbl’TBLPROPERTIES (“skip.header.line.count”=“1”);skip.header.line.count的取值x和数据文件的实际行数n有如下关系:当x<=0时,DLA在读取文件时,不会过滤掉任何信息,即全部读取;当0当x>=n时,DLA在读取文件时,会过滤掉所有的文件内容。2.2 TSV文件与CSV文件类似,TSV格式的文件也是纯文本文件,列与列之间的分隔符为Tab。例如,数据文件oss://bucket-for-testing/oss/text/cities/city.tsv的内容为Beijing China 010ShangHai China 021Tianjin China 022建表语句可以为CREATE EXTERNAL TABLE city ( city STRING, country STRING, code INT) ROW FORMAT DELIMITED FIELDS TERMINATED BY ‘\t’ STORED AS TEXTFILE LOCATION ‘oss://bucket-for-testing/oss/text/cities’;2.3 多字符数据字段分割符文件假设您的数据字段的分隔符包含多个字符,可采用如下示例建表语句,其中每行的数据字段分割符为“||”,可以替换为您具体的分割符字符串。ROW FORMAT SERDE ‘org.apache.hadoop.hive.contrib.serde2.MultiDelimitSerDe’with serdeproperties(“field.delim”="||")示例:CREATE EXTERNAL TABLE test_csv_multidelimit ( id STRING, name STRING, location STRING, create_date STRING, create_timestamp STRING, longitude STRING, latitude STRING) ROW FORMAT SERDE ‘org.apache.hadoop.hive.contrib.serde2.MultiDelimitSerDe’with serdeproperties(“field.delim”="||")STORED AS TEXTFILE LOCATION ‘oss://bucket-for-testing/oss/text/cities/’;2.4 JSON文件DLA可以处理的JSON文件通常以纯文本的格式存储,在建表时除了要指定STORED AS TEXTFILE, 还要定义SERDE。在JSON文件中,每行必须是一个完整的JSON对象。例如,下面的文件格式是不被接受的{“id”: 123, “name”: “jack”, “c3”: “2001-02-03 12:34:56”}{“id”: 456, “name”: “rose”, “c3”: “1906-04-18 05:12:00”}{“id”: 789, “name”: “tom”, “c3”: “2001-02-03 12:34:56”}{“id”: 234, “name”: “alice”, “c3”: “1906-04-18 05:12:00”}需要改写成:{“id”: 123, “name”: “jack”, “c3”: “2001-02-03 12:34:56”}{“id”: 456, “name”: “rose”, “c3”: “1906-04-18 05:12:00”}{“id”: 789, “name”: “tom”, “c3”: “2001-02-03 12:34:56”}{“id”: 234, “name”: “alice”, “c3”: “1906-04-18 05:12:00”}不含嵌套的JSON数据建表语句可以写CREATE EXTERNAL TABLE t1 (id int, name string, c3 timestamp)STORED AS JSONLOCATION ‘oss://path/to/t1/directory’;含有嵌套的JSON文件使用struct和array结构定义嵌套的JSON数据。例如,用户原始数据(注意:无论是否嵌套,一条完整的JSON数据都只能放在一行上,才能被Data Lake Analytics处理):{ “DocId”: “Alibaba”, “User_1”: { “Id”: 1234, “Username”: “bob1234”, “Name”: “Bob”, “ShippingAddress”: { “Address1”: “969 Wenyi West St.”, “Address2”: null, “City”: “Hangzhou”, “Province”: “Zhejiang” }, “Orders”: [{ “ItemId”: 6789, “OrderDate”: “11/11/2017” }, { “ItemId”: 4352, “OrderDate”: “12/12/2017” } ] } }使用在线JSON格式化工具格式化后,数据内容如下:{ “DocId”: “Alibaba”, “User_1”: { “Id”: 1234, “Username”: “bob1234”, “Name”: “Bob”, “ShippingAddress”: { “Address1”: “969 Wenyi West St.”, “Address2”: null, “City”: “Hangzhou”, “Province”: “Zhejiang” }, “Orders”: [ { “ItemId”: 6789, “OrderDate”: “11/11/2017” }, { “ItemId”: 4352, “OrderDate”: “12/12/2017” } ] }}则建表语句可以写成如下(注意:LOCATION中指定的路径必须是JSON数据文件所在的目录,该目录下的所有JSON文件都能被识别为该表的数据):CREATE EXTERNAL TABLE json_table_1 ( docid string, user_1 struct< id:INT, username:string, name:string, shippingaddress:struct< address1:string, address2:string, city:string, province:string >, orders:array< struct< itemid:INT, orderdate:string > > >)STORED AS JSONLOCATION ‘oss://xxx/test/json/hcatalog_serde/table_1/’;对该表进行查询:select * from json_table_1;+———+—————————————————————————————————————-+| docid | user_1 |+———+—————————————————————————————————————-+| Alibaba | [1234, bob1234, Bob, [969 Wenyi West St., null, Hangzhou, Zhejiang], [[6789, 11/11/2017], [4352, 12/12/2017]]] |+———+—————————————————————————————————————-+对于struct定义的嵌套结构,可以通过“.”进行层次对象引用,对于array定义的数组结构,可以通过“[数组下标]”(注意:数组下标从1开始)进行对象引用。select DocId, User_1.Id, User_1.ShippingAddress.Address1, User_1.Orders[1].ItemIdfrom json_table_1where User_1.Username = ‘bob1234’ and User_1.Orders[2].OrderDate = ‘12/12/2017’;+———+——+——————–+——-+| DocId | id | address1 | _col3 |+———+——+——————–+——-+| Alibaba | 1234 | 969 Wenyi West St. | 6789 |+———+——+——————–+——-+使用JSON函数处理数据例如,把“value_string”的嵌套JSON值作为字符串存储:{“data_key”:“com.taobao.vipserver.domains.meta.biz.alibaba.com”,“ts”:1524550275112,“value_string”:"{"appName":"","apps":[],"checksum":"50fa0540b430904ee78dff07c7350e1c","clusterMap":{"DEFAULT":{"defCkport":80,"defIPPort":80,"healthCheckTask":null,"healthChecker":{"checkCode":200,"curlHost":"","curlPath":"/status.taobao","type":"HTTP"},"name":"DEFAULT","nodegroup":"","sitegroup":"","submask":"0.0.0.0/0","syncConfig":{"appName":"trade-ma","nodegroup":"tradema","pubLevel":"publish","role":"","site":""},"useIPPort4Check":true}},"disabledSites":[],"enableArmoryUnit":false,"enableClientBeat":false,"enableHealthCheck":true,"enabled":true,"envAndSites":"","invalidThreshold":0.6,"ipDeleteTimeout":1800000,"lastModifiedMillis":1524550275107,"localSiteCall":true,"localSiteThreshold":0.8,"name":"biz.alibaba.com","nodegroup":"","owners":["junlan.zx","张三","李四","cui.yuanc"],"protectThreshold":0,"requireSameEnv":false,"resetWeight":false,"symmetricCallType":null,"symmetricType":"warehouse","tagName":"ipGroup","tenantId":"","tenants":[],"token":"1cf0ec0c771321bb4177182757a67fb0","useSpecifiedURL":false}"}使用在线JSON格式化工具格式化后,数据内容如下:{ “data_key”: “com.taobao.vipserver.domains.meta.biz.alibaba.com”, “ts”: 1524550275112, “value_string”: “{"appName":"","apps":[],"checksum":"50fa0540b430904ee78dff07c7350e1c","clusterMap":{"DEFAULT":{"defCkport":80,"defIPPort":80,"healthCheckTask":null,"healthChecker":{"checkCode":200,"curlHost":"","curlPath":"/status.taobao","type":"HTTP"},"name":"DEFAULT","nodegroup":"","sitegroup":"","submask":"0.0.0.0/0","syncConfig":{"appName":"trade-ma","nodegroup":"tradema","pubLevel":"publish","role":"","site":""},"useIPPort4Check":true}},"disabledSites":[],"enableArmoryUnit":false,"enableClientBeat":false,"enableHealthCheck":true,"enabled":true,"envAndSites":"","invalidThreshold":0.6,"ipDeleteTimeout":1800000,"lastModifiedMillis":1524550275107,"localSiteCall":true,"localSiteThreshold":0.8,"name":"biz.alibaba.com","nodegroup":"","owners":["junlan.zx","张三","李四","cui.yuanc"],"protectThreshold":0,"requireSameEnv":false,"resetWeight":false,"symmetricCallType":null,"symmetricType":"warehouse","tagName":"ipGroup","tenantId":"","tenants":[],"token":"1cf0ec0c771321bb4177182757a67fb0","useSpecifiedURL":false}"}建表语句为CREATE external TABLE json_table_2 ( data_key string, ts bigint, value_string string)STORED AS JSONLOCATION ‘oss://xxx/test/json/hcatalog_serde/table_2/’;表建好后,可进行查询:select * from json_table_2;+—————————————————+—————+————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————+| data_key | ts | value_string |+—————————————————+—————+————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————+| com.taobao.vipserver.domains.meta.biz.alibaba.com | 1524550275112 | {“appName”:”",“apps”:[],“checksum”:“50fa0540b430904ee78dff07c7350e1c”,“clusterMap”:{“DEFAULT”:{“defCkport”:80,“defIPPort”:80,“healthCheckTask”:null,“healthChecker”:{“checkCode”:200,“curlHost”:"",“curlPath”:"/status.taobao",“type”:“HTTP”},“name”:“DEFAULT”,“nodegroup”:"",“sitegroup”:"",“submask”:“0.0.0.0/0”,“syncConfig”:{“appName”:“trade-ma”,“nodegroup”:“tradema”,“pubLevel”:“publish”,“role”:"",“site”:""},“useIPPort4Check”:true}},“disabledSites”:[],“enableArmoryUnit”:false,“enableClientBeat”:false,“enableHealthCheck”:true,“enabled”:true,“envAndSites”:"",“invalidThreshold”:0.6,“ipDeleteTimeout”:1800000,“lastModifiedMillis”:1524550275107,“localSiteCall”:true,“localSiteThreshold”:0.8,“name”:“biz.alibaba.com”,“nodegroup”:"",“owners”:[“junlan.zx”,“张三”,“李四”,“cui.yuanc”],“protectThreshold”:0,“requireSameEnv”:false,“resetWeight”:false,“symmetricCallType”:null,“symmetricType”:“warehouse”,“tagName”:“ipGroup”,“tenantId”:"",“tenants”:[],“token”:“1cf0ec0c771321bb4177182757a67fb0”,“useSpecifiedURL”:false} |+—————————————————+—————+————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————+下面SQL示例json_parse,json_extract_scalar,json_extract等常用JSON函数的使用方式:mysql> select json_extract_scalar(json_parse(value), ‘$.owners[1]’) from json_table_2;+——–+| _col0 |+——–+| 张三 |+——–+mysql> select json_extract_scalar(json_obj.json_col, ‘$.DEFAULT.submask’) from ( select json_extract(json_parse(value), ‘$.clusterMap’) as json_col from json_table_2) json_objwhere json_extract_scalar(json_obj.json_col, ‘$.DEFAULT.healthChecker.curlPath’) = ‘/status.taobao’;+———–+| _col0 |+———–+| 0.0.0.0/0 |+———–+mysql> with json_obj as (select json_extract(json_parse(value), ‘$.clusterMap’) as json_col from json_table_2)select json_extract_scalar(json_obj.json_col, ‘$.DEFAULT.submask’)from json_obj where json_extract_scalar(json_obj.json_col, ‘$.DEFAULT.healthChecker.curlPath’) = ‘/status.taobao’;+———–+| _col0 |+———–+| 0.0.0.0/0 |+———–+2.5 ORC文件Optimized Row Columnar(ORC)是Apache开源项目Hive支持的一种优化的列存储文件格式。与CSV文件相比,不仅可以节省存储空间,还可以得到更好的查询性能。对于ORC文件,只需要在建表时指定 STORED AS ORC。例如,CREATE EXTERNAL TABLE orders_orc_date ( O_ORDERKEY INT, O_CUSTKEY INT, O_ORDERSTATUS STRING, O_TOTALPRICE DOUBLE, O_ORDERDATE DATE, O_ORDERPRIORITY STRING, O_CLERK STRING, O_SHIPPRIORITY INT, O_COMMENT STRING) STORED AS ORC LOCATION ‘oss://bucket-for-testing/datasets/tpch/1x/orc_date/orders_orc’;2.6 PARQUET文件Parquet是Apache开源项目Hadoop支持的一种列存储的文件格式。使用DLA建表时,需要指定STORED AS PARQUET即可。例如,CREATE EXTERNAL TABLE orders_parquet_date ( O_ORDERKEY INT, O_CUSTKEY INT, O_ORDERSTATUS STRING, O_TOTALPRICE DOUBLE, O_ORDERDATE DATE, O_ORDERPRIORITY STRING, O_CLERK STRING, O_SHIPPRIORITY INT, O_COMMENT STRING) STORED AS PARQUET LOCATION ‘oss://bucket-for-testing/datasets/tpch/1x/parquet_date/orders_parquet’;2.7 RCFILE文件Record Columnar File (RCFile), 列存储文件,可以有效地将关系型表结构存储在分布式系统中,并且可以被高效地读取和处理。DLA在建表时,需要指定STORED AS RCFILE。例如,CREATE EXTERNAL TABLE lineitem_rcfile_date ( L_ORDERKEY INT, L_PARTKEY INT, L_SUPPKEY INT, L_LINENUMBER INT, L_QUANTITY DOUBLE, L_EXTENDEDPRICE DOUBLE, L_DISCOUNT DOUBLE, L_TAX DOUBLE, L_RETURNFLAG STRING, L_LINESTATUS STRING, L_SHIPDATE DATE, L_COMMITDATE DATE, L_RECEIPTDATE DATE, L_SHIPINSTRUCT STRING, L_SHIPMODE STRING, L_COMMENT STRING) STORED AS RCFILELOCATION ‘oss://bucke-for-testing/datasets/tpch/1x/rcfile_date/lineitem_rcfile'2.8 AVRO文件DLA针对AVRO文件建表时,需要指定STORED AS AVRO,并且定义的字段需要符合AVRO文件的schema。如果不确定可以通过使用Avro提供的工具,获得schema,并根据schema建表。在Apache Avro官网下载avro-tools-.jar到本地,执行下面的命令获得Avro文件的schema:java -jar avro-tools-1.8.2.jar getschema /path/to/your/doctors.avro{ “type” : “record”, “name” : “doctors”, “namespace” : “testing.hive.avro.serde”, “fields” : [ { “name” : “number”, “type” : “int”, “doc” : “Order of playing the role” }, { “name” : “first_name”, “type” : “string”, “doc” : “first name of actor playing role” }, { “name” : “last_name”, “type” : “string”, “doc” : “last name of actor playing role” } ]}建表语句如下,其中fields中的name对应表中的列名,type需要参考本文档中的表格转成hive支持的类型CREATE EXTERNAL TABLE doctors(number int,first_name string,last_name string)STORED AS AVROLOCATION ‘oss://mybucket-for-testing/directory/to/doctors’;大多数情况下,Avro的类型可以直接转换成Hive中对应的类型。如果该类型在Hive不支持,则会转换成接近的类型。具体请参照下表:2.9 可以用正则表达式匹配的文件通常此类型的文件是以纯文本格式存储在OSS上的,每一行代表表中的一条记录,并且每行可以用正则表达式匹配。例如,Apache WebServer日志文件就是这种类型的文件。某日志文件的内容为:127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] “GET /apache_pb.gif HTTP/1.0” 200 2326127.0.0.1 - - [26/May/2009:00:00:00 +0000] “GET /someurl/?track=Blabla(Main) HTTP/1.1” 200 5864 - “Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.65 Safari/525.19"每行文件可以用下面的正则表达式表示,列之间使用空格分隔:([^ ]) ([^ ]) ([^ ]) (-|\[[^\]]\]) ([^ "]|"[^"]") (-|[0-9]) (-|[0-9])(?: ([^ "]|"[^"]") ([^ "]|"[^"]"))?针对上面的文件格式,建表语句可以表示为:CREATE EXTERNAL TABLE serde_regex( host STRING, identity STRING, userName STRING, time STRING, request STRING, status STRING, size INT, referer STRING, agent STRING)ROW FORMAT SERDE ‘org.apache.hadoop.hive.serde2.RegexSerDe’WITH SERDEPROPERTIES ( “input.regex” = “([^ ]) ([^ ]) ([^ ]) (-|\[[^\]]\]) ([^ "]|"[^"]") (-|[0-9]) (-|[0-9])(?: ([^ "]|"[^"]") ([^ "]|"[^"]"))?")STORED AS TEXTFILELOCATION ‘oss://bucket-for-testing/datasets/serde/regex’;查询结果mysql> select * from serde_regex;+———–+———-+——-+——————————+———————————————+——–+——+———+————————————————————————————————————————–+| host | identity | userName | time | request | status | size | referer | agent |+———–+———-+——-+——————————+———————————————+——–+——+———+————————————————————————————————————————–+| 127.0.0.1 | - | frank | [10/Oct/2000:13:55:36 -0700] | “GET /apache_pb.gif HTTP/1.0” | 200 | 2326 | NULL | NULL || 127.0.0.1 | - | - | [26/May/2009:00:00:00 +0000] | “GET /someurl/?track=Blabla(Main) HTTP/1.1” | 200 | 5864 | - | “Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.65 Safari/525.19” |+———–+———-+——-+——————————+———————————————+——–+——+———+————————————————————————————————————————–+2.10 Esri ArcGIS的地理JSON数据文件DLA支持Esri ArcGIS的地理JSON数据文件的SerDe处理,关于这种地理JSON数据格式说明,可以参考:https://github.com/Esri/spatial-framework-for-hadoop/wiki/JSON-Formats示例:CREATE EXTERNAL TABLE IF NOT EXISTS california_counties( Name string, BoundaryShape binary)ROW FORMAT SERDE ‘com.esri.hadoop.hive.serde.JsonSerde’STORED AS INPUTFORMAT ‘com.esri.json.hadoop.EnclosedJsonInputFormat’OUTPUTFORMAT ‘org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat’LOCATION ‘oss://test_bucket/datasets/geospatial/california-counties/‘3. 总结通过以上例子可以看出,DLA可以支持大部分开源存储格式的文件。对于同一份数据,使用不同的存储格式,在OSS中存储文件的大小,DLA的查询分析速度上会有较大的差别。推荐使用ORC格式进行文件的存储和查询。为了获得更快的查询速度,DLA还在不断的优化中,后续也会支持更多的数据源,为用户带来更好的大数据分析体验。本文作者:金络阅读原文本文为云栖社区原创内容,未经允许不得转载。

November 23, 2018 · 5 min · jiezi

29.22分钟学会书写正则

写在最前面看到标题你可能会疑惑为什么不是30分钟?因为我这个文章图文并茂,非常恐怖,兄弟,其实你不用30分钟就可以看懂。你可能会以为我在吹牛B,但是当你看完的时候,一掐表,你会发现我真的是在吹牛B那又为什么是.22呢?作为一个理科生,保留两位小数是不变的信仰。而在下,仅仅是喜欢2这个数字,如是而已正则表达式正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式通常被用来检索、替换、校验那些符合某个模式(规则)的文本。RegExp对象在爪洼死苦瑞per特中,RegExp 对象表示正则表达式,它是对字符串执行模式匹配的强大工具。那么要如何使用呢?两种方式:字面量,构造函数var reg = /\bhello\b/g //字面量 // \b代表单词边界(WordBoundary) 也就是说这个正则匹配的是 hello world这种hello 而不是helloworld//因为helloworld连起来了,没有单词边界var reg = new RegExp(’\bhello\b’,‘g’)//注意两者的区别//后面这种方法需要转义反斜杠(javascript的原因),//而且这个g(修饰符)是单独提取出来的//而且正则两边没有/包围的,上面第一种是这样的=> /正则表达式/正则可视化工具Regulex可视化图形,对理解正则有非常大的帮助二话不说先进来这个网站,这个文章将使用这个网站来验证写的例子。元字符正则表达式由两种基本字符类组成原义字符元字符原义字符,就是表示原本意思的字符,像上面正则中的hello,就代表匹配hello这个字符串元字符呢,就是表示不是原本意思的字符,这样想就简单多了吧。像上面这个\b既然元字符表示的不是本事的字符,那我如果就要匹配它原本的字符呢?比如说我就要匹配+号,号,那么请使用 \ 来转义字符下面这些元字符先随便过一遍先,不用背熟也可往下看~$ 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 ’n’ 或 ‘r’。要匹配 $ 字符本身,请使用 $。() 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 ( 和 )。 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 。+ 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 +。. 匹配除换行符 n 之外的任何单字符。要匹配 . ,请使用 . 。[] 标记一个中括号表达式的开始。要匹配 [,请使用 [。{} 标记限定符表达式的开始。要匹配 {,请使用 {。| 指明两项之间的一个选择。要匹配 |,请使用 |。? 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 ?。\ 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, ’n’ 匹配字符 ’n’。’n’ 匹配换行符。序列 ‘' 匹配 “",而 ‘(’ 则匹配 “("。^ 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 ^。\cX 匹配由x指明的控制字符。例如, cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 ‘c’ 字符。\f 匹配一个换页符。等价于 x0c 和 cL。\n 匹配一个换行符。等价于 x0a 和 cJ。\r 匹配一个回车符。等价于 x0d 和 cM。\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ fnrtv]。注意 Unicode 正则表达式会匹配全角空格符。\S 匹配任何非空白字符。等价于 1。\t 匹配一个制表符。等价于 x09 和 cI。\v 匹配一个垂直制表符。等价于 x0b 和 cK边界从一开始的例子我们就知道了这个b,不对,是这个\b他表示的就是单词边界的意思.我们知道,fck这个是有很多用法的,可以单独用,也可以加个ing多种词性使用。然后我们只想找到单独的fck,看代码//作为光荣的社会主义接班人怎么可能用fck做例子呢?var reg = /\bis\b/g;var str = “this is me”;str.replace(reg,‘X’)//“this X me"var reg = /is/g;var str = “this is me”;str.replace(reg,‘X’)//“thX X me"两者区别清晰可见,不容我多说了吧,各位客官。再来看看一个问题,如果我只要开头部分的A字符而文本中间的A字符却不要,又该如何?只需如此,便可对敌var reg = /^A/g;var str = “ABA”;str.replace(reg,‘X’);//“XBA"需要以A为结尾的正则,则是如下var reg = /A$/g;var str = “ABA”;str.replace(reg,‘X’);//“ABX"注意,正如开头结尾的位置一样,^和$的位置也是如此,^放在正则表达式前面,$放在表达式后面字符类一般情况下,正则表达式一个字符对应字符串的一个字符比如表达式 \bhello 就表示 匹配 字符\b h e l l o,如果我们想要匹配一类字符的时候?比如我要匹配a或者b或者c,我们就可以使用元字符 []来构建一个简单的类[a,b,c]就把a,b,c归为一类,表示可以匹配a或者b或者c。如果你会一丢丢英文的话,你应该就可以看懂下面的图,one of a,b,c,也就是匹配abc中任意一个范围类当我们学习了上面的内容以后,如果我们要写匹配0到9的数字,就应该是这样写但是如果我要匹配更多呢?那不是键盘都要敲烂了?这正则也太不智能了吧???显然,你能想到的,创造正则的人也想到了我们可以这样子好了,方便了一些,然后你可能又会吃惊,那么我的短横线-呢?我如果要匹配0-9以及短横线呢?莫慌,只要在后面补回去即可这个图可以清楚看到有两条分支,也就是说我可以走0-9这条路也可以走短横线这条路预定义类学习了上面以后,我们就可以书写匹配数字的正则了,[0-9]那么有没有更简便更短的方法呢?巧了,正则就是辣么强大在上面的元字符部分内容中,你可能已经窥得其中精妙了上表格,不是,上图(这个segmentfault哪里插入表格啊??)我们可以根据英文单词的意思,来记住这些预定义类的用法。我们发现,大写字母和小写字母的区别就是取反!,如d和D同时我们从表格中的等价类可以发现如果我们要一个类的取反,那么就在类中加一个 ^none of abc量词如果要你写一个匹配10个数字的正则?你会怎么写诶你可能已经胸有成竹的写下了\d\d\d\d\d\d\d\d\d\d吃惊,你会发现,尽管是你单身二十余年的右手,依然感到了一丝乏力!疲惫,有时是在过度劳累之后为了挽救一些人的右臂,正则有了量词实现上面的需求我们只要 \d{10}Digit 10times为了方便一些英语不好的人,比如我,我甚至使用了鲜为人知的百度翻译(广告费私我)但是,如果我不知道要匹配具体多少个数字呢?反正就是匹配100个到1000个之间的数字当当当当让我们看看可视化工具的结果,方便理解注意,这个{n,m}是包括n次和m次的哦,是闭区间哦贪婪模式与非贪婪模式从上面一则我们知道,如果我们要匹配100到1000个数字的话,是这样写\d{100,1000}如果我给的字符串里有1000个数字,但是我只想匹配前面100个呢?如果按照上面这样写,则如下var reg = /\d{3,6}/;var str = “123456789”;str.replace(reg,‘替换成这个’);//“替换成这个789"我们可以看到,上面这个例子是匹配了6个数字,将6个数字替换了,尽管他的正则匹配的是3到6个数字。没错,它是贪婪的!它会尽可能地匹配更多!这就是正则的 贪婪匹配,这是默认的,如果我们不想要那么贪婪,如何变得容易满足一点?只需要在量词后面加上 ? 即可var reg = /\d{3,6}?/;var str = “123456789”;str.replace(reg,‘替换成这个’);//“替换成这个456789"可以清楚看到正则只匹配了前面3个数字这就是正则的非贪婪模式分支条件如果我只需要匹配100个或者1000个数字呢?就只有100和1000两种可能,而不是100到1000任意一个数字,又该如何对敌?这就要设计到正则的分支条件了\d{100}|\d{1000}需要注意的是这个 | 分割的是左右两边所有部分,而不是仅仅连着这个符号的左右两部分,看下图有时候我们只需要一部分是分支,后面走的是同一条主干,只需要把分支用()包含即可注意:这个匹配是从正则左边的分支条件开始的,如果左边满足了,那么右边就不会在对比!var reg = /\d{4}|\d{2}/var str = “12345"str.replace(reg,‘X’);// “X5"var reg = /\d{2}|\d{4}/var str = “12345"str.replace(reg,‘X’);//“X345"分组当我们要匹配一个出现三次的单词而不是数字的时候,会怎么写呢?你可能会这样写hello{3}然后你打开可视化工具妈耶,居然只重复了我的o字母!死渣则,好过分其实,我们只要使用()就可以达到分组的目的,使量词作用于分组,上面分支条件中的括号亦是如此前瞻/后顾sometimes,我们要找寻的字符可能还要依靠前后字符来确定比如说我要替换连续的2个数字,而且它的前面要连着是2个英文字母,这样的数字我才要你可能会疑惑,这样写不就完事了吗?\d{2}\w{2}上面匹配的是2个数字和2个字母,虽然是连着的,但是匹配了是4个字符,如果我要替换匹配文本的话,那就替换了4个字符,而我们只想替换2个数字!这个时候就需要用到断言了首先我们需要明白几个点正则表达式从文本头部到尾部开始解析,文本尾部方向叫做‘前’,也就是往前走,就是往尾巴走前瞻就是正则表达式匹配到规则(此例中的‘2个数字’)的时候,向前看看,看看是否符合断言(此例中的‘前面连着2个字母’),后瞻/后顾的规则则相反。(javascript不支持后顾)上表格!根据表格内容,我们就可以解决这个问题了,注意\w包括数字哦题目要求是连着2个字母var reg = /\d{2}(?=[a-zA-Z]{2})/;var str = “1a23bc456def”;str.replace(reg,‘X’);//“1aXbc456def"只替换了数字,没有替换后面的断言哦!顺便把这个负向前瞻看看吧看到这个not followed by 我想你应该知晓用法了嘿嘿嘿正则就介绍到这里啦下篇文章将介绍javascript中的正则对象的属性,以及一些方法。如果有意见或者建议,请在评论区中指出,谢谢 fnrtv ↩ ...

November 9, 2018 · 1 min · jiezi

正则表达式手册

表达式全集原文链接:正则表达式手册字符描述\将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符。例如,“n”匹配字符“n”。“\n”匹配一个换行符。串行“\”匹配“\”而“(”则匹配“(”。^匹配输入字符串的开始位置。如果设置了RegExp对象的Multiline属性,^也匹配“\n”或“\r”之后的位置。$匹配输入字符串的结束位置。如果设置了RegExp对象的Multiline属性,$也匹配“\n”或“\r”之前的位置。匹配前面的子表达式零次或多次。例如,zo能匹配“z”以及“zoo”。等价于{0,}。+匹配前面的子表达式一次或多次。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}。?匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“does”或“does”中的“do”。?等价于{0,1}。{n}n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o。{n,}n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o”。{n,m}m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o。“o{0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。?当该字符紧跟在任何一个其他限制符(,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串“oooo”,“o+?”将匹配单个“o”,而“o+”将匹配所有“o”。.匹配除“\n”之外的任何单个字符。要匹配包括“\n”在内的任何字符,请使用像“(.\n)”的模式。(pattern)匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用“(”或“|</code>”。(?:pattern)匹配pattern但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用或字符“(|)”来组合一个模式的各个部分是很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。(?=pattern)正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。(?!pattern)正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如“Windows(?!95|98|NT|2000)”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。(?<=pattern)反向肯定预查,与正向肯定预查类拟,只是方向相反。例如,“(?<=95|98|NT|2000)Windows”能匹配“2000Windows”中的“Windows”,但不能匹配“3.1Windows”中的“Windows”。(?<!pattern)反向否定预查,与正向否定预查类拟,只是方向相反。例如“(?<!95|98|NT|2000)Windows”能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”。x|y匹配x或y。例如,“z|food”能匹配“z”或“food”。“(z|f)ood”则匹配“zood”或“food”。[xyz]字符集合。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”。[^xyz]负值字符集合。匹配未包含的任意字符。例如,“[^abc]”可以匹配“plain”中的“p”。[a-z]字符范围。匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。[^a-z]负值字符范围。匹配任何不在指定范围内的任意字符。例如,“[^a-z]”可以匹配任何不在“a”到“z”范围内的任意字符。\b匹配一个单词边界,也就是指单词和空格间的位置。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。\B匹配非单词边界。“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。\cx匹配由x指明的控制字符。例如,\cM匹配一个Control-M或回车符。x的值必须为A-Z或a-z之一。否则,将c视为一个原义的“c”字符。\d匹配一个数字字符。等价于[0-9]。\D匹配一个非数字字符。等价于[^0-9]。\f匹配一个换页符。等价于\x0c和\cL。\n匹配一个换行符。等价于\x0a和\cJ。\r匹配一个回车符。等价于\x0d和\cM。\s匹配任何空白字符,包括空格、制表符、换页符等等。等价于[\f\n\r\t\v]。\S匹配任何非空白字符。等价于[^\f\n\r\t\v]。\t匹配一个制表符。等价于\x09和\cI。\v匹配一个垂直制表符。等价于\x0b和\cK。\w匹配包括下划线的任何单词字符。等价于“[A-Za-z0-9_]”。\W匹配任何非单词字符。等价于“[^A-Za-z0-9_]”。\xn匹配n,其中n为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,“\x41”匹配“A”。“\x041”则等价于“\x04&1”。正则表达式中可以使用ASCII编码。\num匹配num,其中num是一个正整数。对所获取的匹配的引用。例如,“(.)\1”匹配两个连续的相同字符。\n标识一个八进制转义值或一个向后引用。如果\n之前至少n个获取的子表达式,则n为向后引用。否则,如果n为八进制数字(0-7),则n为一个八进制转义值。\nm标识一个八进制转义值或一个向后引用。如果\nm之前至少有nm个获得子表达式,则nm为向后引用。如果\nm之前至少有n个获取,则n为一个后跟文字m的向后引用。如果前面的条件都不满足,若n和m均为八进制数字(0-7),则\nm将匹配八进制转义值nm。\nml如果n为八进制数字(0-3),且m和l均为八进制数字(0-7),则匹配八进制转义值nml。\un匹配n,其中n是一个用四个十六进制数字表示的Unicode字符。例如,\u00A9匹配版权符号(©)。常用正则表达式匹配内容正则表达式用户名/^[a-z0-9_-]{3,16}$/密码/^[a-z0-9_-]{6,18}$/十六进制值/^#?([a-f0-9]{6}|[a-f0-9]{3})$/电子邮箱/^([a-z0-9_.-]+)@([\da-z.-]+).([a-z.]{2,6})$//^[a-z\d]+(.[a-z\d]+)@(\da-z?)+(.{1,2}[a-z]+)+$/URL/^(https?://)?([\da-z.-]+).([a-z.]{2,6})([/\w .-])/?$/IP 地址/((2[0-4]d|25[0-5]|[01]?dd?).){3}(2[0-4]d|25[0-5]|[01]?dd?)//^(?:(?:25[0-5]|20-4|[01]?0-9?).){3}(?:25[0-5]|20-4|[01]?0-9?)$/HTML标签/^<([a-z]+)([^<]+)(?:>(.)</1>|s+/>)$/删除代码\注释(?<!http:|S)//.*$Unicode编码中的汉字范围/^[\u2E80-\u9FFF]+$/

November 1, 2018 · 1 min · jiezi

JavaScript(E5,6) 正则学习总结学习,可看可不看!

1.概述正则表达式(regular expression)是一种表达文本模式(即字符串结构)的方法。创建方式有两种方式:一种是使用字面量,以斜杠表示开始和结束。var regex = /xyz/另一种是使用RegExp构造函数。var regex = new RegExp(‘xyz’); 它们的主要区别是,第一种方法在引擎编译代码时,就会新建正则表达式,第二种方法在运行时新建正则表达式,所以前者的效率较高。而且,前者比较便利和直观,所以实际应用中,基本上都采用字面量定义正则表达式。2.实例属性i:忽略大小写m:多行模式g:全局搜索3.实例方法3.1 RegExp.prototype.test()正则实例对象的test方法返回一个布尔值,表示当前模式是否能匹配参数字符串。/小智/.test(‘小智 终身学习执行者’) // truereg.exec(str) 返回匹配结果数组,不匹配则返回null,每执行一次exec就向后匹配一次3.2 RegExp.prototype.exec()3.2.1 reg.exec(str) 返回匹配结果数组,不匹配则返回null,每执行一次exec就向后匹配一次var s = ‘_x_x’;var r1 = /x/;var r2 = /y/;r1.exec(s) // [“x”]r2.exec(s) // null3.2.1.2如果表达式里有括号(),称为组匹配,返回结果中,第一个是整体匹配结果,后面依次是每个括号匹配的结果var s = ‘x_x’;var r = /(x)/;r.exec(s) // ["x", “x”]exec方法的返回数组还包含以下两个属性:input:整个原字符串。index:整个模式匹配成功的开始位置(从0开始计数)。var r = /a(b+)a/;var arr = r.exec(’abbba_aba’);arr // [“abbba”, “bbb”]arr.index // 1arr.input // “abbba_aba“3.2.3 如果表达式中有g选项进行全局搜索,则可以多次使用 exec,下次的匹配从上次的结果后开始 var reg = /a/g;var str = ‘abc_abc_abc’var r1 = reg.exec(str);r1 // [“a”]r1.index // 0reg.lastIndex // 1var r2 = reg.exec(str);r2 // [“a”]r2.index // 4reg.lastIndex // 5var r3 = reg.exec(str);r3 // [“a”]r3.index // 8reg.lastIndex // 9var r4 = reg.exec(str);r4 // nullreg.lastIndex // 04.字符串的实例方法4.1 str.match(reg),与 reg.exec相似,但是,如果使用g选项,则str.match一次性返回所有结果。var s = ‘abba’;var r = /a/g;s.match(r) // [“a”, “a”]r.exec(s) // [“a”]4.2 str.search(reg) ,返回匹配成功的第一个位置,如果没有任何匹配,则返回-1。’x_x’.search(/x/)// 14.3 str.replace(reg,newstr) ;用第一个参数reg去匹配,用第二个参数newstr 去替换,正则表达式如果不加g修饰符,就替换第一个匹配成功的值,否则替换所有匹配成功的值。‘aaa’.replace(‘a’, ‘b’) // “baa”‘aaa’.replace(/a/, ‘b’) // “baa”‘aaa’.replace(/a/g, ‘b’) // “bbb” 4.4 str.split(reg[,maxLength]) 用匹配的模式切割,第二个参数是限制返回结果的最大数量5. 匹配规则5.1 字面量字符和元字符大部分字符在正则表达式中,就是字面的含义,比如/a/匹配a,/b/匹配b。如果在正则表达式之中,某个字符只表示它字面的含义(就像前面的a和b),那么它们就叫做“字面量字符”(literal characters)。除了字面量字符以外,还有一部分字符有特殊含义,不代表字面的意思。它们叫做“元字符”(metacharacters),主要有以下几个。(1) 点字符(.)点字符(.)匹配除回车(r)、换行(n) 、行分隔符(u2028)和段分隔符(u2029)以外的所有字符。/c.t/上面代码中,c.t匹配c和t之间包含任意一个字符的情况,只要这三个字符在同一行,比如cat、c2t、c-t等等,但是不匹配coot。(2)位置字符^ 表示字符串的开始位置$ 表示字符串的结束位置// test必须出现在开始位置/^test/.test(’test123’) // true// test必须出现在结束位置/test$/.test(’new test’) // true// 从开始位置到结束位置只有test/^test$/.test(’test’) // true/^test$/.test(’test test’) // false(3)选择符(|)竖线符号(|)在正则表达式中表示“或关系”(OR),即cat|dog表示匹配cat或dog。/11|22/.test(‘911’) // true上面代码中,正则表达式指定必须匹配11或22。5.2 转义符正则表达式中那些有特殊含义的元字符,如果要匹配它们本身,就需要在它们前面要加上反斜杠。比如要匹配+,就要写成+。/1+1/.test(‘1+1’)// false/1+1/.test(‘1+1’)// true正则表达式中,需要反斜杠转义的,一共有12个字符:^、.、[、$、(、)、|、*、+、?、{和。需要特别注意的是,如果使用RegExp方法生成正则对象,转义需要使用两个斜杠,因为字符串内部会先转义一次。(new RegExp(‘1+1’)).test(‘1+1’)// false(new RegExp(‘1\+1’)).test(‘1+1’)// true5.3 字符类字符类(class)表示有一系列字符可供选择,只要匹配其中一个就可以了。所有可供选择的字符都放在方括号内,比如[xyz] 表示x、y、z之中任选一个匹配。/[abc]/.test(‘hello world’) // false/[abc]/.test(‘apple’) // true有两个字符在字符类中有特殊含义。(1)脱字符(^)如果方括号内的第一个字符是[^xyz]表示除了x、y、z之外都可以匹配:/[^abc]/.test(‘hello world’) // true/[^abc]/.test(‘bbc’) // false如果方括号内没有其他字符,即只有[^],就表示匹配一切字符,其中包括换行符。相比之下,点号作为元字符(.)是不包括换行符的。var s = ‘Please yes\nmake my day!’;s.match(/yes.*day/) // nulls.match(/yes[^]*day/) // [ ‘yes\nmake my day’]上面代码中,字符串s含有一个换行符,点号不包括换行符,所以第一个正则表达式匹配失败;第二个正则表达式[^]包含一切字符,所以匹配成功。 (2)连字符(-) 某些情况下,对于连续序列的字符,连字符(-)用来提供简写形式,表示字符的连续范围。比如,[abc]可以写成[a-c],[0123456789]可以写成[0-9],同理[A-Z]表示26个大写字母。/a-z/.test(‘b’) // false/[a-z]/.test(‘b’) // true 以下都是合法的字符类简写形式。[0-9.,][0-9a-fA-F][a-zA-Z0-9-][1-31]上面代码中最后一个字符类[1-31],不代表1到31,只代表1到3。另外,不要过分使用连字符,设定一个很大的范围,否则很可能选中意料之外的字符。最典型的例子就是[A-z],表面上它是选中从大写的A到小写的z之间52个字母,但是由于在 ASCII 编码之中,大写字母与小写字母之间还有其他字符,结果就会出现意料之外的结果。/[A-z]/.test(’\’) // true上面代码中,由于反斜杠(’’)的ASCII码在大写字母与小写字母之间,结果会被选中。5.4 预定义模式预定义模式指的是某些常见模式的简写方式。d 匹配0-9之间的任一数字,相当于[0-9]。D 匹配所有0-9以外的字符,相当于[^0-9]。w 匹配任意的字母、数字和下划线,相当于[A-Za-z0-9]。W 除所有字母、数字和下划线以外的字符,相当于[^A-Za-z0-9]。s 匹配空格(包括换行符、制表符、空格符等),相等于[ \t\r\n\v\f]。S 匹配非空格的字符,相当于[^ \t\r\n\v\f]。b 匹配词的边界。B 匹配非词边界,即在词的内部。// \s 的例子/\s\w*/.exec(‘hello world’) // [” world”]// \b 的例子/\bworld/.test(‘hello world’) // true/\bworld/.test(‘hello-world’) // true/\bworld/.test(‘helloworld’) // false// \B 的例子/\Bworld/.test(‘hello-world’) // false/\Bworld/.test(‘helloworld’) // true通常,正则表达式遇到换行符(n)就会停止匹配。var html = “<b>Hello</b>\n<i>world!</i>”;/./.exec(html)[0]// “<b>Hello</b>“上面代码中,字符串html包含一个换行符,结果点字符(.)不匹配换行符,导致匹配结果可能不符合原意。这时使用s字符类,就能包括换行符。var html = “<b>Hello</b>\n<i>world!</i>”;/[\S\s]/.exec(html)[0]// “<b>Hello</b>\n<i>world!</i>“上面代码中,[Ss]指代一切字符。5.5 重复类模式的精确匹配次数,使用大括号({})表示。{n}表示恰好重复n次,{n,}表示至少重复n次,{n,m}表示重复不少于n次,不多于m次。/lo{2}k/.test(’look’) // true/lo{2,5}k/.test(’looook’) // true上面代码中,第一个模式指定o连续出现2次,第二个模式指定o连续出现2次到5次之间。5.6 量词符*. ? 问号表示某个模式出现0次或1次,等同于{0, 1}。. * 星号表示某个模式出现0次或多次,等同于{0,}。. + 加号表示某个模式出现1次或多次,等同于{1,}。5.7 贪婪模式上一小节的三个量词符,默认情况下都是最大可能匹配,即匹配直到下一个字符不满足匹配规则为止。这被称为贪婪模式。var s = ‘aaa’;s.match(/a+/) // [“aaa”]上面代码中,模式是/a+/,表示匹配1个a或多个a,那么到底会匹配几个a呢?因为默认是贪婪模式,会一直匹配到字符a不出现为止,所以匹配结果是3个a。如果想将贪婪模式改为非贪婪模式,可以在量词符后面加一个问号。var s = ‘aaa’;s.match(/a+?/) // [“a”]除了非贪婪模式的加号,还有非贪婪模式的星号()和非贪婪模式的问号(?)+?:表示某个模式出现1次或多次,匹配时采用非贪婪模式。?:表示某个模式出现0次或多次,匹配时采用非贪婪模式。??:表格某个模式出现0次或1次,匹配时采用非贪婪模式。5.8 组匹配(1)概述正则表达式的括号表示分组匹配,括号中的模式可以用来匹配分组的内容。/fred+/.test(‘fredd’) // true/(fred)+/.test(‘fredfred’) // true上面代码中,第一个模式没有括号,结果+只表示重复字母d,第二个模式有括号,结果+就表示匹配fred这个词。 下面是另外一个分组捕获的例子。var m = ‘abcabc’.match(/(.)b(.)/);m// [‘abc’, ‘a’, ‘c’] 上面代码中,正则表达式/(.)b(.)/一共使用两个括号,第一个括号捕获a,第二个括号捕获c。注意,使用组匹配时,不宜同时使用g修饰符,否则match方法不会捕获分组的内容。var m = ‘abcabc’.match(/(.)b(.)/g);m // [‘abc’, ‘abc’]正则表达式内部,还可以用n引用括号匹配的内容,n是从1开始的自然数,表示对应顺序的括号。/(.)b(.)\1b\2/.test(“abcabc”)// true上面的代码中,1表示第一个括号匹配的内容(即a),2表示第二个括号匹配的内容(即c)。(2)非捕获组(?:x)称为非捕获组(Non-capturing group),表示不返回该组匹配的内容,即匹配的结果中不计入这个括号。非捕获组的作用请考虑这样一个场景,假定需要匹配foo或者foofoo,正则表达式就应该写成/(foo){1, 2}/,但是这样会占用一个组匹配。这时,就可以使用非捕获组,将正则表达式改为/(?:foo){1, 2}/,它的作用与前一个正则是一样的,但是不会单独输出括号内部的内容。var m = ‘abc’.match(/(?:.)b(.)/);m // [“abc”, “c”]上面代码中的模式,一共使用了两个括号。其中第一个括号是非捕获组,所以最后返回的结果中没有第一个括号,只有第二个括号匹配的内容。(3)先行断言x(?=y)称为先行断言(Positive look-ahead),x只有在y前面才匹配,y不会被计入返回结果。比如,要匹配后面跟着百分号的数字,可以写成/d+(?=%)/。“先行断言”中,括号里的部分是不会返回的。var m = ‘abc’.match(/b(?=c)/);m // [“b”]上面的代码使用了先行断言,b在c前面所以被匹配,但是括号对应的c不会被返回。(4)先行否定断言x(?!y)称为先行否定断言(Negative look-ahead),x只有不在y前面才匹配,y不会被计入返回结果。比如,要匹配后面跟的不是百分号的数字,就要写成/d+(?!%)/。/\d+(?!.)/.exec(‘3.14’)// [“14”]上面代码中,正则表达式指定,只有不在小数点前面的数字才会被匹配,因此返回的结果就是14。6. 实战6.1 消除字符串首尾两端的空格 var str = ’ #id div.class ‘; str.replace(/^\s+|\s+$/g, ‘’) // “#id div.class"6.2 验证手机号码var reg = /1[24578]\d{9}/;reg.test(‘154554568997’); //truereg.test(‘234554568997’); //false6.3 把手机号码替换成 var reg = /1[24578]\d{9}/;var str = ‘姓名:张三 手机:18210999999 性别:男’;str.replace(reg, ‘’) //“姓名:张三 手机:* 性别:男"6.4 匹配网页标签var strHtlm = ‘小智小智<div>222222@.qq.com</div>小智小智’;var reg = /<(.+)>.+</\1>/;strHtlm.match(reg); // ["<div>222222@.qq.com</div>"]6.5 替换敏感字let str = ‘中国共产党中国人民解放军中华人民共和国’;let r = str.replace(/中国|军/g, input => { let t = ‘’; for (let i = 0; i<input.length; i++) { t += ‘’; } return t;}) console.log(r); //共产党人民解放中华人民共和国 6.6 千位分隔符let str = ‘100002003232322’;let r = str.replace(/(\d)(?=(?:\d{3})+$)/g, ‘$1,’);console.log(r); //100,002,003,232,322参考链接https://developer.mozilla.org…https://wangdoc.com/javascrip… 一个笨笨的码农,我的世界只能终身学习! ...

October 5, 2018 · 2 min · jiezi

ECMAScript正则表达式6个最新特性

译者按: 还没学好ES6?ECMAScript 2018已经到来啦!原文:ECMAScript regular expressions are getting better!作者: Mathias Bynens: Google V8引擎开发者译者:Fundebug为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,翻译仅用于学习。1999年,ECMAScript 3添加了对正则表达式的支持。16年之后,ECMAScript 6(即ECMAScript 2015或者ES6)引入了Unicode模式(u选项), sticky模式(y选项)以及RegExp.prototype.flags的getter方法。这篇博客将介绍ECMAScript正则表达式的最新特性:dotAll模式(s选项)Lookbehind断言Named capture groupsUnicode property escapesString.prototype.matchAll规范RegExp遗留特性1. dotAll模式(s选项)这个特性已经在ECMAScript 2018正式发布了。默认情况下,.可以匹配任意字符,除了换行符:/foo.bar/u.test(‘foonbar’); // false另外,.不能匹配Unicode字符,需要使用u选项启用Unicode模式才行。ES2018引入了dotAll模式,通过s选项可以启用,这样,.就可以匹配换行符了。/foo.bar/su.test(‘foonbar’); // true2. Lookbehind断言这个特性已经在ECMAScript 2018正式发布了。ECMAScript目前仅支持lookahead断言。下面示例是Positive lookahead,匹配字符串“42 dollars”中紧跟着是"dollars"的数字:const pattern = /d+(?= dollars)/u;const result = pattern.exec(‘42 dollars’);console.log(result[0]); // 打印42下面示例是Negative lookahead,匹配字符串“42 pesos”中紧跟着的不是"dollars"的数字:const pattern = /d+(?! dollars)/u;const result = pattern.exec(‘42 pesos’);console.log(result[0]); // 打印42ES2018添加了lookbehind断言。下面示例是Positive lookbehind,匹配字符串“$42”中前面是"$“的数字:const pattern = /(?<=$)d+/u;const result = pattern.exec(’$42’);console.log(result[0]); // 打印42下面示例是Negative lookbehind,匹配字符串“$42”中前面不是是”$“的数字:const pattern = /(?<!$)d+/u;const result = pattern.exec(‘42’);console.log(result[0]); // 打印42Fundebug专注于网页、微信小程序、微信小游戏,支付宝小程序,React Native,Node.js和Java线上BUG实时监控,欢迎免费试用3. Named capture groups这个特性已经在ECMAScript 2018正式发布了。目前,正则表达式中小括号匹配的分组是通过数字编号的:const pattern = /(d{4})-(d{2})-(d{2})/u;const result = pattern.exec(‘2017-01-25’);console.log(result[0]); // 打印"2017-01-25"console.log(result[1]); // 打印"2017"console.log(result[2]); // 打印"01"console.log(result[3]); // 打印"25"这样很方便,但是可读性很差,且不易维护。一旦正则表达式中小括号的顺序有变化时,我们就需要更新对应的数字编号。ES2018添加named capture groups, 可以指定小括号中匹配内容的名称,这样可以提高代码的可读性,也便于维护。const pattern = /(?<year>d{4})-(?<month>d{2})-(?<day>d{2})/u;const result = pattern.exec(‘2017-01-25’);console.log(result.groups.year); // 打印"2017"console.log(result.groups.month); // 打印"01"console.log(result.groups.day); // 打印"25"4. Unicode property escapes这个特性已经在ECMAScript 2018正式发布了。Unicode标准为每一个字符分配了多个属性。比如,当你要匹配希腊语字符时,则可以搜索Script_Extensions属性为Greek的字符。Unicode property escapes使得我们可以使用ECMAScript正则表达式直接匹配Unicode字符的属性:const regexGreekSymbol = /p{Script_Extensions=Greek}/u;console.log(regexGreekSymbol.test(’’)); // 打印true5. String.prototype.matchAll这个特性还处在Stage 3 Draftg和y选项通常用于匹配一个字符串,然后遍历所有匹配的子串,包括小括号匹配的分组。String.prototype.matchAll让这个操作变得更加简单了。const string = ‘Magic hex numbers: DEADBEEF CAFE 8BADF00D’;const regex = /b[0-9a-fA-F]+b/g;for (const match of string.matchAll(regex)) { console.log(match);}每一个迭代所返回的match对象与regex.exec(string)所返回的结果相同:// Iteration 1:[ ‘DEADBEEF’, index: 19, input: ‘Magic hex numbers: DEADBEEF CAFE 8BADF00D’]// Iteration 2:[ ‘CAFE’, index: 28, input: ‘Magic hex numbers: DEADBEEF CAFE 8BADF00D’]// Iteration 3:[ ‘8BADF00D’, index: 33, input: ‘Magic hex numbers: DEADBEEF CAFE 8BADF00D’]注意,这个特性还处在Stage 3 Draft,因此还存在变化的可能性,示例代码是根据最新的提案写的。另外,浏览器也还没有支持这个特性。String.prototype.matchAll最快可以被加入到ECMAScript 2019中。6. 规范RegExp遗留特性这个提案还处在Stage 3 Draft这个提案规范了RegExp的遗留特性,比如RegExp.prototype.compile方法以及它的静态属性从RegExp.&dollar;1到RegExp.&dollar;9。虽然这些特性已经弃用(deprecated)了,但是为了兼容性我们不能将他们去。因此,规范这些RegExp遗留特性是最好的方法。因此,这个提案有助于保证兼容性。参考阮一峰 - ECMAScript 6 入门Fundebug博客 - JavaScript正则表达式进阶指南ECMAScript 2018: the final feature set关于FundebugFundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了6亿+错误事件,得到了Google、360、金山软件等众多知名用户的认可。欢迎免费试用!版权声明:转载时请注明作者Fundebug以及本文地址:https://blog.fundebug.com/201… ...

August 30, 2018 · 1 min · jiezi