aardio 文档

preg 正则表达式扩展库使用指南

一. 简介

使用PCRE(Perl Compatible Regular Expressions)组件创建的正则表达式支持库, 兼容Perl,PHP正则语法,可在修正符中添加"j"选项以启用 JavaScript 兼容语法( 注:标准库中的 string.regex 使用 VBS 语法 )

用法与 PHP 大同小异,例如 PHP 中使用正则表达式拆分字符串的代码如下:

$keywords = preg_split ("/[\s,]+/is", "hypertext language,,programming");
var_dump($keywords);

那么用 aardio 代码用法类似,区别是先创建正则表达式对象,代码如下:

import preg;
import console;

var $keywords = preg("/[\s,]/is").split ( "hypertext language,,programming");
console.varDump( $keywords )
console.pause(true);

preg().replace()  函数用法类似 string.replace() 替换参数可使用字符串(支持逆向引用)、或替换回调函数、替换表对象。

preg().gmath() 全局查找函数类似 string.gmatch() 函数用法。

下面是一个完整的示例:

import console; 
import preg;

var regex = preg("(\w+\:\/\/)(?P<host>[\w.]+)");

var testString = /*
http://bbs.aardio.com
http://www.baidu.com
*/

console.log( "测试是否匹配", regex.test(testString) );
console.log( "查找匹配位置", regex.find(testString) );
console.log( "获取匹配字符串", regex.match(testString) );

//全局匹配
for scheme,host in regex.gmatch( testString  ) { 
    console.log("发现匹配字符串", scheme,host )
}

console.log( '字符串替换结果\r\n', regex.replace( testString,"ftp://\2" ) );

console.log( '函数替换结果\r\n', regex.replace( testString
        ,function(scheme,host){
                if( host == "bbs.aardio.com" )
                        return "ftp://" + host; 
        }  ) );

//数组匹配,找出所有网址并返回数组
var urls = regex.grep( {
        "http://bbs.aardio.com";
        "www.aardio.net";
        "http://www.baidu.com";
} );

console.varDump(urls)
regex.free(); 

$keywords = preg("/[\s,]/is").split ( "hypertext language,,programming");
console.varDump( $keywords );
console.pause();

注意:

aardio 所有字符串处理函数原生支持模式匹配,aardio 模式匹配是语法简化的正则表达式,基本语法类似正则表达式,但不支持对捕获分组使用模式运算等,添加了更多实用的功能(例如局部禁用模式匹配、元序列等功能),运行速度更快。模式匹配使用!实现边界断言,而正则表达式使用\b表示单词分界, aardio提供了更多的字符类,例如\a表示字母,\u表示大写字符,\l表示小写字符, \p表示标点符号,以及使用冒号表示中文宽字符等等。

aardio 模式匹配式匹配实现了正则表达式最常用的主要功能,但是运行速度却比 preg 正则表达式的速度快数十倍,比 string.regex 提供的 VBS 正则表达式快数百倍

参考:模式匹配快速入门

二. 创建正则表达式对象:

如下 aardio 代码用于创建 preg 正则表达式对象

import preg;  

var 正则表达式对象 = preg("正则表达式","模式修正符")

注意 aardio 并不推荐将正则表达式放在一对斜杠内 - 这种兼容 PHP 的语法意谓需要多分析一次正则, 所以 preg( "/正则表达式/模式修正符" ) 改为 preg("正则表达式","模式修正符") 更好一些。

\ 是正则表达式的转义符,因为 aardio 单引号内是转义字符串 -  所以你必须用 '\\' 表示 正则表达式中的 "\", 为了避免混淆一般将正则表达式或模式匹配写在双引号中,在 aardio 中双引号包含的是原始字符串。

三. 正则表达式中常用模式

