JavaScript-正则表达式入门指南-全-

JavaScript 正则表达式入门指南(全)

原文:Introducing Regular Expressions

协议:CC BY-NC-SA 4.0

一、正则表达式简介

为了开始介绍正则表达式,我将从一个例子开始。这是一个你已经经历了几百次的问题。当您在线输入客户数据时,许多 web 表单会要求您提供电子邮件地址。为了避免输入错误的地址,立即验证是有意义的。一种方法是将字符串分成几部分(在@字符之前和之后),分析点的位置和最后一个点(即顶级域名)之后的字符数。

几个如果和循环之后,你就差不多完成了。或者你简单地使用一个正则表达式:

ˆ[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)* @ [a-zA-Z0-9-]+\.([azA-Z]{2,3})$

你明白了吗?如果这是你第一次使用正则表达式,可能很难读懂。

正则表达式是正则文本中模式识别的一种形式。正则表达式将模式与文本进行比较。在脚本或编程语言中封装为对象的整个表达式将返回truefalse。结果显然会告诉调用者比较是否成功。因此,如果你把它放在实际语言的语境中看,就能更好地理解这个表达。因为本书专门介绍 JavaScript,所以该语言的用法如下:

 1   var email = "joerg@krause.net";
 2   
 3   console.log(check(email));
 4   
 5   function check(email) {
 6     if (email.match(/^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*@[a-zA-Z0-9-]+\\
 7   .([a-zA-Z]{2,3})$/)) {
 8        return true;
 9     } else {
10        return false;
11     }
12   }

这里,表达式是通过使用典型的文字作为边界/expression/来实现的,比较是通过每个字符串对象提供的match函数来实现的。请注意,斜线不能放在引号中。它是创建对象的文字。

A434767_1_En_1_Figa_HTML.jpg A Test Environment

对于第一步,使用在线提供的 JavaScript 测试控制台很有帮助。我推荐用 Repl。it1

A434767_1_En_1_Fig1_HTML.jpg

图 1-1。

The example in Repl.it A434767_1_En_1_Figb_HTML.jpg REPL

术语 REPL 是读取-评估-打印-循环的缩写。这是一种与脚本语言交互工作的方法。在维基百科上阅读更多相关信息。 2

抄袭还是脚手架?

我将在第四章展示许多有用的表达。然而,第一章展示了可以使用的普通和非普通表达式。因为表达式有时很难理解,你可以下载所有的例子。

A434767_1_En_1_Figc_HTML.jpg Website

访问本书的支持网站。??

如果您想成为一名专业的 JavaScript 开发人员,正则表达式是您工具集的一部分。你应该试着完全理解这些表达,并开始创造你自己的表达。

它是如何工作的?

您可能很想知道前面的表达式是如何工作的。

如果你开始分析这样的表达式,最好从特殊字符的提取开始。在这个特定的表达式中,它们包括下列之一:ˆ, $, +, *, ?, [], ()。所有其他字符在这里没有特殊的含义。常规字符是少数。通常,这种模式使用占位符和描述性字符多于单词中的实际字母。

以下是特殊字符的概述:

  • 让识别从头开始。如果您写ˆx,只有当字母“x”出现在第一个字符时,表达式才会匹配它。
  • $让您定义图案的结束位置。
  • *是一个占位符,表示没有或有任意数量的字符。
  • +是一个占位符,表示一个或任意数量的字符。
  • ?是一个占位符,表示没有字符或只有一个字符。
  • [a-z]定义一组字母或数字中的一个字符。您可以使用大写字母、小写字母或数字,只需将它们放在括号中,或者您可以将它们定义为一个范围,如示例所示。
  • ()分组字符或字符串。您也可以在这样的组中使用集合操作符*、+和?
  • {}是定义大括号前字符的重复标记。可以重复多次。范围可以用数字来定义;如果开头和结尾分别给出,数字用逗号({3,7})书写。
  • \(反斜杠)屏蔽元字符和特殊字符,使它们不再具有特殊含义。
  • .恰好代表一个字符。如果你真的需要一个点,就写\.

现在你可以很容易地将表达式拆分得很好。@字符代表其自身,第一步拆分表达式:

1   ^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*
2   @
3   [a-zA-Z0-9-]+\.([a-zA-Z]{2,3})$

@字符之前的部分必须至少有一个字符。这是由第一个字符定义[_a-zA-Z0-9-]强制的,所有可接受的字符都带有+符号。然后,表达式后面可能会跟一个实际的点\.,它本身后面可以跟一个或多个字符。整个“点加更多字符”组是可选的,可以无休止地重复(*)。第二部分类似。集合定义缺少下划线字符,该字符可能不会出现在常规域名中。点不是可选的(没有设置操作符),其余部分可以是两个或三个字符长(这是忽略了四个或更多字符的新域名,但我想你明白了)。

解析表达式

这个表达式可能会出现在这里,但它还远远不够完美。

在少数情况下,表达式可能拒绝可接受的电子邮件地址,也接受不规则的电子邮件地址。一个表情还是很难解释。因为您很快会遇到更复杂的例子,所以我将在这里使用另一种布局:

 1   ^                           // Start at the beginning

 2      [_a-zA-Z0-9-]            // Define a group

 3      +                        // One or more times

 4        (                      // Group 1

 5            \.                 // a "real" dot

 6            [_a-zA-Z0-9-]      // Another group

 7            +                  // One or more times

 8        )                      // /* End group 1 */

 9        *                      // Group optional or multiple times

10     @                         // The @ character

11     [a-zA-Z0-9-]              // Character definition

12     +                         // On or multiple times

13     \.                        // A "real" dot

14       (                       // Group 2

15           [a-zA-Z]            // A character definition

16           {2,3}               // Two or three characters

17       )                       // /* End group 2 */

18   $                           // End of text must come now

那就简单多了,不是吗?不幸的是,你不能用 JavaScript 那样写。我将只使用这种形式来分解棘手的表达式。如果你对这本书里的一个表达感到困惑,试着通过把每个字符放在一行并写下它的意思来解决这个模式。

Footnotes 1

repl.it/languages/JavaScript

2

en。维基百科。org/wiki/read %和 2% 80% 93 eval %和 2%80%93print_ loop

3

www . joergkrause . de/?p = 219

二、识别模式

在这一章中,我将用简单的例子来展示基本的技术。这对于日常使用来说可能不够,但是对于你自己的实验来说是一个很好的基础。

基础

如果一个表达式被求值,会发生什么?实际上,我们的目标总是在另一个更大的文本中搜索一个文本部分。搜索的文本可能来自公式、文件、数据库,也可能只是一个字符串。但这不是重点。你可以使用一个简单的搜索功能,通常,这样一个简单的搜索更有效。正则表达式定义搜索文本中的属性。这样,你就不需要一次又一次地改变搜索词,而是给出一个变化范围。搜索和替换可能会达到一个全新的水平。

A434767_1_En_2_Figa_HTML.jpg Pattern

正则表达式通过使用模式来识别包含文本的字符串。

搜索时,表达式用于解析字符串之间的比较。根据结果,比较分为truefalse。为了获得搜索模式的部分以供进一步检查,这种模式识别任务可以返回已经找到的部分的组。表达式中还有重复使用的组,以创建更复杂的属性。稍后,我将展示如何使用它。现在,我将展示几个例子来解释模式中属性的创建。

