apg 代码_正则表达式的替代方法:apg-exp

apg 代码

Sebastian SeitzAlmir Bijedic对此文章进行了同行评审。 感谢所有SitePoint的同行评审员使SitePoint内容达到最佳状态!

几乎没有程序员会时不时使用一种形式或另一种形式使用正则表达式 。 对于许多人来说,模式语法似乎是晦涩难懂的。 本教程将介绍一个新的模式匹配引擎apg-exp ,它是RegExp的功能丰富的替代方案,具有ABNF模式语法,在外观上更容易一些。

快速比较

您是否曾经需要验证电子邮件地址并遇到类似这样的问题?

^[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$

模式匹配引擎是完成此任务的正确工具。 这是一个设计良好,编写良好的正则表达式。 效果很好。 那不喜欢什么?

好吧,如果您是一位具有正则表达式的专家,那么什么都没有。 但是对于我们其他人来说,

  • 难以阅读
  • 更难写
  • 难以维护

正则表达式语法具有悠久的历史,并且已深深地集成到我们作为程序员每天使用的许多工具和语言中。

但是,有一种替代语法已经存在了很长时间,它在Internet技术规范的作者和用户中非常流行,具有正则表达式的所有功能,但在JavaScript编程领域很少使用。 即,在A ugmented ackus-ÑAUR˚FORM,或ABNF,由正式定义IETFRFC 5234RFC 7405

让我们看看在ABNF中相同的电子邮件地址可能是什么样。

email-address   = local "@" domain
local           = local-word *("." local-word)
domain          = 1*(sub-domain ".") top-domain
local-word      = 1*local-char
sub-domain      = 1*sub-domain-char
top-domain      = 2*6top-domain-char
local-char      = alpha / num / special
sub-domain-char = alpha / num / "-"
top-domain-char = alpha
alpha           = %d65-90 / %d97-122
num             = %d48-57
special         = %d33 / %d35 / %d36-39 / %d42-43 / %d45 / %d47 
                / %d61 / %d63 / %d94-96 / %d123-126

当然,它并不那么紧凑,但是它像HTML和XML一样,被人类和机器读取。 我猜想,除了对通配符搜索模式的了解以外,您还可以用“普通英语”阅读这里发生的事情。

  • 电子邮件地址定义为本地部分和以@分隔的域
  • 本地部分是一个单词,后跟可选的以点分隔的单词
  • 域是一个或多个点分隔的子域,后跟一个顶部域
  • 您在这里可能不知道,但可能可以猜测的唯一的事情是:
    • 就像通配符*表示“零个或多个”, 1*表示“一个或多个”和2*6表示最少2次重复和最多6次重复
    • /分隔其他选择
    • %d定义十进制字符代码和字符代码范围
    • 例如, %d35代表# ,ASCII十进制35
    • %d65-90代表AZ范围内的任何字符,ASCII小数点65-90

示例1中将RegExp和apg-exp与此电子邮件地址进行比较。

apg-exp是一种模式匹配引擎,旨在具有RegExp的外观,但使用ABNF语法进行模式定义。 在接下来的几节中,我将指导您完成:

  • 如何让apg-exp进入您的应用
  • ABNF语法的简要指南
  • 使用apg-exp-一些示例
  • 接下来要去哪里—更多详细信息,高级示例

启动并运行-如何获取它

npm

如果您在Node.js环境中工作,请从您的项目目录运行:

npm install apg-exp --save

然后,您可以使用require()在代码中访问它。

例如:

var ApgExp = require("apg-exp");
var exp = new ApgExp(pattern, flags);
var result = exp.exec(stringToMatch);

的GitHub

要从GitHub获取代码的副本,您可以将存储库克隆到您的项目目录中:

git clone https://github.com/ldthomas/apg-js2-exp.git apg-exp

将其下载为zip文件

然后在page.html

<!-- optional stylesheet used in tutorial examples -->
<link rel="stylesheet" href="./apg-exp/apgexp.css">
<script src="./apg-exp/apgexp-min.js"></script>

<script>
  var useApgExp = function(){
      var exp = new ApgExp(pattern, flags); 
      var result = exp.exec(stringToMatch);
      /* do something with the result */
  }
</script>

CDN

您还可以使用RawGit直接从GitHub源创建CDN版本。 但是,请务必阅读无正常运行时间或支持保证 (实际上,请务必阅读完整的FAQ )。

本教程的所有示例均使用以下内容。

<link rel="stylesheet"
 href="https://cdn.rawgit.com/ldthomas/apg-js2-exp/89c6681798ba9e47583b685c87b244406b18a26d/apgexp.css">
<script
 src="https://cdn.rawgit.com/ldthomas/apg-js2-exp/c0fc3adac954a6f6ad6f265fd2f8f06f68001e10/apgexp-min.js"
 charset="utf-8"></script>

免费学习PHP!

全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。

原价$ 11.95 您的完全免费

这些文件被缓存在MaxCDN服务器上,只要它们仍然可用,您就可以随意使用它们进行测试。 但是,对于生产而言,应将apgexp-min.jsapgexp.css副本apgexp.css在您自己的服务器上,以确保访问
并将它们包含在您的页面中,使其最适合您的应用程序。

ABNF简要指南

ABNF是描述短语的语法,短语可以是任何字符串。 如您在上面的电子邮件示例中所看到的,它使您可以将复杂的短语分解为更简单的短语的集合。 短语定义的形式为:

name = elements LF

其中LF是换行符(换行符\n )。

下表是这些元素的简要指南(完整指南请参见SABNF )。

元件 定义 评论/范例
名称 规则名称 字母+连字符( 请参阅下面的注释1
%d32 单个字符代码 字符代码的十进制值
%d97.98.99 字符串字符串 abc
%d48-57 字符码范围 任意ASCII数字0-9
“ abc” 不区分大小写的文字字符串 abc或ABC等
%s” aBc” 区分大小写的文字字符串 仅aBc(=%d97.66.99)
空间 连接两个元素 %d97%d98(= ab)
/ 分隔两个备用元素 %d97 /%d98(= a或b)
*元件 元素的零个或多个重复 见下面的注2
(元素) 分组,视为单个元素 请参阅下面的注释3
[元素] 可选分组 [%d97]%d98(ab或b)
%^ 输入字符串的开头 仅将位置与空短语匹配
%$ 输入字符串的结尾 仅将位置与空短语匹配
&元件 展望元素 元素必须跟随当前字符串的位置
!元件 展望未来 元素不得跟随当前字符串位置
&&元件 向后寻找元素 元素必须在当前字符串位置之前
!!元件 一无所有 元素不能在当前字符串位置之前
\名称 向后引用规则“名称” 匹配找到的“名称”的上一个短语
;评论 评论 评论来自; 到行尾

注意1 :规则名称必须在该行的第一列中以字母字符开头。 续行必须以空格或制表符开头。 第一条规则定义了要匹配的整个短语或模式。 以下规则定义了整个短语中的命名子短语(或命名捕获)。

注2 :重复的一般形式是n*m ,定义了最少n重复和最大m重复。 简写符号可以是*m表示零到mn*表示n到无穷大,也可以只是n表示n*n

注意3 :分组对于保持交替和串联行为符合预期非常重要。 串联比交替具有更严格的绑定。 如果

phrase1 = elem (foo / bar) blat LF
phrase2 = (elem foo) / (bar blat) LF
phrase3 = elem foo / bar blat LF

然后, phrase1elem foo blatelem bar blat匹配,而phrase2phrase3elem foobar blat匹配。 小心并自由使用分组。

使用apg- exp-几个例子

现在,您的应用程序中已包含apg-exp ,并且了解编写模式语法的基础知识,让我们直接进入有趣的部分,并查看一些如何使用它的示例。

在下面的下一节中,我将构造对象的无聊细节放入。 您可以根据需要参考它以理解示例。 我还将跳过错误处理,但您应该意识到,如果存在模式错误,构造函数将抛出ApgExpError异常对象,该对象具有几个方便的函数,用于格式化显示模式错误。 您的try/catch块可能看起来像这样:

try {
  var exp = new ApgExp(pattern, flags);
  var result = exp.exec(stringToMatch);
  if (result) {
    // do something with results
  } else {
    // handle failure
  }
} catch(e) {
  if (e.name === "ApgExpError") {
    // display pattern errors to console
    console.log(e.toText());
    // display pattern errors to HTML page
    $("#errors").html(e.toHtml());
  } else {
    // handle other exceptions
  }
}

电话号码

电话号码是一个常见的表单验证问题,也是介绍一些基本知识的好机会。 通常,您只想知道以类似于电话号码的形式输入了10位数字,而不必担心北美计划国际号码的详细信息。

我们只要求它以圆括号(或一个数字)开头,并包含3、3和4个数字的块,由0到3个非数字分隔。这足以接受最常见的格式,并且足以捕获该区域,办公室和订户代码作为任何重新格式化的捕获组。

这是ABNF中的样子:

phone-number = ["("] area-code sep office-code sep subscriber
area-code    = 3digit                       ; 3 digits
office-code  = 3digit                       ; 3 digits
subscriber   = 4digit                       ; 4 digits
sep          = *3(%d32-47 / %d58-126 / %d9) ; 0-3 ASCII non-digits
digit        = %d48-57                      ; 0-9

示例2演示了这一点,并为您提供了更改电话号码格式的机会:

示例3让我们使用模式语法,看看您可能希望看到的错误消息:

RegExp语法,

\(?(\d{3})\D{0,3}(\d{3})\D{0,3}(\d{4})

也不难, 示例4进行了并排比较。

日期

现在让我们稍微增加一下,看看匹配日期时apg-exp和RegExp的比较。

我们的日期格式要求是:

mm/dd/yy or mm/dd/yyyy
dd/mm/yy or dd/mm/yyyy
mm, 1-12 or 01-12, i.e. with or without leading zero
dd, 1-31 or 01-31, i.e. with or without leading zero
yy, 00-99
yyyy, 1900-1999 or 2000-2099

mm/dd/yyyy格式本身并不那么困难,但是限制数字范围才是提高格式的原因。 使用ABNF到达那里看起来像这样:

date     = %^ (mm-first / dd-first) %$
mm-first = mm "/" dd "/" yyyy   ; month before day
dd-first = dd "/" mm "/" yyyy   ; day before month
dd       = "0" digit1           ;    01-09
         / ("1"/"2") digit      ; or 10-29
         / "3" %d48-49          ; or 30-31
         / digit1               ; or 1-9
mm       = "0" digit1           ;    01-09
         / "1" %d48-50          ; or 10-12
         / digit1               ; or 1-9
yyyy     = ("19" / "20") 2digit ; 1900-1999 or 2000-2099
         / 2digit               ; or 00-99
digit    = %d48-57              ; 0-9
digit1   = %d49-57              ; 1-9

评论使它很不言自明。 请注意, ddmmyyyy具有最短的替代项。 这一点非常重要,因为apg-exp总是采用“首场比赛获胜”的方法来替代。 从左到右尝试替代项,一旦找到匹配项,将忽略所有剩余的替代项。 在这里,这意味着当一个模式可以匹配一位或两位数字时,两位数模式必须是第一位。

按照与上述完全相同的方法,将日期分为ddmm[yy]yy备用模式,然后将它们组合为完整日期,将产生以下RegExp语法:

^(?:((?:0[1-9])|(?:1[0-2])|(?:[1-9]))/((?:0[1-9])|(?:(?:1|2)[0-9])|(?:3[0-1])|(?:[1-9]))|((?:0[1-9])|(?:(?:1|2)[0-9])|(?:3[0-1])|(?:[1-9]))/((?:0[1-9])|(?:1[0-2])|(?:[1-9])))/((?:19|20)?[0-9][0-9])$

我不是RegExp专家,因此可能有一些方法可以缩短此时间,但是此方法可以工作。 您可以在示例5中进行比较。 跳到那边做一下。

使用递归匹配嵌套对(())

最后,我想展示如何匹配括号对,括号等的嵌套对。 尽管这是一个非常重要的模式匹配问题,但是RegExp无法做到。 对于匹配的括号对,请考虑以下ABNF:

P = L P R / L R
L = "("
R = ")"

请注意,规则P出现在其自己的定义中。 这称为递归。 尽管某些类型的正则表达式引擎确实支持递归,而某些RegExp工具确实提供了递归功能,但是JavaScript的RegExp根本不支持递归。 上面的LR已选择匹配括号,但只要LR不能匹配相同,它们就可以是任何东西。

转到示例6 ,我们将在递归中获得一些乐趣。

在离开匹配的嵌套对这一主题之前,我想演示一些实际的示例, apg-exp可以帮助您。

示例6中,您看到了如何在对中匹配对并在方括号之间包含文本。 假设您的任务是编写一个程序,使得:如果光标位于大括号{ ,突出显示匹配的大括号}

示例7向您显示了此问题的解决方案。 您将需要了解sticky模式。

最后一个嵌套对示例。 您是否曾经想过注释掉一个很大HTML块,只是发现该块中已经有注释了? 令人沮丧吗? 只需在Internet上搜索“ HTML嵌套注释问题”,便会感到沮丧。 示例8为您显示了解决此问题的一种可能的方法。 警告 -这可能会使您有些紧张。 您将需要了解result.rules对象和global模式。

汇集全部

总结一下,让我们看看完整的表单验证示例的外观。

通常,在创建新帐户时,会要求您输入用户名,电子邮件地址,密码和密码确认。 我们要求用户名是3-32个ASCII字母,连字符和句点。 密码必须为8-16个大写字母,小写字母或数字,并且每个密码中至少有一个。

在继续之前,该表格将在任何无效条目上方显示一个描述性错误消息。 示例9将所有内容组合在一起。

见钢笔APG-EXP:一个正则表达式的替代:实施例9通过SitePoint( @SitePoint )上CodePen

库API

我试图在本节中提供足够的信息来理解上面的示例。 但是,有许多高级功能需要对解析理论进行更深入的了解才能使用,而我刚刚对那些功能进行了“高级”注释。

输入参数

apg-exp构造函数ApgExp最多包含四个参数。

var exp = new ApgExp(pattern[, flags[, nodeHits[, treeDepth]]]);
  • 模式
    • 字符串:如上一节所述的SABNF模式语法*。
    • 对象:实例化的APG解析器对象。 (高级选项。)
  • 标志:字符串:任何字符"gyud"
    • g –全局模式:迭代所有模式匹配(请参见示例8
    • y –粘性模式:将匹配项固定在exp.lastIndex (请参见示例7
    • u – Unicode模式:以整数字符代码而不是字符串的数组形式返回结果
    • d –调试模式:高级选项–公开APG跟踪对象
  • nodeHits:整数> 0:默认= Infinity :将匹配算法限制为"nodeHits"步骤。 防止灾难性的回溯。
  • treeDepth:整数> 0:默认值= Infinity :限制匹配的解析器的树深度。

* 例如,要匹配字母数字名称,可以使用输入字符串:

var pattern = "alphanum = alpha *(alpha / num)\n"
            + "alpha = %d65-90 / %d97-122\n"
            + "num = %d48-57\n";

属性和方法

构造的对象exp本身具有16个属性和14个方法。

属性 类型 描述
AST 目的 (高级: APG抽象语法树对象)
调试 布尔值 如果设置了调试标志d ,则为true;否则为false
标志 重新格式化的flags参数副本
全球 布尔值 如果设置了全局标志g ,则返回true,否则返回false
输入 字符串/数组(*) 输入字符串的副本
lastIndex 整数 在(**)开始尝试匹配的字符索引
leftContext 字符串/数组(*) 匹配模式的前缀
lastMatch 字符串/数组(*) 匹配的模式(= result[0]
nodeHits 整数 输入值(有关实际值,请参见result.nodeHits
rightContext 字符串/数组(*) 匹配模式的后缀
规则 目的 参见result.rules 。 在此仅保留最后一个匹配的短语。
资源 输入的SABNF模式语法字符串
布尔值 如果设置了粘性标志y ,则返回true,否则返回false
跟踪 目的 (高级:请参阅APG跟踪对象。)如果调试标志为false,则为undefined
treeDepth 整数 输入值(实际值请参见result.treeDepth
统一码 布尔值 如果设置了Unicode标志u ,则返回true,否则返回false

注1 :如果unicode标志为true则它将是整数字符代码的数组,否则为字符串。

注2 :用户可以自由将其设置为任何值。 模式匹配始终从该索引值开始。 尝试匹配后,它的使用和值受全局和粘性模式影响。 参见示例78

方法 争论 描述
defineUdt(name,func) 字符串,函数 高级:用于SABNF的UDT功能。
排除([字符串]) 规则名称数组 排除在result.rules对象中的规则名称列表
exec(str) 尝试在str中进行模式匹配
包括([字符串]) 规则名称数组 仅包含在result.rules对象中的规则名称列表
maxCallStackDepth() 没有 返回调用堆栈深度的上限
替换(str,repl) 字符串,字符串或函数 匹配str中的patten,替换为repl
sourceToHtml() 没有 返回HTML格式的输入模式
sourceToHtmlPage() 没有 将输入模式作为完整HTML页面返回
sourceToText() 没有 返回ASCII文本格式的输入模式
split(str [,limit]) 字符串,整数 在模式匹配处分割输入字符串
测试(str) 如果找到模式匹配项,则返回true ,否则返回false (请参见示例9。
toHtml() 没有 以HTML格式返回此对象的属性
toHtmlPage() 没有 以完整HTML页面形式返回此对象的属性
到文本() 没有 以ASCII文本格式返回此对象的属性

结果对象

模式匹配成功后,将在具有7个属性和3个方法的result对象中返回结果:

var result = exp.exec(str);
属性 类型 描述
[0] 字符串/数组(*) 匹配的模式
输入 字符串/数组(*) 输入字符串的副本
指数 整数 匹配模式的第一个字符的索引
长度 整数 匹配模式中的字符数
nodeHits 整数 匹配所需的实际解析步骤数
规则 目的 (命名捕获)每个命名规则的所有匹配短语的数组(***)
treeDepth 整数 比赛期间达到的实际最大解析树深度

注释3 :例如, result.rules["name"][i] = {phrase: string/array, index: integer}参见示例8

方法 争论 描述
toHtml() 没有 返回HTML格式的属性
toHtmlPage() 没有 返回属性作为完整HTML页面
到文本() 没有 返回ASCII文本格式的属性

下一步去哪里

我试图将其最小化,只是让您了解一下apg-exp库和ABNF的外观以及它们与RegExp的堆叠方式。 但是,您还有很多事情要做。 如果您想提高模式匹配技能,或者只是想冒险,请查看这些更高级的示例并参考完整的用户指南

我是否已说服您在下一个项目中尝试apg-exp? 您认为ABNF比正则表达式更容易使用吗? 我希望在下面的评论中收到您的来信!

翻译自: https://www.sitepoint.com/alternative-to-regular-expressions/

apg 代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值