/pattern/ 匹配结果
. 匹配除换行符以外的所有字符
x? 匹配 0 次或一次 x 字符串
x* 匹配 0 次或多次 x 字符串,但匹配可能的最少次数
x+ 匹配 1 次或多次 x 字符串,但匹配可能的最少次数
.* 匹配 0 次或一次的任何字符
.+ 匹配 1 次或多次的任何字符
{m} 匹配刚好是 m 个 的指定字符串
{m,n} 匹配在 m个 以上 n个 以下 的指定字符串
{m,} 匹配 m个 以上 的指定字符串
[] 匹配符合 [] 内的字符
[^] 匹配不符合 [] 内的字符
[0-9] 匹配所有数字字符
[a-z] 匹配所有小写字母字符
[^0-9] 匹配所有非数字字符
[^a-z] 匹配所有非小写字母字符
^ 匹配字符开头的字符
$ 匹配字符结尾的字符
\d 匹配一个数字的字符,和 [0-9] 语法一样
\d+ 匹配多个数字字符串,和 [0-9]+ 语法一样
\D 非数字,其他同 \d
\D+ 非数字,其他同 \d+
\w 英文字母或数字的字符串,和 [a-zA-Z0-9] 语法一样
\w+ [a-zA-Z0-9]+ 语法一样
\W 非英文字母或数字的字符串,和 [^a-zA-Z0-9] 语法一样
\W+ [^a-zA-Z0-9]+ 语法一样
\s 空格,和 [\n\t\r\f] 语法一样
\s+ [\n\t\r\f]+ 一样
\S 非空格,和 [^ \n\t\r\f] 语法一样
\S+ [^ \n\t\r\f]+ 语法一样
\b 匹配以英文字母,数字为边界的字符串
\B 匹配不以英文字母,数值为边界的字符串
a|b|c 匹配符合a字符 或是b字符 或是c字符 的字符串
abc 匹配含有 abc 的字符串
(pattern) () 这个符号会记住所找寻到的字符串,是一个很实用的语法。第一个 () 内所找到的字符串变成 $1 这个变量或是 \1 变量,第二个 () 内所找到的字符串变成 $2 这个变量或是 \2 变量,以此类推下去。
\pattern\i i 这个参数表示忽略英文大小写,也就是在匹配字符串的时候,不考虑英文的大小写问题。
\ 如果要在 pattern 模式中找寻一个特殊字符,如 *,则要在这个字符前加上 \ 符号,这样才会让特殊字符失效

四. 正则表达式示例

范例 说明
"/perl/" 找到含有 perl 的字符串
"/^perl/" 找到开头是 perl 的字符串
"/perl$/" 找到结尾是 perl 的字符串
"/c|g|i/" 找到含有 c 或 g 或 i 的字符串
"/cg{2,4}i/" 找到 c 后面跟着 2个到 4个 g ,再跟着 i 的字符串
"/cg{2,}i/" 找到 c 后面跟着 2个以上 g ,再跟着 i 的字符串
"/cg{2}i/" 找到 c 后面跟着 2个 g,再跟着 i 的字符串
"/cg*i/" 找到 c 后面跟着 0个或多个 g ,再跟着 i 的字符串,如同/cg{0,1}i/
"/cg+i/" 找到 c 后面跟着一个以上 g,再跟着 i 的字符串,如同/cg{1,}i/
"/cg?i/" 找到 c 后面跟着 0个或是 1个 g ,再跟着 i 的字符串,如同/cg{0,1}i/
"/c.i/" 找到 c 后面跟着一个任意字符,再跟着 i 的字符串
"/c..i/" 找到 c 后面跟着二个任意字符,再跟着 i 的字符串
"/[cgi]/" 找到符合有这三个字符任意一个的字符串
"/[^cgi]/" 找到没有这三个字符中任意一个的字符串
"/\d/" 找寻符合数字的字符,可以使用/\d+/来表示一个或是多个数字组成的字符串
"/\D/" 找寻符合不是数字的字符,可以使用/\D+/来表示一个或是更多个非数字组成的字符串
"/\*/" 找寻符合 * 这个字符,因为 * 在常规表达式中有它的特殊意思,所以要在这个特殊符号前加上\ 符号,这样才会让这个特殊字符失效
"/abc/i" 找寻符合 abc 的字符串而且不考虑这些字符串的大小写

五. 正则表达式高级用法

