Notes

1、正则表达式帮助我们在字符串中寻找特定模式。

js创建正则表达式的两种等价写法:

let re1 = new RegExp("abc");
let re2 = /abc/;

2、应用正则表达式

console.log(/abc/.test("abcde"));
// → true
console.log(/abc/.test("abxde"));
// → false

3、字符集合

\d Any digit character
\w An alphanumeric character (“word character”)
\s Any whitespace character (space, tab, newline, and similar)
\D A character that is not a digit
\W A nonalphanumeric character
\S A nonwhitespace character
. Any character except for newline
/abc/ A sequence of characters
/[abc]/ Any character from a set of characters
/[^abc]/ Any character not in a set of characters
/[0-9]/ Any character in a range of characters
/x+/ One or more occurrences of the pattern x
/x+?/ One or more occurrences, nongreedy
/x*/ Zero or more occurrences
/x?/ Zero or one occurrence
/x{2,4}/ Two to four occurrences
/(abc)/ A group
/a|b|c/ Any one of several patterns
/\d/ Any digit character
/\w/ An alphanumeric character (“word character”)
/\s/ Any whitespace character
/./ Any character except newlines
/\b/ A word boundary
/^/ Start of input
/$/ End of input

\d等转移字符可以放在 [ ] 里而不丧失含义,但是 . 和+ 之类的特殊符号不行,会变为普通的符号。

整体取反,非0非1:

let notBinary = /[^01]/;
console.log(notBinary.test("1100100010100110"));
// → false
console.log(notBinary.test("1100100010200110"));
// → true

4、重复匹配

+ one or more,* zero or more

console.log(/'\d+'/.test("'123'"));
// → true
console.log(/'\d+'/.test("''"));
// → false
console.log(/'\d*'/.test("'123'"));
// → true
console.log(/'\d*'/.test("''"));
// → true

? zero or one

let neighbor = /neighbou?r/;
console.log(neighbor.test("neighbour"));
// → true
console.log(neighbor.test("neighbor"));
// → true

{2} a pattern should occur a precise number of times,It is also possible to specify a range this way: {2,4} means the element must occur at least twice and at most four times.

let dateTime = /\d{1,2}-\d{1,2}-\d{4} \d{1,2}:\d{2}/;
console.log(dateTime.test("1-30-2003 8:45"));
// → true

You can also specify open-ended ranges when using braces by omitting the number after the comma. So, {5,} means five or more times.

5、分组(子表达式)

括号内的n个元素被视作一个整体元素(分组,子表达式):

let cartoonCrying = /boo+(hoo+)+/i;
console.log(cartoonCrying.test("Boohoooohoohooo"));
// → true

i表示该表达式大小写不敏感。

6、进行正则匹配的另外一种方式

可以让我们获取额外的信息:

let match = /\d+/.exec("one two 100");
console.log(match);
// → ["100"]
console.log(match.index);
// → 8

exec的返回值:匹配失败为null,成功则如上所示。

等价写法:

console.log("one two 100".match(/\d+/));
// → ["100"]

含括号表达式的情况:

let quotedText = /'([^']*)'/;
console.log(quotedText.exec("she said 'hello'"));
// → ["'hello'", "hello"] console.log(/bad(ly)?/.exec("bad"));
// → ["bad", undefined]
console.log(/(\d)+/.exec("123"));
// → ["123", "3"]

返回数组的第一个元素为整个正则表达式匹配的字符串,而第二元素为() 内正则(子表达式)匹配的字符串(没有就是undefined,多个就取最后一个)。容易知道,第二个元素几乎总是第一个元素的子集。

7、The Date class

console.log(new Date());
// → Sat Sep 01 2018 13:54:43 GMT+0800 (中国标准时间) console.log(new Date(2009, 11, 9));
// → Wed Dec 09 2009 00:00:00 GMT+0800 (中国标准时间)
console.log(new Date(2009, 11, 9, 12, 59, 59, 999));
// → Wed Dec 09 2009 12:59:59 GMT+0800 (中国标准时间) console.log(new Date(1997, 10, 19).getTime());
// → 879868800000
console.log(new Date(1387407600000));
// → Thu Dec 19 2013 07:00:00 GMT+0800 (中国标准时间) console.log(new Date().getTime());
// → 1535781283593
console.log(Date.now());
// → 1535781283593

