2016-05-20 19:49:30

正则表达式利器:JSVerbalExpressions解析与使用

如果你不觉得正则表达式很难读写的话,要么你是一个天才,要么,你不是地球人

很多语言中总会有一只名叫"正则表达式"的拦路虎,用它那由一大堆令人眩晕的数字,字母和字符的爪子直接拍晕你.

实际上,正则表达式能够干的活,全部都能够用正常的代码来实现.至于为何要使用正则表达式,博主认为有以下两点

  • 效率更高.编译器能够利用正则对象对复杂文本分析做出特别的优化,这涉及到有限自动机等相关内容
  • 代码更简短.例如jQuery的选择器引擎Sizzle的开头有几十个正则,如果全部都用正常的代码进行展开的话,代码将会变得非常臃肿

正则表达式能够被常规代码所实现, 这意味着正则本身容易被描述.著名开源Javascript开源库JSVerbalExpressions便使用了这一点.使用JSVerbalExpressions这一利器,即使是完全不会正则的入门者,也能构建出正确的正则表达式

JSVerbalExpressions

JSVerbalExpressions是Javascript的开源库,目前Github达到了9400+的Star数量,在另一方面体现了它对正则表达式开发的帮助之大.

本文以Node.js服务器端环境进行演示.前端开发者也可以在浏览器中导入.

注: 以下全部以VerEx来称呼JSVerbalExpressions

安装

npm install verbal-expressions

引入

var VerEx = require("verbal-expressions");

初见

var regExp = VerEx();
console.log(regExp)

首先直接将一个实例输出到控制台

可以看到,VerEx()返回的就是一个正则表达式,而且此实例上的方法并没有RegExp的常见方法,因此可以先认为这些方法全部都是此实例的静态方法

查询VerEx源码,如下

    function VerbalExpression() {
        var verbalExpression = new RegExp();

        // Add all the class methods
        VerbalExpression.injectClassMethods(verbalExpression);

        // Return the new object.
        return verbalExpression;
    }

    // Define the static methods.
    VerbalExpression.injectClassMethods = function injectClassMethods(verbalExpression) {
        var method;
        // Loop over all the prototype methods
        for (method in VerbalExpression.prototype) {
            // Make sure this is a local method.
            if (VerbalExpression.prototype.hasOwnProperty(method)) {
                // Add the method
                verbalExpression[method] = VerbalExpression.prototype[method];
            }
        }

        return verbalExpression;
    };

可以得知:VerEx每次运行时,都会先生成一个空的正则表达式,然后用injectClassMethods方法,来把VerbalExpression.prototype的成员全部添加到这个空的正则表达式中作为静态属性和方法.这样就避免了直接修改RegExp.prototype.

直接修改对象的原型一直是不可取的,除非那个对象只有自己在使用.

另外,注意到范例中并没有使用new,实际上,由于此函数中完全没有使用this,因此无论用不用new都没有问题

静态属性与链式调用

静态属性

VerEx()得到的正则被添加进去了4个静态属性,分别为:

_prefixes
_source
_suffixes
_modifiers

_modifiers就是正则的修饰符了,即正则中的"i","g","m"的排列组合."i"代表忽略大小写,"g"代表全局搜索,"m"代表多行

_prefixes为前缀,_suffixes为后缀,都是作为辅助变量,含义清楚

再来看最后的_source,它只出现在add方法中,用来备份正则的值

        add: function add(value) {
            this._source += value || "";
            this.compile(this._prefixes + this._source + this._suffixes, this._modifiers);

            return this;
        },

如上,add方法中调用了RegExp.prototype.compile方法,更改了原正则的值.

RegExp.prototype.compile()和new RegExp()效果一样.另外,RegExp.prototype.compile已经被弃用,这里使用它是为了利用原始变量,防止创建新变量.

链式调用

VerEx的链式调用比较讨巧,因为它的实例本身就是一个正则对象,大多数方法只需要在最后一句加上return this;即可,个别需要修改正则的值的方法,则利用了返回值就是本身的内置方法,例如所有有输入的方法都调用了用来净化不安全字符的方法sanitize

        sanitize: function sanitize(value) {
            var reRegExpEscape;

            if (value.source) {
                return value.source;
            }

            if (typeof value === "number") {
                return value;
            }

            // Regular expression meta characters, URL: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/regexp
            reRegExpEscape = /([\].|*?+(){}^$\\:=[])/g;

            // Escape RegExp special characters only
            // $& => Last match, URL: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastMatch
            return value.replace(reRegExpEscape, "\\$&");
        },