1. 贪婪/懒惰

所有限定匹配次数的正则量词运算符都是贪婪的。它们尽可能多地匹配更多更长的目标字符串。在正则运算符后添加 ? 能让表达式仅匹配尽可能短的长度。修改器U也能惰化量词运算符。

下面是一个简单演示:

import console;
import preg;

var str = 'hihihi oops hi';

//使用贪婪的{n,}操作符进行匹配 
var m = preg('/((hi){2,})/').match(str); //返回值将是 'hihihi'
console.log(m)

//操作符匹配,在量词运算符后添加问号表示惰化匹配
var m = preg('/((hi){2,}?)/').match(str);//返回值将是 'hihi'
console.log(m)
console.pause()

2. 捕获组引用(Back referencing)

捕获组引用是在正则表达式内部引用之前捕获到的内容的方法,也可以用于 preg().replace()函数的替换串中引用匹配分组,使用\0引用匹配的整个字符串, 使用 \1引用第一个匹配分组。

3. 命名捕获组(Named Groups)

使用(?P<name>pattern)格式的语法指定一个正则捕获组的名称,name 指定组名,pattern 则是正则模式。

请看下面的例子:

import console;
import preg;

var regex = preg("/(?P<quote>""|').*?(?P=quote)/");
var m = regex.exec( ' "测试字符串" ' );

//exec返回的对象,可以使用名字直接引用命名分组
console.log("使用的引号", m.quote )
console.pause()

上式中,quote 就是组名,|是改组匹配内容的正则。后面的(?P=quote)是在引用组名为 quote 的捕获组。

4. 单词边界(Word Boundaries)

\b 匹配单词边界,这是一个零宽匹配,不消耗字符长度

例如 /\bend\b/ 只匹配 "end" , 不会匹配 "friend" 或 "ending"。

\B 的作用则完全相反,匹配不是单词边界的位置。

注意:正则表达式又或者 aardio 模式匹配通常都会用大写表示反义。

5. 原子组(Atomic Groups)

原子组是一种特殊的正则表达式分组,不会捕获匹配结果。

原子组通常用于提高正则表达式的性能,避免不必要的回溯。可以使用 (?>pattern) 来定义原子组,其中 pattern 是要匹配的模式。

示例:

假设我们有一个字符串 "catastrophe" 并希望匹配 "cat"、"cater" 或 "catastrophe":

/\b(cat|cater|catastrophe)\b/

在这个例子中,正则引擎会首先尝试匹配 "cat"。如果匹配成功,但后续字符不符合整个模式,正则引擎会回溯并尝试匹配 "cater" 和 "catastrophe"。这种回溯会影响性能。

使用原子组可以避免这种情况:

/\b(?>cat|cater|catastrophe)\b/

在这个替代写法中,一旦 "cat" 匹配成功,正则引擎不会回溯去尝试 "cater" 或 "catastrophe",从而提高匹配效率。

6. 递归(Recursion)

递归(Recursion)在正则表达式中用于匹配嵌套结构,例如括号嵌套 (this (that)) 或 HTML 标签嵌套 <div><div></div></div>。我们使用 (?R) 来表示递归调用整个模式。

下面是一个匹配嵌套括号的例子:

\(((?>[^()]+)|(?R))*\)

这个表达式的解释如下: - \(\) 匹配括号的开头和结尾。 - (?>[^()]+) 匹配不包含括号的所有字符。 - (?R) 递归调用整个模式,匹配嵌套的括号。 - * 表示匹配前面的模式零次或多次。

这个表达式会尽可能多地匹配嵌套的括号。

另一个递归的例子是匹配嵌套的 HTML 标签:

<([\w]+)([^<]*?)>((?>[^<]+)|(?R))*<\/\1>

这个表达式的解释如下: - <([\w]+)([^<]*?)> 匹配开始标签,并捕获标签名。 - ((?>[^<]+)|(?R))* 匹配标签内容,要么是非标签字符,要么是递归调用整个模式匹配嵌套标签。 - <\/\1> 匹配结束标签,\1 引用之前捕获的标签名。