通过正则表达式,由String创建日期:

"use strict";

function getDate(string) {
let [_, month, day, year] =
/(\d{1,2})-(\d{1,2})-(\d{4})/.exec(string);
return new Date(year, month - 1, day);
}
console.log(getDate("1-30-2003"));
// → Thu Jan 30 2003 00:00:00 GMT+0100 (CET)

PS. 下划线除了用来占位外没有其它含义。

8、强制匹配整个字符串

利用 ^ 和 $ 。例如/^\d+$/匹配完全由数字构成的字符串,/^!/ 匹配由!开头的字符串,而/x^/ 啥也匹配不了。

用 \b 标注单词边界:

console.log(/cat/.test("concatenate"));
// → true
console.log(/\bcat\b/.test("concatenate"));
// → false
console.log(/\bcat\b/.test("xx cat xx"));
// → true

9、Choice patterns

let animalCount = /\b\d+ (pig|cow|chicken)s?\b/;
console.log(animalCount.test("15 pigs"));
// → true
console.log(animalCount.test("15 pigchickens"));
// → false

10、正则匹配的机制

当你进行正则匹配时(test或者exec),正则引擎将从所给字符串的开头开始尝试匹配,接着是第二个字符,第三个字符... 试图在所给字符串中寻找一个匹配,直到找到一个匹配项或者到达字符串末尾结束。要么返回第一个匹配,要么什么都匹配不到。

/**
* 模拟用正则\b\d+ (pig|cow|chicken)s?\b
* 匹配"the 3 pigs"
*/ const str = "the 3 pigs"; function simulateRegex(str, start) {
const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
// 逐个字符尝试匹配,直到找到一个匹配项或者到达字符串尾结束
for(let currentPosition = start; currentPosition != str.length; ++currentPosition) {
let tempPosition = currentPosition;
if(tempPosition == 0 || str[tempPosition - 1] == " ") {} else continue;
// 匹配单词边界通过,前面是标点也是可以的。。
if(!str[tempPosition++] in digits) continue;
// 至少匹配一个数字通过
while(str[tempPosition] in digits) {
tempPosition++;
// 尝试继续匹配数字
}
if(str[tempPosition++] != " ") continue;
// 匹配一个空格通过
let tempWord;
if(str.slice(tempPosition, tempPosition + "pig".length) === (tempWord = "pig") ||
str.slice(tempPosition, tempPosition + "cow".length) === (tempWord = "cow") ||
str.slice(tempPosition, tempPosition + "chicken".length) === (tempWord = "chicken")) {
tempPosition += tempWord.length;
} else {
continue;
}
// 单词匹配成功
if(str[tempPosition] == "s") tempPosition++;
// 有没s都可以
if(tempPosition == str.length || str[tempPosition] == " ") {
// 最后的单词边界
let match = [str.slice(currentPosition, tempPosition + 1)];
return match;
}
}
return null;
} let match = simulateRegex(str, 4);
console.log(match);
// → ["3 pigs"]

11、回溯Backtracking

正则引擎在进行分支匹配(|)或重复匹配(+ *)时,如果发现无法继续再继续往下匹配,就会进行“回溯”。

在进行分支匹配时,如果第一个分支就匹配成功,就不再匹配其它分支,如果不成功就会回溯到分支的入口,进入到另外一个分支继续匹配。

而进行重复匹配时,例如说/^.*x/用匹配"abcxe",.*会首先把所有字符消费干净,当正则引擎发现最后还需要一个x时,*操作符会尝试少匹配一个字符,但是仍然没发现x,于是继续回溯,直到发现x,最终得到字符串abc。

12、The replace method

replace配合正则:

console.log("papa".replace("p", "m"));
// → mapa console.log("Borobudur".replace(/[ou]/, "a"));
// → Barobudur
console.log("Borobudur".replace(/[ou]/g, "a")); // g代表global全部
// → Barabadar

replace的真正强大之处在于可以用“$数字”引用匹配字符串:

console.log(
"Liskov, Barbara\nMcCarthy, John\nWadler, Philip"
.replace(/(\w+), (\w+)/g, "$2 $1"));
// → Barbara Liskov
// John McCarthy
// Philip Wadler "hello, word, every, one".replace(/(\w+),/g, "$1 "); // “$+数字”引用匹配中的分组
// → "hello word every one"
"hello, word, every, one".replace(/one/g, "$& $&"); // “$&”引用整个匹配
// → "hello, word, every, one one"