字符、行和文本

我已经使用了字符和文本等术语。在正则表达式的上下文中,术语行也很重要。行以换行符结束(在文本处理程序中,这是你按回车键的地方)。许多终止字符使用换行符。文件通常是逐行读取的。这就是正则表达式显式处理行的原因。您需要这些知识来搜索行尾以外的内容。

A434767_1_En_2_Figb_HTML.jpg Line Breaks

如果文本不包含任何换行符,则不需要元字符作为行开始和行结束。他们什么都不做。

正则表达式术语

这里我来解释一下比较特殊的术语。

元字符

在正则表达式中,可以用元字符定义特殊属性。其中一些你已经知道了——特别是搜索文本中开始位置的ˆ和结束位置的$

ˆ$称为元字符。如果您想将元字符作为常规字符来查找,您必须在它前面加一个反斜杠:

\$, \ˆ

反斜杠破坏了特殊含义。因此,反斜杠本身也是元字符。如果你特别寻找反斜杠,你写\\

下面是对所有元字符的更全面的解释。

文字

任何编程语言都有几种文字来表达除关键字以外的东西,如数字、运算符或文本。JavaScript 在这里也不例外。JavaScript 中的正则表达式使用/(正斜杠)作为文字字符。

/[abc]/

表达式可以赋给变量,也可以直接用作参数。

1   var patt = /abc/;
2   var s = /abc/.toString();
3   console.log(s);

字符类别

如果您要查找字符串或单个字符,使用字符类通常非常有效。字符类写在方括号里,比如[abc]。整个表达式代表该类中的一(1)个字符。在这个例子中,它或者是a,或者是b,或者是c。既不是ab也不是bc。您必须使用重复运算符来告诉表达式引擎从这样的类中寻找字符的更多外观。

你可以一个接一个地写出你要找的任何字符,或者用破折号来分组。表达式[a-c]也会碰到字母b

参考

成功找到的文本部分的一部分存储在临时存储单元中。您可以稍后在同一个表达式中引用它们。引用使用一个反斜杠,后跟存储位置。比如看起来像\4

元字符概述

下面是 JavaScript 正则表达式中可用的元字符的完整列表。

起点、终点和边界

您已经从简介章节中了解到的最重要的几个:

  • ˆ是开始(或开始)
  • $是结束了
  • \b定义一个单词的边界
  • \B没有定义边界

这是什么意思?如果你找“auto”这个词,“a”一定是开头,而“o”是结尾。要精确地搜索这个字符串(前后都没有),您的表达式是ˆauto$。如果您要查找的单词在文本中的任何地方,表达式就是简单的auto

1   var patt = /^auto$/;
2   console.log(patt.test("auto"));
3   console.log(patt.test("automatic"));
4   
5   var patt2 = /auto/;
6   console.log(patt.test("That's our automobile, it's an BMW."));

输出显示结果,如图 2-1 所示。

A434767_1_En_2_Fig1_HTML.jpg

图 2-1。

Output of the script

结尾大多是一行的结尾。如果设置了一些额外的开关,情况可能不是这样。开关在表达式之外,控制正则表达式引擎。你可以在第三章找到更多关于他们的信息。

单词边界是从单词到周围部分的过渡。拉丁语文本中的单词以空格或标点符号结尾,如逗号、分号或句号。特殊符号\b寻找这样的过渡。它并不完全代表一个字符;更多的是介于两者之间。这就是我在导言中对财产一词的意思。正则表达式描述了搜索文本的属性,而不是实际的搜索词。

表达式/\bco在句子“这很复杂”中识别“co”“co…”前面的空格构成了单词 boundary。该表达式在“肿瘤学很难”这句话中找不到任何内容。—即使“co”也在文本中。

任何字符

通常你会在一个特定的位置寻找一个角色,但是哪个角色并不重要。在类中处理所有的字符是很痛苦的。这就是为什么有另一个元字符:

  • .是任何字符,确切地说

现在,您可以在不同的上下文中查找“auto ”:

  • .uto查找“自动”、“自动”、“自动”等,也查找“分配器”。后者可能是不期望的。
1   var patt = /.uto/;
2   console.log(patt.test("auto"));
3   console.log(patt.test("automatic"));
4   console.log(patt.test("distributor"));

输出显示结果,如图 2-2 所示。

A434767_1_En_2_Fig2_HTML.jpg

图 2-2。

Output of the script

没有字符

如果你不寻找任何东西,正则表达式似乎没有用。但事实如此。想象你在一个文件中寻找空行。这就是只使用结束字符的结果:

ˆ$

是的,就是这么简单,因为表达式定义了唯一的字符是行的开始和结束。因此,该行必须为空。这就是你要找的。空在这里的意思是“中间没有东西”

单个ˆ作为搜索模式没有任何意义。它只是宣告了必须有一个开始。这是任何线都具有的属性。任何文本都将匹配此模式。

字符类别

字符类定义了类组。

众多中的一个

字符类写在方括号中。如果没有其他运算符,它始终只是模式中的一个位置。组由一个破折号组成,多个组简单地写在一起。该类中的任何字符都将被“按原样”处理,并失去其作为元字符的特殊意义。

  • [aeiou]定义英语中的元音
  • [a-f]定义字母 a、b、c、d、e、f(仅小写)
  • [a-fA-F0-9]定义十六进制数的所有数字

顺序只在群体中重要。简单来说,表达式[a-fA-F][A-Fa-f]是相同的。

1   var patt = /[a-f]+/;
2   console.log(patt.test("Auto"));
3   console.log(patt.test("42"));
4   console.log(patt.test("12 Days"));
5   console.log(patt.test("borrow"));

输出显示结果,如图 2-3 所示。

A434767_1_En_2_Fig3_HTML.jpg

图 2-3。

Output of the script

否定

