初步了解正则表达式,附带快手校招一面的一道代码题

52 阅读2分钟

限定符

  1. ?    代表?前面的字符要出现一次和零次
  2. *     代表*前面的字符可以没有也可以出现多次
  3. +     代表+前面的字符要出现一次以上
  4. {}     代表前面的字符出现的范围 比如ab{2,4}c 就会匹配abbc abbbc abbbbc
  5. ()     ()括起来的字符代表一个整体 比如(ab)+ 代表ab这个整体要出现一次以上

或运算符

  1. |     假如我要匹配a cat 或者a dog 我就要写成 a (cat|dog)

字符类

  1. []     [abc] 等价于(a|b|c) 方括号里面的内容代表要求匹配的字符只能取自于它 们,我们可以在方括号里指定字符的范围 比如[a-z]代表所有的小写英文字符[a-zA-Z]代表所有的英文字符 [a-zA-Z0-9]代表所有的英文字符和数字

  2. ^     ^叫脱字符 代表取反 比如[^0-9] 代表所有的非数字字符(包括换行符) 必须要写在[]里面才是取反的意思

元字符

  1. \d 代表数字字符
  2. \w 代表单词字符 英文,数字及下划线
  3. \s 代表空白符 包含Tab和换行符
  4. \D 代表非数字字符
  5. \W 代表非单词字符
  6. \S 代表非空白字符
  7. . 代表任意字符,但不包含换行符
  8. ^ 匹配行首 比如^a 只会去匹配行首的a
  9. $ 匹配行尾
  10. \b 代表单词边界 ,不占位置,不代表实际字符
  11. \ 转义字符 既能取消元字符的特殊含义,也能赋予普通字符特殊功能 比如. 就是匹配. 这个点号
  12. / 定届符 /正则表达式内容/标志 /abc/ 匹配 "abc" /\d+/g 匹配一个或多个数字,全局搜索 就是匹配成功了一个 要接着匹配 如果没有g的话 匹配成功一个就停止了

贪婪与懒惰匹配

<.+> 贪婪匹配-尽可能的匹配多个字符 
输入: <div>hello</div><p>world</p> 
匹配结果: <div>hello</div><p>world</p> 
整个字符串都被匹配了,因为它从第一个 < 开始,一直匹配到最后一个 >

<.+?>懒惰匹配-尽可能匹配更短的字符串
输入: <div>hello</div><p>world</p> 
匹配结果: <div> 和 <p> 
找到了两个独立的标签,而不是一个长的匹配

快手校招一面代码题

背景是小编的项目用到了marked库 然后面试官就让我实现一个关键字高亮的一个效果 给了两个示例让自己编写一个函数实现 如果是你们会怎么实现

示例1 输入 str = '我熟悉react,vue,es6' 
关键字数组 keyWords = ['react','vue','es6'] 
预期输出 我熟悉<span>react</span>、<span>vue</span>及<span>es6</span> 

示例2 输入 str = '我熟悉vue,es6' 
关键字数组 keyWords = ['react','vue','vuejs','es6'] 
预期输出 我熟悉<span>vuejs</span>及<span>es6</span> //vue匹配的是vuejs 匹配长的那一个

小编也是不出所料没搞出来哈哈 面试官最后跟我说可以用正则表达式来做 在此之前压根不会正则表达式 我都是用ai直接给我生成 因此写了这篇文章来鞭挞自己,下面是小编下来写的答案,仅供参考

function highlightKeywords(str, keywords) {
  const sortedKeys = [...new Set(keywords)]

  const pattern = new RegExp(sortedKeys.join('|'), 'g');

  let result = str.replace(pattern, match => `<span>${match}</span>`);
  // 直接处理连接符逻辑
  result = result.replace(/,/g, '、');

  // 找到最后一个顿号替换为"及"
  const lastCommaIndex = result.lastIndexOf('、');
  if (lastCommaIndex !== -1) {
    result = result.substring(0, lastCommaIndex) + '及' + result.substring(lastCommaIndex + 1);
  }
  let newKeys = sortedKeys.sort()
  //对于第二个例子排序后为 ['es6','react','vue','vuejs']
  for (let i = 0; i < newKeys.length; i++) {
    if (result.includes(newKeys[i]) && i < newKeys.length - 1 
    && newKeys[i + 1].startsWith(newKeys[i])) {
      result = result.replace(newKeys[i], newKeys[i + 1])
    }
  }
  return result;
}

// 测试
console.log(highlightKeywords('我熟悉react,vue,es6', ['react', 'vue', 'es6']));
// 输出:我熟悉<span>react</span>、<span>vue</span>及<span>es6</span>

console.log(highlightKeywords('我熟悉vue,es6', ['react', 'vue', 'vuejs', 'es6']));
// 输出:我熟悉<span>vuejs</span>及<span>es6</span>