夜色下的梧桐树

The Magic of Regex

用正则来解题...

1.提取字符串中所有 4 位连续数字

给定一段字符串,找到所有不重复的连续的 4 个数字组成的子字符串,例:123456 -> ['1234', '2345', '3456']

// 输入
"abc1234d567890123456efg345hi777777j890"
 
// 输出
['1234', '5678', '6789', '7890', '8901', '9012', '0123', '2345', '3456', '7777'];
解:
// 法一
[...'abc1234d567890123456efg345hi777777j890'.matchAll(/(?=(\d{4})(?<!\1.+))/g)].map((e) => e.pop());
 
// 法二
[...new Set([...'abc1234d567890123456efg345hi777777j890'.matchAll(/(?=(\d{4}))/g)].map((e) => e.pop()))];

2.Reverse case

const input = 'Hello world!';
 
const reverseCase = (txt) => {
   // write your code here
};
 
console.log(reverseCase(input)); // "hELLO wORLD!"
解:
'Hello world!'.replace(/[a-z]/gi, (c) => String.fromCharCode(c.charCodeAt() ^ 32));

3.任务队列

setTimeout(() => {
   console.log(1);
}, 0);
new Promise((resolve) => {
   resolve();
   console.log(2);
}).then(() => {
   console.log(3);
});
const foo = async () => {
   console.log(4);
   await bar();
   console.log(5);
};
const bar = () => {
   console.log(6);
};
foo();
console.log(7);
解:
2467351

4.转换金额

const convertToPrice = (num: string) => {
   // write your code here
};
 
console.log(convertToPrice(12)); // $12.00
console.log(convertToPrice(1687001)); // $1,687,001.00
console.log(convertToPrice(136.456)); // $136.45
console.log(convertToPrice(-160164.2)); // -$160,164.20
解:
const convertToPrice = (num: string) => {
   // write your code here
   const formatNum: string = num.toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,');
   return '$' + formatNum;
};

5.三角形

// write your code here
console.log('*********'.replace(...));
// *********
// ********
// *******
// ******
// *****
// ****
// ***
// **
// *
解:
console.log('*********'.replace(/(?!^)(?=(\*+))/g, '$1\n'))

6.获取字符串最后一个数字

// 取出 `购买1个` 中的数字 `1`
const str = '对不起,【新客专享!19.9 元 4 件】润百颜水润次抛 2 支+美白次抛 1 支+屏障调理面膜 1 片全活动周期内最多只能购买 1 个。';
解:
str.match(/\d+/g).pop()

7.匹配所有双引号外的文字

const input = 'Hello my "name" is "John Smith Doe", nice "to meet" you.';
 
input.match(/开始你的表演/g); // ['Hello my ', ' is ', ', nice ', ' you.']
解:
/(?:"[^"]+"\K)?[^"]+/g;

8.匹配由小写字母组成的合法的减法表达式,要求等式三个部分齐全,且“被减数”必须是“减数”的子串

匹配
aa-a=a
abc-b=ac
aaaa-aa=aa
aabb-ab=ab
aabb-aab=b
aabbcc-aa=bbcc
aaaabbbb-ab=aaabbb
ababab-b=abaab
ababab-b=aabab
ababab-b=ababa
 
不匹配:
aa-a=aa
abc-b=a
aaaa-aa=aaa
aabb-aab=ab
a-a=
aa-=aa
aaaabbbb-ab=aaaabbb
ababab-aab=bbab
abcd-ac=bd
++-+=+
 
解:
/^(?=.+-.+=.+)(\w*)(\w*)(\w*)-\2=\1\3$/gm;

9.匹配由两种不同字符组成的字符串,前者重复次数与后者相同

匹配
ab
aabb
aaabbb
cd
ccccdddd
eeeeeeefffffff
++++++------
[[[[[]]]]]
(((())))
 
不匹配
abc
a
aab
abb
abab
aaaabbb
aabbbb
aabbaabb
cdd
abc
解:
/^(?=(.)\1*((?!\1).))(\1(?3)?\2)$/gm;

10.判断包含 ABCD 中的任意两种, 且仅有两种

通过
12A345678B
777C542AA12
AB
CDC
BD12
123AB456
AAABBB
1A2A3A4C5C1C1
 
不通过
ABC
ABCD
123ABC
12345A123
123
解:
/^(?=.*([ABCD]).*(?!\1)([ABCD]))(?!.*(?!\1|\2)[ABCD]).*/gm;

11.判断只包含张三、李四、王五和赵六中的两种

通过
我认识张三和李四
张三和王五在玩游戏
李四和赵六今天没来
张三先到赵六后到的
王五:“张三是我的好朋友
张三:“我叫张三王五是我的好朋友
张三丰:“我叫张三王五六是我的好朋友
 
