最近在写 LCTT 译文的解析工具,写正则、调试正则的过程非常的痛苦。
比如,下面的这个正则表达式就是我用来提取文章的英文标题的正则。由于 LCTT 的文章元信息存储历史上出现过多次,所以我不得不写一个比较复杂的正则来匹配出想要的结果。
比如,下面的这段代码是我用来从文件路径当中提取出不同内容的正则,看起来十分复杂,对吧?
/(?<collectDate>(?<type>(?<path>\.\/)(?:published))\/(?<yearOrSeries>(?:.+)?)\/(?<month>(?<=\d+)\.(?=\d+))?)(?:⭐️|⭐️⭐️)?(?<title>.+)\.md/
相比于看起来复杂,更加麻烦的是可维护性。正则表达式的可维护性相比于正常我们熟悉的编程语言来说,是差了一大截。基本上如果要对上述这段正则表达式进行修改或者二次开发,难度极高。但借助一些工具,我们可以使用类似 DSL 的方式来管理正则表达式,则可以以一个更友好的方式来维护你的正则表达式。
比如,以我为例,上面这张图片中的定义,便是上方正则的等价替换。虽然写了更多的代码,但确实可读性、易于理解性和易于维护性,有了显著的提升。
想要使用,也不复杂,只需要使用对应的 DSL 语法来完成定义,即可完成正则表达式的构建。以我上面的这段正则为例,我使用的是 javascript 的 magic-regexp
包,实际使用时,大概是这样的,定义出对应的正则,直接基于其进行匹配即可。
const magicRegxp = require('magic-regexp');
const { createRegExp, exactly, oneOrMore, char, anyOf, carriageReturn, global, multiline, linefeed, maybe, digit } = magicRegxp;
const regExp = createRegExp(
exactly("./").groupedAs("path")
.and(anyOf("published")).groupedAs("type")
.and(exactly("/"))
.and(maybe(oneOrMore(char)).groupedAs("yearOrSeries"))
.and(exactly("/"))
.and(maybe(
exactly(".").after(oneOrMore(digit)).before(oneOrMore(digit)).groupedAs("month")
)).as("collectDate")
.and(maybe(
exactly("⭐️").or(exactly("⭐️⭐️"))
))
.and(exactly(oneOrMore(char)).groupedAs("title"))
.and(exactly(".md")
));
let result = regExp.exec(file_content);
console.log(result.groups.title);
Code language: JavaScript (javascript)
当然,这样的维护方式,在不同的语言下有着类似的实现,如果你使用 Golang ,那么 Rex,也是一个不错的选择。
真DSL,那不如Pomsky(就是rulex,Rust做的编译成正则表达式的语言)
我看看去