通过使用元字符ˆ,一个完整的字符类可以被全部字符否定。它与使用相同符号的起始行元字符有不同的含义,也没有共同之处。这只是一个不同的背景。一些字符根据上下文有不同的含义。这就是为什么小心地分离正则表达式的各个部分是至关重要的。你必须了解人物的背景。

  • [ˆ0-9]包括除数字以外的所有内容(包括诸如#*%之类的字符)。
  • [ˆaeiou]定义了所有辅音,还有更多的,比如数字。

ˆ的特殊含义只有在它是左括号后的第一个字符时才起作用。如果它在类中的其他地方,它只是它自己的一个普通字符:

  • [!"@§$%ˆ&/()=]定义键盘数字键上的所有字符在 Shift 级别。

数字

寻找数字是一项常见的任务。数字由数字组成,使用前导符号、小数点和千位标记。以下表达式可能会有所帮助:

  • [0-7]是八进制数的数字(基数为 8)
  • [0-9+-.] are带符号和小数点的十进制数字
  • [a-fA-F0-9]是十六进制数字

日期和时间

以下表达式显示了如何识别文本中的日期和时间片段:

  • [0-9/]日期(美国格式,如 2016 年 4 月 22 日)。
  • [0-9:]时间,如 7:44
  • [0-9:amp]带有“上午”或“下午”的时间

最后一个例子往往过于简单,因为它允许“a9”或“p0m”。

其他的很弱,因为时间 26:99 也会被找到。

1   var patt = /[0-9:amp]/;
2   console.log(patt.test("12am"));
3   console.log(patt.test("4:17"));

输出显示结果,如图 2-4 所示。

A434767_1_En_2_Fig4_HTML.jpg

图 2-4。

Output of the script A434767_1_En_2_Figc_HTML.jpg Limitations

这些例子表明简单的正则表达式有明显的局限性。虽然可以编写与复数值范围完全匹配的表达式,但工作量可能会很大。你可以在附录中找到更好的例子。

用线串

通过使用字符类,您可以轻松区分小写字母和大写字母:

  • [gG]reen, [rR]ed

如果一个名字不能以独特的方式书写,你可以使用类:

  • M[ae][iy]er匹配“迈耶”、“迈耶”、“迈尔”、“迈尔”。

缩写

元字符通常形成缩写,如表 2-1 中所列和所述。

表 2-1。

Metacharacter Abbreviations

| 缩写 | 描述 | | --- | --- | | `\t` | 制表机 | | `\n` | 换行 | | `\r` | 回车(回车) | | `\f` | 换页 | | `\v` | 垂直制表机 | | `\s` | 空白(打印时不可见,包括`\t`、空格、`\n`、`\r`、`\f`) | | `S` | \s 的否定 | | `\w` | 单词字符(组成单词的字母,例如在`[_a-zA-Z0-9]`中) | | `W` | `\w`的否定 | | `\d` | 数字,如`[0-9]` | | `D` | `\d`的否定 | | `\b` | 单词的边界、开头或结尾;都不在\w 定义中。 | | `B` | `\b`的否定 | | `\0` | Nul ( `nil`)字符(物理 0) | | `\xxx` | 字符值,写成八进制数 | | `\xdd` | 字符值,写成十六进制数 | | `\uxxxx` | Unicode 字符,写成十六进制数 | | `\cxxx` | 控制,ASCII 值 |

重复运算符

到目前为止显示的所有元字符都有一个共同点:它们只处理一个字符。如果需要多次出现,可以使用重复运算符。介绍中提到了一些特色菜。这里我会给出更完整的描述。但是让我们从一些简单的例子开始:

  • a*定义"a"不出现或出现,如"a""aaaa"等。
  • a+定义一个或任意数量的"a",如"a""aa"等。
  • a?定义无或有一个"a",如""或“a”确切。

常见运算符

通用运算符使用一定范围内的多个字符。以下表达式使用通用运算符:

  • {min, max}声明必需的min字符和允许的max字符
  • {wert}确切的数字
  • {,max}最小值可以省略(零到max)
  • {min,}最大值也可以省略;min任意数字
  • 两个范围都可以省略(关于这个结构的更多信息,见下一段)

当然,你总是可以用这个表达式来代替简写的*、+和?。详细格式如下所示:

  • {,}表示*
  • {0,1}表示?
  • {1,}表示+

这些运算符非常适用于长度定义明确的文本,例如邮政编码:

1   <form>
2     <input type="text" name="zip" value="">
3     <input type="submit">
4   </form>

以下脚本检查用户输入的邮政编码是否有意义:

 1   <script>
 2   var re = /^[0-9]{5}$/;
 3   var field = "12683";
 4   var checkzip = re.exec(feld);
 5   if(!checkzip) {
 6     alert("The zip code " + checkplz + " is not correct.");
 7   } else {
 8       console

.log(checkplz)
 9   }
10   </script>

输出显示结果,如图 2-5 所示。

A434767_1_En_2_Fig5_HTML.jpg

图 2-5。

Output of the script

摘要

表 2-2。

Repetition Operators

| 操作员 | 意义 | 描述 | | --- | --- | --- | | `?` | 0 – 1 | 没有或一个 | | `*` | 0 – ∞ | 没有或任何 | | `+` | 1 – ∞ | 一个或任何 | | `{num}` | 数字 | 准确数字 | | `{min,}` | 最小 – ∞ | 最小最小值 | | `{,max}` | 0–最大值 | 无或最大值为 max | | `{min, max}` | 最小最大值 | 最小最小值和最大值中的最大值 |

特殊操作员

“或”运算符是一个特殊的运算符,写作|。例如,/green|red/匹配“绿色苹果”中的“绿色”和“红色苹果”中的“红色”,但不匹配“红色或绿色苹果”。

参考

到目前为止显示的元素看起来很简单,对吗?元字符和类的更复杂的组合还不够灵活。(与传统编程语言相比)真正缺少的是循环。特别是在研究字符串时,连续的逐字符搜索非常有用。

要在正则表达式中得到类似的东西,可以使用引用。那是某种特殊的元字符,比如\1\2等等。这些编号部分引用了在搜索文本中较早找到的字符组。重要的组写在括号(…)中。

有时很难以正确的方式阅读括号,因为现在你有各种各样的括号,甚至可以出现嵌套。最好的方法是一种简单实用的方法:只需计算左括号。这才是最重要的。第一个的块由\1引用,第二个由\2引用,依此类推。JavaScript 最多支持九个引用。其他语言可能支持更多。

A434767_1_En_2_Figd_HTML.jpg Be careful

在字符类中,不能使用引用。这是因为反斜杠在这里没有特殊的含义。但是,由字符类形成的部分可以放在括号中,作为参考。

表达式/apple(,)\sorange\1/将匹配字符串“苹果,橘子,樱桃,桃子”中的“苹果,橘子”。逗号在第一组中是有界的,因此它也出现在“orange”之后。也注意中间的\s。这是空格元字符。

用于引用的组也可以用于重复操作符。使用组作为参考仅仅是一个副作用。

简单组

如果要重复一组字符,只需使用括号:(ab)+abcabcabc等中找到匹配,而在aacbb中没有。

1   var patt = /(ab)+/;
2   console.log(patt.test("abc"));
3   console.log(patt.test("abcabc"));
4   console.log(patt.test("aacbb"));

输出显示结果,如图 2-6 所示。

A434767_1_En_2_Fig6_HTML.jpg

图 2-6。

Output of the script

封闭字符

表达式是一种常用的搜索模式,其中文本由一对相同的字符括起来。这通常是编程语言中字符串或 HTML 中标记的字面外观。观看这些文本片段:

  • “单词单词”应被发现
  • “‘词词’不得发现
  • 应找到“单词单词”

正则表达式的一个可能的解决方案如下所示:

/ˆ(["']){1}.*\1$/

这是怎么回事?表达式以ˆ开始,然后跟一个“(双引号)或一个’(单引号)["']。整个部分被括在括号内,因此它作为一个参考,应该出现一次{1}。然后,任何数量的字符都可以跟在.*后面,直到紧接在$后面,这时必须出现完全相同的引用。那是\1写的。这清楚地表明了引用的性质。

他们没有重复定义;他们重复参考组找到的零件。

1   var patt = /^(["']){1}.*\1$/;
2   console.log(patt.test("\"Word Word\""));
3   console.log(patt.test("\"Word Word\'"));
4   console.log(patt.test("\'Word Word\'"));

输出显示结果,如图 2-7 所示。

A434767_1_En_2_Fig7_HTML.jpg

图 2-7。

Output of the script

因为这个非常结构化的示例通过重复一个单词两次来查找引用的单词,所以准确地处理这个问题是一个很好的练习。我们要找的不是引号,而是两个相同的词:

:/\b((\w+)\s+\2)+/

因为单词有单词边界,所以表达式以一个\b开始。

然后一个组开始,在这个组中,另一个组寻找必须出现一次或多次的单词字符(\w)。后跟一个或多个空格(\s+)。整个组再重复一次(\2)。重复的部分是内部组,因为2指的是第二个左括号。

_ var patt = /\b((\w+)\s+\2)+/;
console.log(patt.test("Script Script istdoppelt"));

非计数组

有时很难读懂一个有很多括号的表达式,其中只有几个(或者只有一个)被用作参考。对此,可以写(:…)组成不计数组。

前瞻参考

回顾参考只是交易的一部分。有时一个匹配是有效的,如果另一个字符跟在后面。这可以通过前瞻参考来实现。两种元字符组合对此很有用:

  • 积极的前瞻;以下字符必须匹配
  • 消极的前瞻;以下字符不得匹配
1   var patt = /(\d{1,3})(?=d)/;
2   var text = "Duration: 16d";
3   var test = patt.exec(text);
4   console.log("Days: " + test[0]);

输出显示结果,如图 2-8 所示。

A434767_1_En_2_Fig8_HTML.jpg

图 2-8。

Output of the script

本示例查找一至三位数的数字,这些数字后面紧跟字母“d”。字母“d”本身不是匹配的一部分——它只是必须在那里使数字匹配。

lookahead and lookbehind

你可以想象,如果有什么东西在向前看,它也可能在向后看。几种语言和平台的正则表达式都有这个特性。但是,JavaScript 不支持后视引用。

三、JavaScript 函数

JavaScript 提供了一个名为RegExp的对象。您可以通过使用new或文字//来创建一个实例。

RegExp 对象

我将从一个概述开始,然后是几个例子。

方法

除了RegExp对象,您可以在 JavaScript 中使用几个内置的字符串函数来处理正则表达式。对象知道这些方法:

  • exec():执行测试并返回第一次命中。
  • test():执行测试并返回truefalse
  • toString():以字符串形式返回表达式。

exec方法返回一个包含以下内容的对象:

  • 包含组的数组(仅计数组):
    • 第一个捕获在索引[0]中
    • 如果有更多的组,这些组遵循[1]..[n]
  • 名为index的属性包含第一次命中的位置,其值从零开始。
  • 名为input的属性包含搜索到的文本。

RegExp对象本身包含更多的信息。当然,您找到了表达式的定义,也找到了迭代到进一步结果的方法(因为exec只返回第一个结果)。

A434767_1_En_3_Figa_HTML.jpgOption g

如果您希望有多个匹配项,您应该使用选项g,这意味着“全局”并在第一个匹配项之后继续搜索。

下面的脚本包含一个非常简单的表达式,它导致了多次点击。同一表达式的后续调用通过增加内部指针贯穿所有命中。这迫使do-循环进行迭代。

 1   var text = "Than Ann answers all questions again.";
 2   var patt = /a/g;
 3   var match = patt.exec(text);
 4   console.log(match[0] + " found at " + match.index);
 5   
 6   match = patt.exec(text);
 7   do {
 8       match = patt.exec(text);
 9       if (!match) break;
10       console.log(match[0] +
11                   " found at " + match.index);
12   } while(true);

输出显示结果,如图 3-1 所示。

A434767_1_En_3_Fig1_HTML.jpg

图 3-1。

Output of the script

性能

以下是一些有助于保持表达式灵活性的属性:

  • constructor:创建RegExp对象的函数。
  • global:检查选项g(布尔),全局。
  • ignoreCase:检查选项i(布尔值),不区分大小写。
  • lastIndex:最后一次点击的索引。
  • multiline:检查选项m(布尔值),多行。
  • source:表情本身。

以下脚本显示了lastIndex属性的作用:

 1   var text = "Than Ann answers all questions again.";
 2   var patt = /a/g;
 3   var match = patt.exec(text);
 4   console.log(match[0] + " found at " + match.index);
 5
 6   match = patt.exec(text);
 7   do {
 8        match = patt.exec(text);
 9        if (!match) break;
10        console.log(match[0] + " found at " + match.index);
11        console.log( "Further search at " + patt.lastIndex);
12   } while(true);

输出显示结果,如图 3-2 所示。

A434767_1_En_3_Fig2_HTML.jpg

图 3-2。

Output of the script

动态性能

如果表达式中有组,它们将显示为动态属性。名字分别是$1$9。如果你不想让一群人这样被抓,考虑使用非计数组(?:)

1   var patt = /(abc)|(def)/;
2   var text = "anton def";
3   console.log(patt.test(text));
4   console.log(RegExp.$1);

第二个输出(第 4 行)返回第一个匹配的内容。那其实就是def

字面形式

文字形式是创建表达式的最简单方法:

1   var patt = /web/i;
2   patt.test("Look for our Web courses!");

这只是返回true。结尾没有了i,结果就是falsei取消了区分大小写,使“web”与“Web”匹配,反之亦然。你甚至可以把这个和exec结合起来:

1   /web/i.exec("Look for our Web courses!");

这将返回一个对象,在第一个位置点击“Web ”,值为 13 用于index。如果没有命中,exec简单地返回null。使用if(//.exec(""))来处理这个问题,因为 JavaScript 会将null视为false。使用new操作符,它看起来像这样:

1   var patt = new RegExp("web");

A434767_1_En_3_Figb_HTML.jpg Literals and Regex

虽然//对 RegExp 有效,但常规字符串在这里也适用——但仅限于这里。

执行选项

已经显示了一些选项。选项不是表达式的一部分;相反,它们修改 regex 引擎的行为。

var pattern = /[A-Z]/i;

以下是所有可用的选项:

  • i:搜索时不区分大小写
  • g:全局搜索,即使已经找到匹配项
  • m:在多行中搜索(如果没有,行尾会停止搜索)

字符串函数

正则表达式不限于RegExp对象。一些常规字符串函数也可以处理这些表达式。

概观

增强了以下函数来处理表达式:

  • search
  • replace
  • match
  • split
1   var str = "Look for our Web courses!";
2   var res = str.search(/Web/i);
3   console.log(res);

res变量包含 13。这是点击量的指数。没有命中,你得到-1。

如果需要布尔值,只需使用match:

1   var str = "Look for our Web courses!";
2   var res = str.match(/Web/);
3   if (res) {
4     console.log("Hit");
5   } else {
6     console.log("No Hit");
7   }

这里你在控制台上被击中。用replace挺像的:

1   var str = "Look for our Web courses!";
2   var res = str.replace(/Web/i, ".NET");
3   console.log(res);

res变量现在包含了查找我们的。网络课程!。

四、模式示例

作为一名开发人员,在你的一生中,你必须一次又一次地完成一些任务。本章包含几个有用的例子。

Web 和网络

本节中的表达式专用于 web 和网络环境。

HTML 标签

这就是你寻找一个固定名称为DIV的 HTML 标签的方法:

/<DIV\b[ˆ>]*>(.*?)<\/DIV>/i

这个表达式寻找任何标签:

/<([A-Z][A-Z0-9]*)\b[ˆ>]*>(.*?)<\/\1>/i

同样,我在第一次点击时使用了一个\1引用来查找匹配的结束标记。

1   var patt = /<([A-Z][A-Z0-9]*)\b[^>]*>(.*?)<\/\1>/i;
2   var html = "Ww want write <b>bold</b> and <i>italic</i> here";
3   console.log(patt.exec(html));

输出显示结果,如图 4-1 所示。

A434767_1_En_4_Fig1_HTML.jpg

图 4-1。

Output of the script

图 4-1 仅显示第一次击中。再次执行以获得更多命中。

知识产权地址

通过 IP 地址,您可以看到 regex 引擎的局限性。搜索模式不太适合数字。经过一些真正的努力,这仍然是可能的,但传统的编程可能是一个更好的选择。让我们先从一个简单的方法开始:

\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b

这很有效,但也适用于 999.999.999.999。而且那绝对不是一个有效的 IP。但是如何将范围限制在 0–255 之间呢?因为这四个部分是相同的,所以只需要为一个部分创建一个模式,然后使用引用。需要的技巧是停止用数字思考,把数字分开处理。单位从 0 到 9,但是如果百大于 250,它只从 0 到 5。对于十位数,情况类似:从 0 到 9,或者从 200 位数,是从 0 到 5。所以我们可以得到从 0 到 255 的数字范围。给你:

/\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.
  (25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.
  (25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.
  (25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/

该表达式将段显示为单个组。如果不需要这些组,整个表达式可以缩短:

/\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}
  (?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/

这并不坏,但它可能看起来有点可怕。同样,处理数字范围不是正则表达式的强项。

Mac 地址

MAC 地址是与硬件相关的标识符,通常用于标识网络端点。它是一个 48 位的数字,是制造商烧录到硬件中的。

A434767_1_En_4_Figa_HTML.jpg有些硬件允许这个数字被软件或设置覆盖,所以老实说可以被欺骗。如果您使用正则表达式来标识这样的数字,请记住这一点。

一般形式是两个十六进制数字的序列,用冒号、空格、破折号甚至什么都不用分隔。在一个漂亮的形式中,它看起来像这样:CB:35:2F:00:7C:A1。下面是表达式:

/ˆ[0-9A-F]{2}([-: ]?[0-9A-F]{2}){5}$/i

以下表达式检查相同的内容,但将所有零件作为组返回,以便进一步研究:

/^([\dA-F]{2})[-: ]?
  ([\dA-F]{2})[-: ]?
  ([\dA-F]{2})[-: ]?
  ([\dA-F]{2})[-: ]?
  ([\dA-F]{2})[-: ]?
  ([\dA-F]{2})$/i

要访问组的内容,您需要 JavaScript 中的一个循环:

1   var patt = /^([\dA-F]{2})[-: ]?([\dA-F]{2})[-: ]?([\dA-F]{2})[-: ]?(\
2   [\dA-F]{2})[-: ]?([\dA-F]{2})[-: ]?([\dA-F]{2})$/i;
3   var mac = "CB:35:2F:00:7C:A1";
4   mac = patt.exec(mac)
5   for(i = 1; i < mac.length; i++) {
6     console.log(mac[i]);
7   }

输出显示结果,如图 4-2 所示。

A434767_1_En_4_Fig2_HTML.jpg

图 4-2。

Output of the script

统一资源定位器

下一个表达式检查通用 URL(统一资源定位器)格式的字符串。URL 以协议(有时称为 schema)开始,这里我们分别允许 http、https 和 ftp。大小写不重要。协议和数据之间的分割线是://。第一种尝试很简单,但是它接受一些无效的形式,拒绝特殊形式的有效形式。主要缺点是 URL 可以包含格式为(https://joergs:password @joergkrause.de/regex/)的用户凭证。不管怎样,有时候这就足够了:

/ˆ([Hh][Tt][Tt][Pp][Ss]?|[Ff][Tt][Pp]):\/\/(.+)$/

更全面的形式如下所示(嵌入在脚本中):

1   var patt = /^((([hH][tT][tT][pP][sS]?|[fF][tT][pP])\:\/\/)?([\w\.\-]\
2   +(\:[\w\.\&%\$\-]+)*@)?((([^\s\(\)\<\>\\\"\.\[\]\,@;:]+)(\.[^\s\(\)\\
3   <\>\\\"\.\[\]\,@;:]+)*(\.[a-zA-Z]{2,4}))|((([01]?\d{1,2}|2[0-4]\d|25\
4   [0-5])\.){3}([01]?\d{1,2}|2[0-4]\d|25[0-5])))(\b\:(6553[0-5]|655[0-2\
5   ]\d|65[0-4]\d{2}|6[0-4]\d{3}|[1-5]\d{4}|[1-9]\d{0,3}|0)\b)?((\/[^\/]\
6   [\w\.\,\?\'\\\/\+&%\$#\=∼_\-@]*)*[^\.\,\?\"\'\(\)\[\]!;<>{}\s\x7F-\x\
7   FF])?)$/;
8   var url = "http://www.joergkrause.de:8080/index.php?id=0";

9   console.log(patt.test(url));

那绝对值得进一步调查。让我们把这个表达式分成相关的部分:

 1   /^                                                 // Start
 2     (                                                //
 3       (                                              //
 4         (                                            //
 5           [hH][tT][tT][pP][sS]?                      // http

 6           |                                          // or

 7           [fF][tT][pP]                               // ftp

 8         )                                            //

 9         \:\/\/                                       // :// token

10       )?
11       (
12         [\w\.\-]+                                    // Domain
13         (\:[\w\.\&%\$\-]+)*@                         //
14       )?
15       (
16         (
17           ([^\s\(\)\<\>\\\"\.\[\]\,@;:]+)            // Parts of domain*
18           (\.[^\s\(\)\<\>\\\"\.\[\]\,@;:]+)*         //
19           (\.[a-zA-Z]{2,4}) // Top level domain
20         )
21         |
22         (
23           (
24             ([01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}    // IP address
25             ([01]?\d{1,2}|2[0-4]\d|25[0-5])          //
26           )
27         )
28         (\b\:                                        // Port number:
29           (6553[0-5]                                 // upper part
30            | //
31            655[0-2]\d                                // ten thousends
32            | //
33            65[0-4]\d{2}                              // thousends
34            | //
35            6[0-4]\d{3}                               // Hundreds
36            | //
37            [1-5]\d{4}                                // Tens
38            | //
39            [1-9]\d{0,3}|0)\b                         // Ones
40          )?                                          //
41          (                                           // path or file

42            (\/[^\/][\w\.\,\?\'\\\/\+&%\$#\=∼_\-@]*)*
43             [^\.\,\?\"\'\(\)\[\]!;<>{}\s\x7F-\xFF]
44          )?
45       )$/

实际上,域定义了不允许的字符,而不是允许的字符。顶级域名限制为两到四个字符,这排除了一些较新的域名。所以在现实中,这个表达式可能需要进一步调整。

查询字符串

URL 后面的部分称为查询字符串。分割线是问号?。该查询字符串通常具有特定的格式,这就是下面的表达式所寻找的格式:

/([ˆ?=&]+)(=([ˆ&]*))?/g

这是正则表达式局限性的另一个例子。你需要一些代码来使它正常工作。完全用正则表达式来做将变得极其复杂。有时候,使用简单的表达式和简单的代码来完成事情比单独使用庞大的表达式或复杂的代码要好。

 1   var uri = 'http://joergkrause.de/index.php?cat=113&prod=2605&query=a\
 2   press';
 3   var queryString = {};
 4   uri.replace(
 5       /([^?=&]+)(=([^&]*))?/g,
 6       function($0, $1, $2, $3) { queryString[$1] = $3; }
 7   );
 8   for (var i in queryString){
 9       if (!queryString[i]) continue;
10     console.log(i + " = " + queryString[i]);
11   }

这段代码假设查询字符串有一串 key=value 对。输出显示结果,如图 4-3 所示。

A434767_1_En_4_Fig3_HTML.jpg

图 4-3。

Output of the script

端口号

端口号从 1 到 65535。这又是一个数字检查练习,我将在本章后面进一步解释。但首先我会展示最后一个表达式:

^(4915[0-1]
 |491[0-4]\d
 |490\d\d
 |4[0-8]\d{3}
 |[1-3]\d{4}
 |[2-9]\d{3}
 |1[1-9]\d{2}
 |10[3-9]\d
 |102[4-9])$

在 JavaScript 中,它可以这样使用(\d是一个数字的元字符):

 1   var patt = /^(4915[0-1]
 2                |491[0-4]\d
 3                |490\d\d
 4                |4[0-8]\d{3}
 5                |[1-3]\d{4}
 6                |[2-9]\d{3}
 7                |1[1-9]\d{2}
 8                |10[3-9]\d
 9                |102[4-9])$/;
10   var port = 1384;
11   console.log(patt.test(port));
12   port = 75000;
13   console.log(patt.test(port));

输出显示true (1384 有效),然后显示false (75000 无效)。

操纵数据

JavaScript 的replace函数直接支持正则表达式。这是基于搜索模式更改数据的常见方式。

删除空格

空格可以很容易地删除。尝试搜索ˆ[ \t]+,将空格替换为空。如果你寻找结尾的空格,用这个:[ \t]+$。如果你想删除开头和结尾的空格,使用这个:ˆ[ \t]+|[ \t]+$。不要像在[ \t](仅空格和制表符)中那样单独声明所有空格字符,您甚至可以添加更多字符,比如换行:[ \t\r\n]。后者可以用空格元字符(\s)缩写。

1   var patt = /[ \t]+/g;
2   var text = "Here•we•have•many••••Spaces\tand•Tabs•too";
3   text = text.replace(patt, "");
4   console.log(text);

清单中的字符只是为了让空格在打印时可见。输出显示结果,如图 4-4 所示。

A434767_1_En_4_Fig4_HTML.jpg

图 4-4。

Output of the script

可变距离的模拟

如果你要寻找的零件之间的距离是一个变量,那么这个表达式就有点难了。假设您正在寻找两个距离很近的单词,但实际距离不是一个常数值。

这个模式需要第一个单词和第二个单词的定义,然后是它们之间的空格。对于单词的未定义部分,单词 border \w+是有帮助的。其他部分使用相反的\W+元字符(无单词字符)。\b是边界本身,并声称没有特征。

“否”表达式看起来是这样的:

\bthere\W+(?:\w+\W+){1,5}?here\b

{1,5}?量词定义了距离的公差。所以这个表达式识别两个相同的单词,两个单词之间的距离是一到五个单词。看一看文本本身:

  • here and there is like there and here

表达式会在句子中间找到单词“there”和“here”(两个单词的距离)。

如果你寻找相反的方式,以及一个 or 运算符,|就足够了:

\b(?:there\W+(?:\w+\W+){1,6}?here |here\W+(?:\w+\W+){1,6}?there)\b

如果你想要成对的单词,可以写得更简单:

\b(word1|word2|word3) (?:\W+\w+){1,6}? \W+(word1|word2|word3)\b

如果要查找同一个单词的精确匹配,可以使用引用来进一步简化表达式:

1   var patt = /\b(there)\W+(?:\w+\W+){1,5}?\1\b/;
2   var text = "here and there is like there and here";
3   console.log(patt.test(text));

这导致了true。如果用“这里”代替,就会得到false(“这里”相隔六个字)。

文件扩展名

以下表达式查找文件扩展名:

/ˆ.*(([ˆ\.][\.][wW][mM][aA])|([ˆ\.][\.][mM][pP][3]))$/

实际例子寻找*.wma*.mp3。这对文件上传场景很有帮助。

不可打印字符

该表达式查找不可打印的字符:

/[\x00-\x1F\x7F]/

颜色的十六进制数字

CSS 和 HTML 中颜色代码的数字使用十六进制形式(#FFFFFF 或#333):

/ˆ#(\d{6})|ˆ#([A-F]{6})|ˆ#([A-F]|[0-9]){6}/

表单验证

表单通常包含 URL、电子邮件、日期或电话号码字段。无论表单来自何处,正则表达式都可以进一步细化检查。

电子邮件

在本书中,我从一个检查电子邮件地址的例子开始。你可以找到数不清的变化。让我们从一个简单的开始,我将进一步完善它:

\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b

这很短,很漂亮,而且——不对。它不能识别完全有效的地址。它甚至让一些无效的漏网之鱼。无论你做什么改进,你首先需要你的需求。处理电子邮件所有有效形式的正则表达式实际上并不存在。但是,99.9%往往就足够好了,不是吗?

该模式的第一个特殊部分是单词边界(\b)的使用,这是在较大文本中查找地址的基础。如果不是需求,可以分别用ˆ$代替边界。

A434767_1_En_4_Figb_HTML.jpg Using functions and options

删除空格已经讨论过了。如果可以使用trim()之类的函数,往往是更好的选择。处理字母大小写最好使用//i选项,而不是像[A-Za-z]这样的扩展字符类。

电子邮件地址的一个关键部分是顶级域名。只要你想只拿到.com或者.net,生活很轻松。但是较新的顶级域名包含了诸如.museum.berlin这样的扩展名。名单是巨大的,新的顶级域名变得随时可用。因此,要么你定期扩展你的表达,要么你接受一切(这意味着joerg@krause.g e eknet将完全有效)。

当然,您可以限制字符的数量。使用{2,4}或延长至{2,6}。您允许的字母越多,您可以捕获的有效顶级域名就越多,无效域名也越多。仅使用互联网早期阶段的顶级水平,这看起来很容易:

ˆ[A-Z0-9._%+-]+@[A-Z0-9.-]+\. ‘ ‘
(?:[A-Z]{2}|com|org|net|edu|gov|mil)$

国名由[A-Z]{1,2}处理,通常可以接受。只要你不在乎“xx”或者“yy”,扩展很容易:

1   `^[A-Z0-9._%+-]+@[A-Z0-9.-]+
2   \.(?:[A-Z]{2}|com|org|net|edu|gov|mil|biz
3   |info|mobi|name|aero|asia|jobs|museum)$`

如果你添加子域名,比如在 joerg@server.firma.provider.com,会有更多的工作要做。因为简单表单接受点,所以它也允许这个电子邮件地址通过。不幸的是,地址joerg@firma….de也被认为是有效的。以下表达式防止了这种情况:

\b[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}\b

由于点成为字符定义的一部分,每个点之前必须至少有一个字符。

所以事情会变得复杂。为了使它正确,看一看互联网标准的定义是一个好主意。对于电子邮件,它是 RFC 5322。从这个文档中,我提取了以下语法定义:

 1     (?:[a-z0-9!#$%&'*+/=?^_`{|}∼-]+
 2      (?:\.[a-z0-9!#$%&'*+/=?^_`{|}∼-]+)*
 3      | "(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]
 4         | \\[\x01-\x09\x0b\x0c\x0e-\x7f])*")
 5    @ (?:(?:a-z0-9?\.)+[a-z0-9]
 6      (?:[a-z0-9-]*[a-z0-9])?
 7      |  \[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}
 8           (?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?
 9              |[a-z0-9-]*[a-z0-9]:
10              (?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]
11              |  \\[\x01-\x09\x0b\x0c\x0e-\x7f])+)
12         \])

符号有目的地将表达式分成两部分。它还包括检查 IP 地址。这很少使用,但完全有效。对于名字,另一个古怪的部分是可以接受的。如果前面有反斜杠,则"\字符有效。这是极不寻常的,但是 RFC 5322 说这是允许的。

虽然它看起来相当复杂,但表达本身却很薄弱。同样,它处理有效的顶级域名,不是按照它们的定义,而是更加全局化。如果我们降低一点要求,不允许名称中有 IP 地址和奇怪的字符,阅读起来就会容易得多:

1   [a-z0-9!#$%&'*+/=?^_`{|}∼-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}∼-]+)*
2   @
3   (?:a-z0-9?\.)+a-z0-9?

现在,我将把它与之前显示的部分结合起来:

1   [a-z0-9!#$%&'*+/=?^_`{|}∼-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}∼-]+)*
2   @
3   (?:a-z0-9?\.)+
4     (?:[A-Z]{2}|com|org|net|edu|gov|mil
5                    |biz|info|mobi|name|aero
6                    |asia|jobs|museum|berlin)\b

A434767_1_En_4_Figc_HTML.jpg Watch Top-Level Domains!

再次注意当前有效的顶级域名列表。完整的名单可以在 IANA 网站上找到。1 如你所见,.bananarepublic是一个有效的顶级域名,.wtf也是。对,WTF。

日期表达式

对于美国的日期格式,我将从一个简单的形式开始:

ˆ(19|20)\d\d- /.- /.$

分隔线是可变的(2015 年 7 月 13 日或 2015 年 7 月 13 日)。它们也可以混合使用(2015/07-13)。混合问题可以通过引用来解决:

ˆ(19|20)\d\d([- /.])(0[1-9]|1[012])\2(0[1-9]|[12][0-9]|3[01])$

对于日期的德语形式,需要另一个表达式:

(0[1-9]|[12][0-9]|3[01])..\d\d

这就是麻烦所在——对于每个表单,您需要另一个表达式。考虑将表达式与| ()运算符结合使用。

年份限制在 1900 年到 2099 年之间。月份可以是 01 到 09 或 10、11、12。天数以十为单位,从 01 到 31。分割线固定为.,但是你可以很容易地改变它。

然而,它并不完美,因为这个表达式允许 31.02.2016。然而,我们都知道二月从来没有 31 天,其他一些月份也是如此。当然,你可以逐月处理。但是在正则表达式中检查闰年是一个真正的挑战。在编程语言中,这很简单:检查你是否能把一年除以 4,但不能除以 100。就这样。

如果必须识别模式,正则表达式既简单又强大。如果一些逻辑开始起作用,事情就会变得棘手。所以让我们结合使用正则表达式和代码:

 1   function check_form(date)
 2   {
 3      var pattern = /(0[1-9]|[12][0-9]|3[01])..\d\d/;
 5      if(date.match(pattern))
 6      {
 7         var date_array = date.split('.');
 8         var day = date_array[0];
 9         // Monate sind intern 0-11

10         var month = date_array[1] - 1;
11         var year = date_array[2];
12         // Prüfung an JavaScript übergeben

13         source = new Date(year,month,day);
14         if(year != source.getFullYear())
15         {
16            console.log('Year wrong!');
17            return false;
18         }
19  
20         if(month != source.getMonth())
21         {
22            console.log('Month wrong!');
23            return false;
24         }
25  
26         if(day != source.getDate())
27         {
28            console.log('Day wrong!');
29            return false;
30         }
31       }
32       else

33       {
34           alert('Pattern wrong!');
35           return false;
36       }
37  
38       return true;
39   }

因此,表达式正在做粗略的部分,代码需要进一步完善。

如果你对正则表达式感到兴奋,并且想在你的同事面前大放异彩,你至少可以这样处理这几个月:

 1   ^(d{0}|
 2      (31(?!(FEB|APR|JUN|SEP|NOV)))
 3       |((30|29)(?!FEB))
 4        |(29(?=FEB(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])
 5                  |((16|[2468][048]|[3579][26])00)))))
 6        |(29(?=FEB(((0[48]|[2468][048]|[13579][26])
 7                  |((16|[2468][048]|[3579][26])00)))))
 8        |(0?[1-9])|1\d|2[0-8])
 9         (JAN|FEB|MAR|MAY|APR|JUL|JUN|AUG|OCT|SEP|NOV|DEC)
10      ((1[6-9]|[2-9]\d)\d{2}|\d{2}|d{0})$

这是使用反向引用的一个非常好的例子。日期和月份之间没有字符,这意味着它也可以识别诸如“23MAR2017”之类的字符串。

A434767_1_En_4_Figd_HTML.jpg Use Functions

如果函数极大地简化了表达式,你应该考虑使用函数。

字符串密码

以下表达式需要具有以下属性的密码:

  • 1 个小写字母
  • 1 个大写字母
  • 1 位数
  • 1 个特殊字符
  • 最少 6 个字符,最多 50 个字符
/((?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\W_]).{6,50})/i

国际标准图书编号

ISBN(国际标准书号)是图书的唯一标识符。这些数字看起来像 ISBN 9-783738-62519-6 或 9-783738-62519-6。

/ˆ(ISBN )?\d-\d{3,6}-\d{3,6}-\d$/

结尾的数字是一个校验值,不能用正则表达式处理。在这里使用代码要容易得多。

货币

对于货币,有一个数字和货币符号的组合。$(美元)字符是一个特殊的挑战,因为它也是一个元字符:

ˆ\$[+-]?([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(\.[0-9]{1,2})?$

数字范围

数字范围总是具有挑战性。假设您要检查 1 到 248。像[1-248]这样的就不行了。你必须创建一个链,并逐位检查。

数字使用[0-9]或\d元字符。从 1 到 99,你应该这样做:

1?[1-9]|[1-9][0-9]

1 到 199 比较容易:1[0-9][0-9]。而且1\d{2}也管用。现在让我们处理一个从 200 到 248 的范围。高于 40 的数字需要特殊处理。首先,让我们看看 200 到 239:

2[0-3][0-9]

现在是 240 比 248:

24[0-8]

总而言之,它看起来像这样:

1[1-9][0-9]|2[0-3][0-9]|24[0-8]

以下是一些常用的范围:

  • 000..255: ˆ([01][0-9][0-9]|2[0-4][0-9]|25[0-5])$
  • 0 或 000..255: ˆ([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$
  • 0 或 000..127: ˆ(0?[0-9]?[0-9]|1[01][0-9]|12[0-7])$
  • 0..999: ˆ([0-9]|[1-9][0-9]|[1-9][0-9][0-9])$
  • 000..999: ˆ[0-9]{3}$
  • 0 或 000..999: ˆ[0-9]{1,3}$
  • 一..999: ˆ([1-9]|[1-9][0-9]|[1-9][0-9][0-9])$
  • 001..999: ˆ(00[1-9]|0[1-9][0-9]|[1-9][0-9][0-9])$
  • 1 或 001..999: ˆ(0{0,2}[1-9]|0?[1-9][0-9]|[1-9][0-9][0- 9])$
  • 0 或 00..59: ˆ[0-5]?[0-9]$
  • 0 或 000..366:ˆ(0?[0-9]?[0-9]|[1-2][0-9][0-9]|3[0-5][0- 9]|``36

浮点数

浮点数和整数一样具有挑战性。您需要处理千分位(一个点(。)在德国和逗号(,)在美国)和小数点(在德国和逗号(,)和点(。)在美国)。让我们从一个简单的版本(美国格式)开始:

[-+]?[0-9]*[.]?[0-9]+

使用*使得小数点之前的所有内容都是可选的。这将允许“–0.6”,这可能是好的,尽管有点奇怪。这是一个更好的例子:

’[-+]?([0-9]*[.][0-9]+|[0-9]+)

写一个异能,看起来是这样的:

ˆ(-?1-9?)((\s?[X*]\s?10Eˆ)|(E([+-]?\d+)))$

这匹配 1.7e5 或者 22eˆ 10。

千分割线

千位分隔符使用逗号(美国格式):

(?:ˆ(?:-)?(?:\d{1,3},(?:\d{3},)*\d{3})(?:\.\d+)?$|ˆ(?:-)?\d*(?:\.\d+)?$)

信用卡

信用卡有非常规则的结构,正则表达式处理得很好。

  • 签证:ˆ4[0-9]{12}(?:[0-9]{3})?$所有签证号码都以“4”开头,有 16 位数字。
  • 万事达:ˆ5[1-5][0-9]{14}$所有的万事达卡号都是 51 到 55 开头,都是 16 位数字。
  • 美国运通:ˆ3[47][0-9]{13}$美国运通以 34 或 37(黄金)开头,有 15 位数字。
  • 大来俱乐部:ˆ3(?:0[0-5]|[68][0-9])[0-9]{11}$大来俱乐部起价 300 到 305 或者 36 或者 38。他们使用 14 位数字。

下面的表达使交易:

1   ^(?:4[0-9]{12}(?:[0-9]{3})?          # Visa
2     | 5[1-5][0-9]{14}                  # MasterCard
3     | 3[47][0-9]{13}                   # American Express
4     | 3(?:0[0-5]|[68][0-9])[0-9]{11}   # Diners Club
5    )$

卡片也有使用 Luhn 算法的校验位。这肯定需要一段代码:

 1   function valid_credit_card(value) {
 2     // Add the former expression here

 3     if (/[⁰-9-\s]+/.test(value)) return false;
 4  
 5          // The LUHn algorithm

 6          var nCheck = 0, nDigit = 0, bEven = false;
 7          value = value.replace(/\D/g, "");
 8  
 9          for (var n = value.length - 1; n >= 0; n--) {
10                   var cDigit = value.charAt(n),
11                             nDigit = parseInt(cDigit, 10);
12   
13                   if (bEven) {
14                           if ((nDigit *= 2) > 9) nDigit -= 9;
15                   }
16  
17                   nCheck += nDigit;
18                   bEven = !bEven;
19          }
20  
21          return (nCheck % 10) == 0;
22   }
23  
24   console.log(valid_credit_card('4012888888881881'));

你可以用前面显示的表达式来代替\d+。表达式只接受数字,不接受为提高可读性而添加的空格或破折号。在开始检查程序之前,请去掉这些字符。

A434767_1_En_4_Fige_HTML.jpg Test Data

若要检查您的代码,您需要信用卡号码。然而,使用真实的信用卡号码总是一个非常糟糕的主意。这就是为什么信用卡发行商拥有永远不会被任何人拥有的测试号码。

表 4-1。

Test Ranges for Credit Cards

| 信用卡类型 | 信用卡号 | | --- | --- | | 美国运通 | 378282246310005 | | 美国运通 | 371449635398431 | | 美国运通公司 | 378734493671000 | | 食客俱乐部 | 30569309025904 | | 就餐者俱乐部 | 38520000023237 | | 万事达信用卡 | 5555555555554444 | | 万事达信用卡 | 5105105105105100 | | 签证 | 4111111111111111 | | 签证 | 4012888888881881 | | 签证 | 4222222222222 |

地理坐标

让我们再一次从一个简单的例子开始:

ˆ[0-9]{1,2}°[0-9]{1,2}[0-9]{1,2} [NnSs] [0-9]{1,2}°[0-9]{1,2}[0-9]{1,2} [WwEe]$

这处理传统形式,例如在 52° 31′27″N 13° 24′37″e 中。

对于其他模式,可能是这样的:

 1   var ck_lat = /^(-?[1-8]?\d(?:\.\d{1,18})?
 2                |90(?:\.0{1,18})?)[EW]?$/i;
 3   var ck_lon = /^(-?(?:1[0-7]|[1-9])?\d(?:\.\d{1,18})?
 4                |180(?:\.0{1,18})?)[NS]?$/i;
 5
 6   function check_lat_lon(lat, lon){
 7     var validLat = ck_lat.test(lat);
 8     var validLon = ck_lon.test(lon);
 9     return (validLat && validLon);
10   }
11
12   console.log(check_lat_lon("13E", "52N"));

脚本使用i操作符作为基点。

guid/新建

guid 是一个全局唯一标识符,是一个 2-128 的数字,由 32 个十六进制数字组成。典型的模式可能是这样的:

  • [{ hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
  • [嘘嘘嘘嘘嘘]
  • [hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh]
  • [0x hhhhhhhhhhhhhhhhhhhhhhhhhhhh]

字母h表示十六进制数字(0-F)。这是你检查它的方法:

 1   /^((?-i:0x)?[A-F0-9]{32}|
 2               [A-F0-9]{8}-
 3               [A-F0-9]{4}-
 4               [A-F0-9]{4}-
 5               [A-F0-9]{4}-
 6               [A-F0-9]{12}|
 7             \{[A-F0-9]{8}-
 8               [A-F0-9]{4}-
 9               [A-F0-9]{4}-
10               [A-F0-9]{4}-
11               [A-F0-9]{12}\})$/i

再次使用i选项,因为你可以用“A”和“A”写十六进制数。

百分率

这里我们从美国格式数字的一个简单模式开始:

(?!ˆ0*$)(?!ˆ0*\.0*$)ˆ\d{1,2}(\.\d{1,4})?$

在德语中,你可以用这样的表达:

(?!ˆ0*$)(?!ˆ0*,0*$)ˆ\d{1,2}(,\d{1,4})?$

Footnotes 1

data . iana . org/TLD/tlds-alpha-by-domain . txt

posted @ 2024-08-19 17:24  绝不原创的飞龙  阅读(9)  评论(0编辑  收藏  举报