不通过
今天张三请假了
张三李四和王五在玩游戏
昨天张三李四王五和赵六在一起打了麻将
我认识张四和李三
张三:“我叫张三
今天没有人请假
解:
/^(?=((?!(张三|李四|王五|赵六)).)*((?2))(?:(?1)|\3)*(?!\3)((?2)))(?!(?:(?1)|\3|\4)*(?!\3|\4)(?2)).*$/gm;

12.匹配一行文字中存在出现过两次且仅出现过两次的数字

匹配
1 1
1 1 2
1 1 2 2
1 2 3 1
11 11
10 11 12 100 10
10 20 30 20 30
10 20 30 20 30 30
100 200 300 404 500 200 300 300
1 1 1 2 2
 
不匹配
1
11
1 1 1
1 2 3
1 2 3 123
1 2 3 1 1
10 11 12 100 100 100 100
10 20 30 20 30 20 30
10 20 30 20 30 30 20
100 200 300 404 500 200 300 300 200
解:
// PCRE
/^
(?*.*?\b(\d+)\b)  # 非元组断言捕获数字进入组1后续匹配失败后确保可以回溯到断言组内重新捕获组1
(?>.*?\b\1\b){2}  # 元组匹配该数字出现两次重复匹配失败后确保不会回溯到组内防止.*?中出现组1内容
(?!.*\b\1\b).*    # 后续不再出现组1中的数字
$/gmx
 
// JS
/^.*(\b\d+\b)(?<!\b\1\b.*\b\1\b)(?=.*\b\1\b)(?!.*\b\1\b.*\b\1\b).*$/gm;

13.找到在括号外的所有字符串