还可以传入函数:

"hello, word, every, one".replace(/(\w+),/g, str => str.toUpperCase());
// → "HELLO, WORD, EVERY, one"
let stock = "1 lemon, 2 cabbages, and 101 eggs";
function minusOne(match, amount, unit) {
amount = Number(amount) - 1;
if (amount == 1) { // only one left, remove the 's'
unit = unit.slice(0, unit.length - 1);
} else if (amount == 0) {
amount = "no";
}
return amount + " " + unit;
}
console.log(stock.replace(/(\d+) (\w+)/g, minusOne));
// → no lemon, 1 cabbage, and 100 eggs

13、贪婪Greed

function stripComments(code) {
return code.replace(/\/\/.*|\/\*[^]*\*\//g, "");
}
console.log(stripComments("1 + /* 2 */3"));
// → 1 + 3
console.log(stripComments("x = 10;// ten!"));
// → x = 10;
console.log(stripComments("1 /* a */+/* b */ 1"));
// → 1 1

可以用replace来去掉代码中的所有注释。

[^]可以匹配任何字符,因为 /**/可能跨多行,句点 . 无法匹配换行符号。

然而上面最后一行代码结果却出错了,这是为什么呢?

因为(+*?, and {}) 这些操作符号都是贪婪的,就像“回溯”里面提到的,它们总是先尽可能地消费字符,直到无路可走才会回头,这样理所当然会匹配到更长的那一个。解决方案就是在这些符号后面加问号 (+?*???{}?),这样它们就会匹配尽可能少的字符串。

function stripComments(code) {
return code.replace(/\/\/.*|\/\*[^]*?\*\//g, "");
}
console.log(stripComments("1 /* a */+/* b */ 1"));
// → 1 + 1

当要用到重复匹配符时,先考虑用非贪婪版本的。

14、动态构建正则表达式

利用new RegExp(拼接字符串, "gi")构建,gi表示global替换全部和大小写不敏感。

let name = "harry";
let text = "Harry is a suspicious character.";
let regexp = new RegExp("\\b(" + name + ")\\b", "gi");
console.log(text.replace(regexp, "_$1_"));
// → _Harry_ is a suspicious character. let name = "dea+hl[]rd";
let text = "This dea+hl[]rd guy is super annoying.";
let escaped = name.replace(/[\\[.+*?(){|^$]/g, "\\$&");
// escaped → "dea\+hl\[]rd"
let regexp = new RegExp("\\b" + escaped + "\\b", "gi");
console.log(text.replace(regexp, "_$&_"));
// → This _dea+hl[]rd_ guy is super annoying.

15、Search

正则版indexof:

console.log("  word".search(/\S/));
// → 2
console.log(" ".search(/\S/));
// → -1

16、The lastIndex property

需求:设置从字符串的某个字符开始匹配

问题:没有方便的办法

理由:不方便正是js的特性。。。。

解决方案:在【严格的条件】下用lastIndex设定起始位置

严格的条件:表达式必须开启g(global)或者s(sticky)选项,并且必须通过exec方式执行匹配。

lastIndex:正则对象的一个属性,数字,决定了下一个匹配从第几个字符开始。在严格条件 ↑ 下设定才有效。非严格条件下改变该值是毫无作用的。

let pattern = /y/g;
pattern.lastIndex = 3;
let match = pattern.exec("xyzzy");
console.log(match.index);
// → 4
console.log(pattern.lastIndex);
// → 5

仅global:匹配成功,自动更新lastIndex为匹配成功位置的下一个位置(如上),匹配失败,lastIndex重新设置为0。

global:从str[lastIndex]开始向后搜索匹配

sticky:从str[lastIndex]直接开始匹配,不向后搜索。

let global = /abc/g;
console.log(global.exec("xyz abc"));
// → ["abc"]
let sticky = /abc/y;
console.log(sticky.exec("xyz abc"));
// → null

所以只需简单调整一下lastIndex就可以让上面成功的失败、失败的成功:

let global = /abc/g;
global.lastIndex = 6; // 从c开始向后搜索匹配
console.log(global.exec("xyz abc"));
// → null
let sticky = /abc/y;
sticky.lastIndex = 4; // 从a开始匹配
console.log(sticky.exec("xyz abc"));
// → ["abc"]

因为在global启用时,LastIndex在匹配完之后是要自动更新的,所以,当用一个正则对象匹配多次的时候就会出现坑爹的结果:

let digit = /\d/g;
console.log(digit.exec("here it is: 1"));
// → ["1"]
console.log(digit.exec("and now: 1"));
// → null

在s启用,或者啥也不启用时不会有这方面的顾虑。

global的另外一方面影响在于,它改变了match的行为:

console.log("Banana".match(/an/g));
// → ["an", "an"]
console.log(/an/g.exec("Banana"));
// → ["an", index: 1, input: "Banana", groups: undefined]
// global改变了match的行为,本来上述两个
// 输出应该相同的(等价操作),而且["an", "an"]
// 后者本应该是子表达式匹配的字符串,前者的子集

总结。。慎用global

17、遍历匹配项

利用global模式下的lastIndex机制应该是最简便的方法。

let input = "A string with 3 numbers in it... 42 and 88.";
let number = /\b\d+\b/g;
let match;
while (match = number.exec(input)) {
console.log("Found", match[0], "at", match.index);
}
// → Found 3 at 14
// Found 42 at 33
// Found 88 at 40

18、解析INI文件

function parseINI(string) {
// Start with an object to hold the top-level fields
let result = {};
let section = result;
string.split(/\r?\n/).forEach(line => {
let match;
if(match = line.match(/^(\w+)=(.*)$/)) {
section[match[1]] = match[2];
} else if(match = line.match(/^\[(.*)\]$/)) {
section = result[match[1]] = {};
} else if(!/^\s*(;.*)?$/.test(line)) {
throw new Error("Line '" + line + "' is not valid.");
}
});
return result;
} console.log(parseINI(`
searchengine=https://duckduckgo.com/?q=$1
spitefulness=9.7 ; comments are preceded by a semicolon...
; each section concerns an individual enemy
[larry]
fullname=Larry Doe
type=kindergarten bully
website=http://www.geocities.com/CapeCanaveral/11451 [davaeorn]
fullname=Davaeorn
type=evil wizard
outputdir=/home/marijn/enemies/davaeorn`));
// → davaeorn: { fullname: "Davaeorn", type: "evil wizard", outputdir: "/home/marijn/enemies/davaeorn" }​
// larry: { fullname: "Larry Doe", type: "kindergarten bully", website: "http://www.geocities.com/CapeCanaveral/11451" }​
// searchengine: "https://duckduckgo.com/?q=$1"​
// spitefulness: "9.7"

19、国际字符

console.log(/												

最新文章

  1. mysql中boolean类型
  2. dotNet使用HttpWebRequest模拟浏览器
  3. MVC4中下拉菜单和单选框的简单设计方法
  4. 【JavaScript】JavaScript脚本代码的位置及在页面中的执行顺序
  5. c#使用MethodInvoker解决跨线程访问控件
  6. C++ ADO 数据查询
  7. 转:VS2010与SVN
  8. C语言基础03
  9. FormData可实现异步传输二进制文件(即异步文件上传)
  10. sqlserver 按日、周、月统计方法
  11. Spring注解问题,[action中注入service失败
  12. Amaze UI 是一个移动优先的跨屏前端框架。 http://amazeui.org/
  13. git clone代码时候出现的报错
  14. chrome不支持embed标签解决方案
  15. js实现输入某串数字,构建完全二叉树,并判断是否为二叉搜索树
  16. linux外接显示屏,关掉本身的笔记本电脑
  17. maven依赖的描述
  18. P3203 [HNOI2010]弹飞绵羊(LCT)
  19. can协议
  20. python测试开发django-38.多对多(ManyToManyField)查询

热门文章

  1. C 逗號表達式 與返回值
  2. NgDL:第四周深层神经网络
  3. vim 常用
  4. JQuery 获取页面某一元素在屏幕上的位置
  5. cocos2d-x JS 字符串
  6. php 数据库备份还原
  7. vs2017创建支持多框架(net4.6.1;net4.6.2;netstandard2.0;netcoreapp2.0)版本
  8. Visual Studio下运行PowerShell脚本自增小版本号并发布到Nuget服务器上
  9. oracle将unix 时间戳转换为date类型
  10. Abp项目构建、swagger及代码生成器