20行代码实现一个简单的Regex Engine

项目地址git

npm install or yarn //安装依赖

npm run test // 运行测试文件

复制代码

咱们将会实现一个简单正则引擎,它的规则以下github

语法 含义 example 匹配
a 匹配文本字面量 "a" "a"
* 匹配0或多个前字符 "a*" "","a","aa"
? 匹配0或1个前字符 "a?" "","a"
. 匹配任意字符 "." "a","b"
^ 起始匹配符 "^a" "a","aa","ab"
$ 结尾匹配符 "a$" "aaa","bba"

咱们用Typescript编写Codetypescript

先考虑单个字符匹配

单个字符,状况简单的多。它有如下五种状况:shell

/** * * @param pattern 匹配的字符 * @param char 须要被匹配的字符 * * case 1 : matchOneChar('','a')->true * case 2 : matchOneChar('a','')->false * case 3 : matchOneChar('.','b')->true * case 4 : matchOneChar('a','b')->false * case 5 : matchOneChar('a','a')->true * * 单个字符匹配有以上状况 */
const matchOneChar =(pattern:string,char:string):boolean=>{
    if(!pattern) return true;   //case 1
    if(!char) return false; //case 2
    if(pattern === ".") return true; //case 3
    return pattern === char;  // case 4,5
}
复制代码

配上测试

import {search,matchOneChar} from './regex';

describe("匹配单个字符",()=>{
    it("匹配字符为空",()=>{
        expect(matchOneChar('','a')).toBe(true)
    });
    it("匹配内容为空",()=>{
        expect(matchOneChar('a','')).toBe(false);
    })
    it("特殊字符 . 匹配",()=>{
        expect(matchOneChar('.','a')).toBe(true);
        expect(matchOneChar('.','b')).toBe(true);
    })
    it("匹配字符是否相同",()=>{
        expect(matchOneChar('a','a')).toBe(true);
        expect(matchOneChar('a','b')).toBe(false);
    })
})
复制代码

多个字符的匹配

先考虑头部对齐匹配

仅考虑文本匹配npm

/** * * @param pattern 匹配字符 * @param text 匹配文本 */
const match =(pattern:string,text:string):boolean=>{
    if (pattern === "") return true;
    if (!text) return false;
    return matchOneChar(pattern[0],text[0]) &&match(pattern.slice(1),text.slice(1));
}
复制代码

添加 “$”匹配符

/**
 * 
 * @param pattern 匹配字符
 * @param text    匹配文本
 */
const match =(pattern:string,text:string):boolean=>{
    if (pattern === "") return true;
    if (pattern === "$"&& text==="") return true;
    if (!text) return false;
    return matchOneChar(pattern[0],text[0]) &&match(pattern.slice(1),text.slice(1));
}
复制代码

添加 “?”匹配符

const match =(pattern:string,text:string):boolean=>{
    if (pattern === "") return true;
    if (pattern === "$"&& text==="") return true;
    if (!text) return false;
    //  添加 “?”匹配
    if (pattern[1] === '?'){
        return matchOneChar(pattern[0],text[0])&&match(pattern.slice(2),text.slice(1))  || match(pattern.slice(2),text);
    }
    return matchOneChar(pattern[0],text[0]) &&match(pattern.slice(1),text.slice(1));
}
复制代码

添加 “*”匹配符

const match =(pattern:string,text:string):boolean=>{
    if (pattern === "") return true;
    if (pattern === "$"&& text==="") return true;
    if (!text) return false;
    if (pattern[1] === '?'){
        return matchOneChar(pattern[0],text[0])&&match(pattern.slice(2),text.slice(1))  || match(pattern.slice(2),text);
    }
    if (pattern[1] === '*'){
        return matchOneChar(pattern[0],text[0])&&match(pattern,text.slice(1))  || match(pattern.slice(2),text);
    }

    return matchOneChar(pattern[0],text[0]) &&match(pattern.slice(1),text.slice(1));
}
复制代码

头部不对齐状况

能够经过转换匹配符或是匹配文本转换成头部对齐状况,有两种处理方案:bash

  • 匹配符前面加上“.*”
const search =(pattern:string,text:string):boolean=>{
    if (pattern[0] === '^') {
        return match(pattern.slice(1), text);
      } else {
        return match('.*' + pattern, text);
      }
}
复制代码
  • 拆分匹配文本,只要又一个匹配上就OK
const search =(pattern:string,text:string):boolean=>{
    if (pattern[0] === '^') {
        return match(pattern.slice(1), text);
      } else {
        return text.split('').some((_, index) => {
            return match(pattern, text.slice(index));
          });
      }
}
复制代码

总结:

思考过程由顶层开始测试

头部不对齐 -> 头部对齐 -> 单个字符匹配 将大的问题一步一步根据条件拆分红小问题,如此往复。 整个实现代码在20行左右。ui

相关文章
相关标签/搜索