(题目要求:找到所有在)最外层配对的括号外(的所有字符串,)注意括号有可能(会((嵌套))哦!) :) (async () => { await console.log(1); })()) 加油 ()()(())():((1)((((((2)((3)))))))最好匹配连续,(且没有多余的分组())() 2 * (1 + 3) = 8(eight) :( 你成功了吗?(yes) no
解:
/(\((?:[^()]|(?1))*+\))(*SKIP)(*F)|.+?(?=$|(?1))/gm;

14.在 JavaScript 中,关键词 void 是什么?

A.类型;
B.方法;
C.运算符;
D.声明关键词;
解:
C

15.尽可能短的代码实现 FizzBuzz

尽可能短的代码实现 FizzBuzz

解:
[...Array(100)].map((_, i) => (++i % 3 ? '' : 'fizz') + (i % 5 ? '' : 'buzz') || i);
for (a = [], i = 0; i < 100; ) (a[i] = (++i % 3 ? '' : 'Fizz') + (i % 5 ? '' : 'Buzz') || i), a;

16.try catch

以下代码输出什么

const foo = () => {
   try {
      new Error('nope');
      return 1;
   } catch (ex) {
      return 2;
   } finally {
      return 3;
   }
};
 
const bar = () =>
   new Promise((resolve, reject) => {
      try {
         new Error('nope');
         resolve(1);
      } catch (ex) {
         reject(2);
      } finally {
         resolve(3);
      }
   });
 
console.log(foo());
console.log(await bar());
解:
3
1

17.变量提升

以下代码输出什么

if (true) {
   foo();
 
   function foo() {
      console.log(1);
   }
 
   foo = 2;
 
   function foo() {
      console.log(3);
   }
 
   foo = 4;
 
   console.log(foo);
}
console.log(foo);
解:
3
4
2

18.严格模式

以下代码输出什么

function func1() {
   arguments[0]++;
   return arguments[0];
}
 
function func2(a, b) {
   arguments[0]++;
   return a;
}
 
function func3(a, b = 0) {
   arguments[0]++;
   return a;
}
console.log(func3(func2(func1(0))));
解:
// 当非严格模式中的函数有包含剩余参数、默认参数和解构赋值,那么arguments对象中的值不会跟踪参数的值(反之亦然)。
2

19.匹配由第一个遇到的可选的“开始”到第一个遇到的可选的“结束”之间的字符串

// 题目要求:匹配由第一个遇到的可选的“开始”到第一个遇到的可选的“结束”之间的字符串。

123456789
123456结束789
123开始456789
123开始456结束789
123结束456开始789
1开始23开始456结束789
123开始456结束78结束9
123456结束78结束9
12结束3开始456结束78开始9
12开始3开始456开始78开始9
解:
// js
/(?<=开始|^(?!(?:(?!结束).)*开始))(?<!结束.*)(?:(?!结束).)+/gm
// PCRE
/(?:^|\G)(?>(?:(?!结束).)*?开始\K|^)(?:(?!结束).)+/gm

20.方括号内数字累加

add[1] + 1 // 2
add[1][2][3] + 4 // 10
add[10][-5][3][100] *2 // 216
+add[1][2][3][4][5] // 15
解:
const makeProxy = (total = 0) =>
   new Proxy([], {
      get(_, prop) {
         if (prop === Symbol.toPrimitive) return () => total;
         return makeProxy(total + Number(prop));
      },
   });
const add = makeProxy();

21.找到出现一次和多次的字符并标记

编写一个函数,接收一个字符串后,对于只出现一次的字符标记为(,出现多次的标记为),并组成一个新的字符串返回。 注:判断重复时忽略大小写。

示例 1:

输入:"din"
输出:"((("

示例 2:

输入:"recede"
输出:"()()()"

示例 3:

输入:"(( @"
输出:"))(("
解:
const rep = (text) => text.replace(/(.)(?=.*\1|(?<=\1.+))|./g, (_, g) => '()'[+!!g]);
rep('din'); // "((("
rep('recede'); // "()()()"
rep('(( @'); // "))(("

22.生成包含 1-100 的数组,但不包含 3 的倍数

生成包含 1-100 的数组,但不包含 3 的倍数
要求 1:一行代码、禁止字面量(出现手动定义 1、2、4、5...的行为)
要求 2:不使用 Array 和 String 相关的任何方法,包括原型链以及构造方法
要求 3:禁止声明变量(var、let、const 以及全局变量)
要求 4:代码中禁止出现等于号"="
解:
Object.values(
   (function (f, i, r) {
      return f(f, i, r);
   })(
      function (f, i, r) {
         return i > 100 ? r : f(f, i + 1, i % 3 ? { ...r, [i]: i } : r);
      },
      1,
      {}
   )
);
[
   ...(function* () {
      while (arguments[0]++ < 100) if (arguments[0] % 3) yield arguments[0];
   })(0),
];

23.生成 1-100 的 FizzBuzz 数组

生成1-100的FizzBuzz数组
条件1:禁止出现字面量(直接定义[1, 2, "Fizz", ...])
条件2:禁止出现任何值比较和条件语句(if、while、switch、三元表达式、==、!=、&&、||等)
条件3:一行代码
解:
Array.from({ length: 100 }, (_, i) => [++i, 'Fizz', 'Buzz', 'FizzBuzz'][!(i % 3) + !(i % 5) * 2]);

24.获取函数名

const getCurrentFunctionName = () => {
   // 开始你的表演
};
 
const foo = () => {
   console.log(getCurrentFunctionName());
};
 
function bar() {
   console.log(getCurrentFunctionName());
}
 
foo();
bar();
解:
const getCurrentFunctionName = () => /.*at ([\w$]+)/s[Symbol.match](new Error().stack)[1];

25.找到所有成对双引号内的单引号

The quick "bro'wn fox'" jumps' over "the la'zy" do'g. The "qui'ck" bro'wn fox ' ju"mps' over the'"lazy"dog'.
解:
// https://regex101.com/r/hBeSAg/1
/(?>(?!^)\G|").*?(?:"(*SKIP)(*F)|\K'(?=.*"))/gm;

26.在由数字和括号组成的字符串中(括号不对称),找到最内层括号内所有偶数位的数字

(1359)0112(7)10(89)369
123(4567)89876(5432101)234567
016(1359)1369(543)(2123)01234
 
(123454(1359)01(12(7)10(89)369
12)3(4567)89876(54(32)101)234567
01(2346(1353569)1369(543)(2123)012)34123
解:
// https://regex101.com/r/rCNPnT/1
/(?:\(|(?!^)\G)\d\K\d(?=\d*\))/gm;

27.找到成对的双引号

12"34"56"78"9
解:
// https://regex101.com/r/Xm7USm/1
/"(?:[^\\"\n]|\\.)*"/gm;

28.使用正则表达式分割字符串为数组

// 输入
const csv = '1234567890|ABCDE|""|"01|02|03"|TEST||AT|899';
// 输出
[
    '1234567890',
    "ABCDE",
    '""',
    '"01|02|03"',
    'TEST',
    '',
    'AT',
    '899'
]
解:
csv.match(/"[^"]*"|^\w*(?=\|)|(?<=\|)\w*/g)

29.对于两位数以上的数字,将其中间的数字用*代替,*与被加密的数字位数相同,但至少存在3个以上

// https://regex101.com/r/1tbZyu/1
// 对于两位数以上的数字,将其中间的数字用*代替,*与被加密的数字位数相同,但至少存在3个以上
12
123
1234
12345
123456
1234567
12345678
123456789
解:
((?<=^.).{0,3}(?=.$))|(?<!^).(?=.+$)
(?<=\d)(?>\d(?=\d{3})|\G\d|(\d*(?=\d)))(?=\d)

30.不使用括号调用函数

function foo() {
    console.log('ok');
}
 
// 不使用括号(),写出至少三种方式可以调用foo方法并打印 "ok"
解:
//1
  foo``;
//2
new foo;
//3
window.valueOf = foo;
+window;