使用 DSL 管理你的正则表达式

最近在写 LCTT 译文的解析工具,写正则、调试正则的过程非常的痛苦。

比如,下面的这个正则表达式就是我用来提取文章的英文标题的正则。由于 LCTT 的文章元信息存储历史上出现过多次,所以我不得不写一个比较复杂的正则来匹配出想要的结果。

比如,下面的这段代码是我用来从文件路径当中提取出不同内容的正则,看起来十分复杂,对吧?

/(?<collectDate>(?<type>(?<path>\.\/)(?:published))\/(?<yearOrSeries>(?:.+)?)\/(?<month>(?<=\d+)\.(?=\d+))?)(?:⭐️|⭐️⭐️)?(?<title>.+)\.md/

相比于看起来复杂,更加麻烦的是可维护性。正则表达式的可维护性相比于正常我们熟悉的编程语言来说,是差了一大截。基本上如果要对上述这段正则表达式进行修改或者二次开发,难度极高。但借助一些工具,我们可以使用类似 DSL 的方式来管理正则表达式,则可以以一个更友好的方式来维护你的正则表达式。

d2b5ca33bd970f64a6301fa75ae2eb22

比如,以我为例,上面这张图片中的定义,便是上方正则的等价替换。虽然写了更多的代码,但确实可读性、易于理解性和易于维护性,有了显著的提升。

想要使用,也不复杂,只需要使用对应的 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,也是一个不错的选择。

2 thoughts on “使用 DSL 管理你的正则表达式

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注