API

VerEx的API分为五种,分别做介绍

Modifiers

Modifiers用来创建复杂的正则表达式,方法包括

    .anything()
    .anythingBut( value )
    .endOfLine()
    .find( value )
    .maybe( value )
    .startOfLine()
    .then( value )
.startOfLine()和.endOfLine()

这是两个非常容易理解的方法,用来规定正则的起始和结束,无非是用add方法来添加^$进行实现

来看一下官方的范例:创建判断URL是否合法的正则表达式

// Create an example of how to test for correctly formed URLs
var tester = VerEx()
    .startOfLine()
    .then("http")
    .maybe("s")
    .then("://")
    .maybe("www.")
    .anythingBut(" ")
    .endOfLine();

// Create an example URL
var testMe = "https://www.google.com";

// Use RegExp object"s native test() function
if (tester.test(testMe)) {
    alert("We have a correct URL "); // This output will fire}
} else {
    alert("The URL is incorrect");
}

console.log(tester); // Outputs the actual expression used: /^(http)(s)?(\:\/\/)(www\.)?([^\ ]*)$/

最后一行的输出十分惊艳,我们完全没有用到任何正则的知识,只是用了Modifiers的七种含义清晰的方法,这确实非常吸引人

有意思的是,endOfLine方法中并没有任何禁止接下来的输入的语句,接下来我在endOfLine后面再添加方法,看看输出的是什么东西

var regExp = VerEx()
                .startOfLine()
                .then("http")
                .maybe("s")
                .then("://")
                .maybe("www.")
                .anythingBut(" ")
                .endOfLine()
                .then("it should be error");

console.log(regExp)

输出如下:

可以看到,输出的正则没有错误,"$"仍然在最后面,这就体现了_prefixes_suffixes这两个被分离的前后缀属性的作用,即防止错误的正则被生成.

其他
    .anything()    //任意字符及任意数量
    .anythingBut( value )    //除了输入的值,其他均可
    .find( value )    //查找输入的值
    .maybe( value ) //可能出现的值
    .then( value ) //必须出现的值

注意,所有输入都可以是正则表达式,因此传一个VerEx()进来也没有问题,这里的输入都会被sanitize()进行过滤,不必考虑传入值的安全与否

Special characters and groups

此组的方法更加细化,例如匹配几个字符中的一个,或者是指定0-9这种范围

.any( value )
    //.anyOf( value )的简略方法
.anyOf( value )
    //匹配输入的正则中的任意一个,例如"a|b|c"
.br()
    //.lineBreak()的简略方法
.lineBreak()
    //添加换行,这里换行使用了"(?:\\r\\n|\\r|\\n)",完美兼容Unix和Windows
.range( from, to)
    //添加范围,例如range("0","9","a","f"),参数个数是2的倍数
.tab()
    //添加制表符
.word()
    //添加字符类

Modifiers

这里Modifiers提供了修改三种修饰符的方法,即"i","g","m"了

.withAnyCase(bool)
    //是否忽略大小写
.stopAtFirst(bool)
    //是否全局搜索
.searchOneLine(bool)
    //是否只搜索一行

Functions

只包含一个方法,即.replace( source, value )

此方法用来快速替换字符串,官方示例如下:

var result = VerEx().find("red").replace("We have a red house", "blue");

// Outputs "We have a blue house"
alert(result);

这相当于给正则对象添加了一个replace方法

Other

    .add( expression )
        //为正则添加正则,所有Modifiers类型的值实际上都调用了这个方法
    .or()
        //相当于添加"|",代表或,例如VerEx().then("http://").or().then("ftp://")代表匹配"http://"或者"ftp://"

还有一个multiple方法,是专门用来添加非捕获组的,常见场景如下

var regex = VerEx().find("a").multiple(" ").find("b");    //multiple中只有一个空格

var code = "a  b"; //a与b之间有两个空格

console.log(regex.test(code)); // true

结语

VerEx是在是个短小精悍的库,短小在于未压缩前不过区区400行,精悍在于它完全实现了傻瓜式的正则开发.正则将真正成为我们手里的工具.今后,就算面试时考官考了复杂的正则,大可以直接告诉考官现在已经有了相关的正则生成工具.

就算不是Javascript,别担心,VerbalExpression可支持多达三十多种语言呢!

本文链接:https://smallpath.me/post/正则表达式利器:JSVerbalExpressions

-- EOF --