aardio 文档

aardio 模式匹配快速入门

简单的模式匹配示例:

var str = string.match("字符串","这里是模式串")   

模式串有两个基本的元素:

  1. 模式元 - 指定匹配的数据
  2. 运算符 - 指定匹配的规则

一、模式元

模式元是模式串里的最小匹配单位,用于表示字符或某一类的字符(或连续的字符序列)。

1. 字面值

字面值指的是 "a" 表示 "a", "b" 表示 "b ",字面意思是什么就表示什么,这个不难理解。

var str = string.match("abcd","abc")   

2. 任意字符

3. 预定义的字符类

正则表达式兼容的字符类:

\n 换行符 \r 回车符 \w 字母和数字 \s 空白符 \d 数字 \f 换页符 '\x0c' \v 匹配一个垂直制表符。等价于 '\x0b' \t 制表符

其他字符类:

\a 字母
\c 控制字符
\i 是否ASCII字符( 字节码 < 0x80 )
\l 小写字母
\p 标点字符
\u 大写字母
\x 十六进制数字(正则表达式里用于16进制字符前缀,即\xhh
\z 表示 '\0'

大写表示反义( 与正则表达式相同 ),例如\D表示不是数字的字符。

import console;

var str = string.match("12345678","\d")  
console.log( str ) //显示 1

console.pause();

4. 自定义的字符集合

例如[abcd]表示字符是 abcd 其中一个,或者[a-z]表示 a 到 z 的任意小写字母字符。

//还是返回 1
var str = string.match("12345678","[1230-9]")   

参考:模式语法 - 字符集合

5. 元序列

例如 <a-zabcd>

元序列的语法与**自定义字符类**类似,但是**元序列**表示的是一连串的字符**序列**。

// 显示 1234
var str = string.match("12345678","<1230-9>")   

要注意元序列内部执行贪婪与激进的匹配规则,只要能匹配到内容就会尽力向后走并且不会回溯(这在需要局部禁止回溯时非常有用)。

元序列实际上是一种更精简速度更快的模式匹配迷你版本,元序列可将一个长度不限的字符序列转换为模式元(可对其应用其他模式式运算符),并且在内部可以支持大多数其他模式语法(不能在元序列内创建捕获组,可以引用捕获组)。

aardio 通过『元序列』限制并避免了在使用模式匹配时复杂度无限上升、并以此换取最大化的匹配速度,更精简更可控的匹配语法。

测试数据: string.regex 耗时 455 毫秒的查询,preg 库正则表达式需要 49 毫秒,而模式匹配仅需要 1.4 毫秒。

参考:模式语法 - 元序列

二、运算符

运算符是指定一个模式应当怎样去匹配的特殊符号。

运算符只能用于模式元。

如果将『模式元』理解为编程语言里的操作数,那么『运算符』就相当于编程语言里的操作符。

运算符有很多种,而最常用的就是用来指定匹配次数的量词。

量词

//匹配结果为 12345 
var str = string.match("12345678","\d{2,5}")   
//匹配结果为 12345678 
var str = string.match("12345678","\d+")   
//匹配结果还是 12345678 
var str = string.match("12345678","\d+\s*") //这里的"\s*"匹配零个或多个空格   

其他运算符请参考:模式语法 - 运算符

三、模式匹配应用实例

模式匹配对正则表达式进行了简化,保留基本语法,牺牲一些功能换取效率。

最简单的模式串

例如模式串 \d+ 包含两个部分:

\d+ 前面的模式元 \d 指定匹配什么样的字符,后面运算符 + 指定怎样去匹配、匹配多少个字符。

模式匹配身份证号码

再例如身份证匹配的模式 \d{15,18}  用于匹配 15 到 18 个数字。

  1. 自定义字符集合

    但我们遇到的一个问题是,最后一位可能不是数值,也有一种可能是"x",也就是说**可能是数值也可能是x**,那么我们就要改为:

    \d{14,17}[\dx]

    但是别人还有可能把 x 大写啊,所以继续修改为:

    \d{14,17}[\dxX]

  2. 元序列

    身份证还不能是 16 位, 17 位数字。所以我们继续修改为:

    var str = string.match("身份证号码","\d{14,14}<\d\d\d>*[\dxX]" )   
    

    上面的 <\d\d\d> 是一个自定义的元序列( metasequence )

    像这些,多动手折腾很快会明白,别人灌输给你的知识永远没有自已探索到的理解深刻。

  3. 首尾锚点

    更进一步,19 位,20 位的数值都可能匹配成功,因为他只要匹配其中18位就成功了,这时候我们还要限定他前面后面都不能有其他的字符,这时就要指定首尾锚点,用 ^ 表示文本开始位置的锚点,用 $ 表示文本结束位置的锚点。

    例如:

    var str = string.match("身份证号码","^\d{14,14}<\d\d\d>*[\dxX]$" )   
    
  4. 跳过空白字符

    但是这样还有一个问题,如果身份证号码前后可能有空格怎么办呢?可以用 \s 表示所有空白字符,加上匹配零到多次的量词运算符就是\s*,再改进如下:

    var str = string.match("身份证号码","^\s*\d{14,14}<\d\d\d>*[\dxX]\s*$" )   
    
  5. 边界断言

    如果身份证前后不仅仅是空格,还可能有别的字符,哪怎么办呢?

    这里我们可以接触一个新的概念:边界断言

    边界断言是在模式元前面加一个表示否定的感叹号,表示从不匹配(左侧)到匹配(右侧)的边界。

    例如 !\d 表示不是数字到数字交界的位置,这是一个试探性的零宽匹配,匹配的仅仅是边界,消耗的字符串长度为 0。

    加上边界断言后的代码如下:

    import console; 
    
    var idNumber = "sfz612323198608110000fgd"  
    idNumber = string.match(idNumber,"!\d(\d{14}<\d\d\d>*[\dxX])![^\dxX]")  
    
    console.log(idNumber);
    console.pause(true);
    

    边界断言是一种左右双向的零宽断言(Zero-width Assertions)语法,
    结合向左回顾断言(逻辑取反) + 向右预测断言以实现边界检测,在字符串首尾两端只检测有字符的内侧边界。

这些需要在**实践中嗑碰出来的知识,如流水无形,无一定之规**。

四、模式匹配中的那些括号

可以熟练使用模式匹配中的括号,表示你精通了模式匹配。

参考:

Markdown 格式