跳到主要内容

正则表达式

一个经典的问题

有一个字符串类型的数字,例如:10000000,把这个数字变成下面的形式(从右往左,每个三个数字加一个逗号):

1000000 ---> 1,000,000
100000000 ---> 100,000,000

如果用正则表达式 + replace 函数如何实现?

答案:正则表达式可以写成:/(?=(\B)(\d{3})+$)/g。reaplce(reg , ",")

🔍 解析

\B 表示匹配非单词边界(\b 表示匹配单词边界)。例如一个字符串 hello world,h 之前有一个单词边界,lo 右边有一个单词边界,w 左边有一个单词边界,d 右边有一个单词边界。一个字符串两端会有单词边界,字符串中有空白字符时也会有单词边界。

var str = "hello world";
var reg = /\w+\b/g;
str.match(reg); // ["hello", "world"]

非单词边界就与单词边界相反了,如:

var str = "qwer";
// qw 之间有非单词边界;we 之间有非单词边界;er 之间有非单词边界
str.match(/\B/g);
// ["","",""]

x(?=y) 这样的形式被称为“先行断言”,它用来匹配 x 仅仅当 x 后面跟着 y ,但 y 并不是匹配结果的一部分,这一点很重要,也就是说我们匹配符合条件的 x。比如下面的匹配案例:

var reg = /hello(?=\s\w+)/g;
// str 字符串里有符合条件的 "hello" 字符串
var str = "hello world";
console.log(str.match(reg)); // ["hello"]

例题中的先行断言没有 x,只有 y 部分。也就是说我们匹配的是一个空字符串(什么都没有的),这个空字符串后面有一个非单词边界和三个连续的数字,三个连续的数字可能有多个(后面有一个 +),我们要从右往左匹配,后面需要加一个 $。把匹配到的空字符替换成 ,,因为是全局匹配,因此就全部替换了。

var reg = /(?=(\B)(\d{3})+$)/g;
var str = "10000000";
console.log(str.match(reg)); // ["", ""]

\B 在这个题目中的作用是防止 , 插入到最左边,例如一个数是:100000,如果不加 \B,替换后就变成了 ,100,000。左边的 1 有单词边界,我们要匹配没有单词边界的数字。

断言

x(?=y) 被称为 “先行断言”,除了先行断言之外还有下面几种断言:

  1. (?<=y)x 匹配 x,仅当 x 前面是 y。这种叫做后行断言。
  2. x(?!y) 仅仅当 x 后面不跟着 y 时匹配 x,这被称为正向否定查找。
  3. (?<!y)x 仅仅当 x 前面不是 y 时匹配 x,这被称为反向否定查找。

四个边界类断言:

  1. ^ 匹配输入的开头。如果多行模式设为 true,^ 在换行符后也能匹配;
  2. $ 匹配输入的结束。如果多行模式设为 true,$ 在换行符前也能匹配;
  3. \b 匹配单词边界;
  4. \B 匹配非单词边界;

(?:x) 这种格式的匹配符与上面的断言很相似,但它不是断言。带有 ?: 的括号被称为“非捕获括号”,match 方法、exec 方法在不使用全局匹配时,都会返回匹配到的括号里的内容和全局内容。而如果加上了 ?: 的括号则不会被捕获。例如:

var reg = /(?:hello)\s?\w+(\d)$/;
var res = "hello world123".match(reg);
console.log(res); // { 0: "hello world123", 1: "3" }
// hello 并不会匹配到

replace 中的捕获标记符也不会捕获到有 ?: 的括号。如:

var reg = /(?:hello)\s?\w+(\d)$/;   // 没有 ?: 时,$1 指向左边第一个括号
var res = "hello world123".replace(reg, "$1--aaa");
console.log(res); // 3--aaa

trim 函数

实现一个字符串的 trim 函数。

trim() 方法返回一个从两头去掉空白字符的字符串,并不影响原字符串本身。

例如:

var newStr = "   hello world ".trim();
console.log(newStr); // "hello world"

最简单的实现:

function trim(str){
return str.replace(/^\s+|\s+$/g, "");
}

而在官方的 polyfill 中,是这么写的:

if (!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
};
}

\s 后面还多了两个东西:\uFEFF\xA0。有些软件,在保存一个以UTF-8编码的文件时,会在文件开始的地方插入三个不可见的字符(0xEF 0xBB 0xBF,即 BOM),转码后是\uFEFF,因此我们在读取时需要自己去掉这些字符。\xA0 其实就是HTML中常见的 &nbsp;(一个空格,之所以要在 HTML 中使用 &nbsp; 来转义空格字符,是因为在 HTML 文档中多个连续的空格字符会被合并成一个,而使用 &nbsp; 转义字符就可以显示连续的空格符了)。