这个表达式综合运用了字符分组、贪婪操作符、回溯以及递归来匹配嵌套的 HTML 标签。

7. 回调(Callbacks)

在处理匹配结果时,有时需要对特定内容进行特别的修改。此时,正则表达式的回调函数就派上用场了。

回调函数用于 preg().replace() 中,实现对匹配结果的动态修改。你可以为 preg().replace() 指定一个函数作为参数,该函数接收匹配结果数组并返回修改后的数组,作为替换的结果。

例如,我们想将某字符串中的首字母转换为大写。

首先,使用正则表达式匹配所有需要大写的字母:

/\b\w/

该表达式使用了单词边界和字符类。接下来,我们需要一个回调函数:

function upperCase(firstLetter) { 
    return string.upper(firstLetter) 
}

函数 upperCase 接收匹配结果数组,并将匹配结果的首字母转换为大写。在这个例子中,firstLetter 代表需要大写的字母。然后,我们利用 preg().replace() 函数实现回调:

import console;
import preg;

function upperCase(c) {  
    return string.upper(c) 
}

var regex = preg("/\b\w/");
var str = regex.replace("very good", upperCase); 
console.log(str)
console.pause()

通过这个简单的回调函数,我们就能实现强大的字符串处理功能。

8. 注释 (Commenting)

注释在正则表达式中虽然不用于匹配字符串,但却是理解和维护复杂正则表达式的重要工具。随着正则表达式变得越来越复杂,理解其匹配逻辑会变得越来越困难。通过在正则表达式中添加注释,可以减少未来的困惑和误解。

要在正则表达式中添加注释,可以使用 (?#comment) 语法,其中 comment 是你的注释内容。例如:

/(?#匹配数字)\d/

如果你计划将代码公开,添加注释显得尤为重要,这样其他人可以更容易地理解和修改你的代码。注释同样有助于你在将来重新审视自己写的代码时快速理解其逻辑。

此外,可以使用 "x""(?x)" 修饰符来格式化正则表达式,使其更具可读性。这个修饰符会让正则引擎忽略表达式中的空格和换行符。需要匹配的空格仍然可以通过 \s\(反斜杠加空格)来表示。

以下是一个使用 "x" 修饰符的示例:

import preg;
import console;

var m = preg("/
\d+   # 匹配数字
\s+   # 匹配空格
\w+   # 匹配单词
/x").match("243523 test");

console.varDump(m);
console.pause();

上述 aardio 代码与以下代码的功能相同:

import preg;
import console;

var m = preg("\d+(?#匹配数字)\s+(?#匹配空格)\w+(?#匹配单词)").match("243523 test");
console.varDump(m);
console.pause();

请始终注意代码的可读性,确保注释清晰明了。

六. 修正符列表

下面列出了当前在 preg 支持库中可能使用的修正符。括号中是这些修正符的内部 PCRE 常量名。修正符中的空格和换行被忽略,其它字符会导致错误。

preg 扩展库在开启此选项( UTF-8 )以后,正则表达式即按字符计数,并可对中文字符使用匹配修饰符,示例如下:

import preg;
import console;

var regex,err = preg("/中+文/u");
$keywords,j = regex.match (  "abc中中中文" );
console.varDump(  $keywords )
console.pause(); 

启用了 UTF-8 编码以后,也可以使用 Unicode 匹配全部中文,如下:

import preg;
import console;

var regex,err = preg("/[\x{4e00}-\x{9fa5}]+/u");
$keywords,j = regex.match (  "abc中文" );
console.varDump(  $keywords )
console.pause(); 

如果在修正符中指定 "j",则可以使用 Javascript 的正则语法指定 Unicode 编码,如下:

import preg;
import console;

var regex,err = preg("/[\u4e00-\u9fa5]+/ju");  
$keywords,j = regex.match (  "abc中文" );
console.varDump(  $keywords )
console.pause();

如果是 UTF-8 编码的字符串, 则需要象下面这样匹配中文

import preg;
import console;

var regex,err = preg("/([\x80-\xFF]+)/");

Markdown 格式