笃定

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
    正则表达式看起来很吓人,不容易让人亲近,但它的确很好用,可以很大程度上提高你的开发效率。本文从零开始介绍了应如何学习正则表达式。文中范例主要为JavaScript。

     正则表达式可以很恐怖,真得很恐怖。幸运的是,一旦记住每个符号所表达的意思,恐惧就会快速消退。如果你对正则表达式一无所知,正如文章标题,那你又就有很多东西要学了。下面让我们马上开始吧。

    第一节:基础学习

    想要高效地学习和掌握正则表达式的关键是花一天的时间记住所有符号。这可能是我所能提供的最好的建议。坐下来,做些记忆卡片,然后记住它们。以下为最常见的一些符号:
    . - 匹配任意字符,换行符除外(如果 dotall 为 false)。
    * - 该符号前面的字符,匹配 0 次或多次。
    + - 该符号前面的字符,匹配 1次或多次
    ? - 该符号前面的字符是可选的。匹配 0 次或 1 次。
    \d - 匹配任何单个数字。
    \w - 匹配任何一个字符(包括字母数字以及下划线)。
    [XYZ] - 匹配字符组中的任意一个字符,即 X、Y、Z 中的任意一个。
    [XYZ]+ - 匹配字符组中的一个或多个字符。
    $ - 匹配字符串结束的位置。
    ^ - 匹配字符串开始的位置。
    [^a-z] - 当出现在字符类中时,^ 表示 NOT(非);对于该示例,表示匹配任何非小写字母。

     

    很闷吧,不过还是记住它们,记住之后你会知道好处的。

    工具

    你认为一个表达式是正确的,非常正确,但就是无法得到想要的结果,这时你可能会产生将头发拔光的冲动。去下载 RegExr 桌面应用程序吧,这个对你是必不可少的,而且玩起来非常有趣的。它提供实时检查,还有一个侧边栏,里面包含了每个字符的定义和用户,非常详细。

    RegExr

    第二节:正则表达式傻瓜教程:抓屏视频

    下一步是学习如何真正地使用这些符号。如果视频是你的偏好,那你走运了。这里有五个课程的视频教程,非常适合你:“正则表达式傻瓜教程”。

     

    第三节:正则表达式和 JavaScript

    我们来看看JavaScript 方法如何使用正则表达式。

    1. Test()

    这个方法接受单个字符串参数,然后返回一个布尔值,该值表明是否找到一个批评。如果你不需要对特定的匹配结果进行操作,比如,验证用户名,“test”方法已足够完成这个任务。

    示例

    1. var username = 'JohnSmith';  
    2. alert(/[A-Za-z_-]+/.test(username)); // returns true  

    在上面的代码中,我们首先声明一个正则表达式,表示仅允许大写或小写字母、下划线和连字符。将这些可接受的字符放在括号中,就指定了一个字符组。紧随其后的 + 号表示我们想要正在寻找的是一个或多个前述字符组中的字符。然后使用该范式对变量“JohnSmith”进行测试。由于存在匹配,浏览器的显示框中将显示 true。

    2. Split()

    你可能对 split 方法已经很熟了。该方法接受单个正规表达式,表示在哪里进行“分割”。请注意,如果喜欢,你还可以使用字符串。

    1. var str = 'this is my string';  
    2. alert(str.split(/\s/)); // alerts "this, is, my, string"  

    上面代码中的 \s 表示单个空格,通过它,我们将字符串分割为一个数组。如果想要访问某个特定的值,使用相应的索引即可。

    1. var str = 'this is my this string';  
    2. alert(str.split(/\s/)[3]); // alerts "string"  

    3. replace()

    可能你已经想到了,replace 方法可以用来将文本中的一部分(由字符串或正则表达式表示)替换为不同的字符串。

    示例

    如果想要将“Hello, World”改为“Hello, Universe”,可以使用下面的代码:

    1. var someString = 'Hello, World';  
    2. someString = someString.replace(/World/, 'Universe');  
    3. alert(someString); // alerts "Hello, Universe"  

    应留意的是,对于这个简单的示例,我们本来可以简单的使用.replace('World', 'Universe')。另外,使用 replace 方法不会自动重写变量的值,我们必须将返回值再次分配给这个变量:someString。

    示例 2

    再举一个例子,假设用户要在我们的网站注册一个账号,我们可能想要提供一些基础的安全预防措施。也许我们想要留下他们的用户名,而删除其他任何符号,引号、分号等等。对于 JavaScript 和正则表达式,执行这类任务是桩琐碎的小事情。

    1. var username = 'J;ohnSmith;@%';  
    2. username = username.replace(/[^A-Za-z\d_-]+/, '');  
    3. alert(username); // JohnSmith;@%  

    看到最后生成的显示值,有人可能会想,上面的代码有错误。事实并非如此。你仔细看看,会发现字母“J”后的分号被删除了,正如我们所期望的那样。为了通知引擎继续搜索字符串查找更多匹配,我们可以做结束的斜杠后面直接添加一个“g”,这个修饰符或标记表示“global(全局)”。修改后的代码如下所示:

    1. var username = 'J;ohnSmith;@%';  
    2. username = username.replace(/[^A-Za-z\d_-]+/g, '');  
    3. alert(username); // alerts JohnSmith  

    现在,正则表达式搜索整个字符串,替换所有必要的字符。让我们看看关键的表达式(.replace(/[^A-Za-z\d_-]+/g, '');),要注意,括号内的向上箭头(即 ^)非常重要。当放在字符组中时,该符号表示“找到所有不是……”。现在回头在看看这个代码,它表示,找到所有不是字母、数字(由 \d 表示)、下划线或连字符的符号;如果找到一个匹配,将其替换为空,事实上就是删除该字符。

    4. Match()

    与test方法不同,match() 返回一个包含所有找到的匹配的数组。

    示例

    1. var name = 'JeffreyWay';  
    2. alert(name.match(/e/)); // alerts "e"  

    上面的代码将显示一个字母“e”。但是,在字符串“JeffreyWay”中实际上包含2个 e。同样,这次我们还是要使用修饰符“g”来声明一个全局搜索。

    1. var name = 'JeffreyWay';  
    2. alert(name.match(/e/g)); // alerts "e,e"  

    如果想要显示数组中这些特定值中的一个,可以做括号中引用想要的索引。

    1. var name = 'JeffreyWay';  
    2. alert(name.match(/e/g)[1]); // alerts "e"  

    示例 2

    让我们看下一个示例,确保我们对它的理解是正确的。

    1. var string = 'This is just a string with some 12345 and some !@#$ mixed in.';  
    2. alert(string.match(/[a-z]+/gi)); // alerts "This,is,just,a,string,with,some,and,some,mixed,in"  

    在这个正则表达式中,我们创建了一个范式,可匹配一个或多个大写或小写字母。这多亏了“i”修饰符。除外,我们还加上了“g”来声明进行全局搜索。上面的代码将显示“This,is,just,a,string,with,some,and,some,mixed,in.”然后,如果想要获取变量数组中这些值中的某一个,我们只需引用相应的索引即可。

    1. var string = 'This is just a string with some 12345 and some !@#$ mixed in.';  
    2. var matches = string.match(/[a-z]+/gi);  
    3. alert(matches[2]); // alerts "just"  

    分割电子邮件地址

    为了练习,我们试着将一个电子邮件地址(nettuts@tutsplus.com)分割为相应的两部分:用户名和域名,即 nettuts 和tutsplus。

    1. var email = 'nettuts@tutsplus.com';  
    2. alert(email.replace(/([a-z\d_-]+)@([a-z\d_-]+)\.[a-z]{2,4}/ig, '$1, $2')); // alerts "nettuts, tutsplus"  

    如果对于正则表达式,你还是新手,上面的代码可能会看起来有点吓人。不用担心,第一次看到都会感到“恐怖”。一旦将它分解为一个一个小子集,你会发觉其实非常简单。下面让我们一条一条进行分析:

    1. .replace(/([a-z\d_-]+)   

    从中间开始看,我们要搜索任何字母、数字、下划线或连字符,并且匹配一次或多次(+)。无论匹配是上面,我们想要访问其值,所以将其放到括号中。这样,我们稍后可以引用这个匹配的子集。

    1. @([a-z\d_-]+)   

    紧接着前一个匹配,我们看到 @ 符号,然后是有一组一个或多个字母、数字、下划线和连字符。同样,我们将其放到括号内,以便稍后访问。

    1. \.[a-z]{2,4}/ig,   

    继续找,我们看到一个点。因为在正则表达式中,句点可表示任何字符(有时换行符除外),因此必须使用“\”进行转义。最后一部分是用于查找“.com”。我们知道,大多数域名,如果不是所有,其后缀为 2 到 4 个字符(com、edu、net、name 等等)。如果找到具体的范围,我们可以先行使用更常规的符号,如 * 或 +。不过,我们在这里是将2个数字放到一个大括号中,分别表示最大值和最小值。

    1. '$1, $2')   

    这个最后一部分表示replace方法的第二个参数,或者我们想要将匹配字符集替换为的对象。在这里,我们使用 $1 和 $2 分别来引用保存在第一个和第二个括号中的值。对于这个特定示例, $1 指向 nettuts,$2 指向 tutsplus。

    创建自己的位置对象

    作为最后的一个项目,我们将创建位置对象。位置对象向用户提供有关当前页面的信息:href、协议、地址、端口等。请注意,这里仅仅是作为练习之用。对于真正的网址,使用已有的位置对象即可。

    首先我们创建位置函数,该函数接受单个参数,该参数表示我们想要“解码”的网址,我们将其称为“loc”。

    1. function loc(url) { }   

    现在,我们可以按照下面的方式调用它,并传入一个乱七八糟的 url:

    1. var l = loc('http://www.somesite.com?somekey=somevalue&anotherkey=anothervalue#theHashGoesHere');   

    下一步,我们需要返回包含多个方法的对象。

    1. function loc(url) {  
    2.     return {  
    3.   
    4.      }  
    5. }   

    搜索(search)

    我们不会创建所有方法,但我们会模仿其中几个。第一个是“search”。使用正则表达式,我们将要搜索 url 并返回查询字符串中的所有内容。

    1. return {  
    2.      search : function() {  
    3.         return url.match(/\?(.+)/i)[1];   
    4.                // returns "somekey=somevalue&anotherkey=anothervalue#theHashGoesHere"
    5.      }  
    6. }   

    在上面的代码中,我们使用了传入的 url,试图使用我们的正则表达式对其进行匹配。这个正则表达式在整个字符串中搜索问号,问号表示查询字符串(querystring)的开始。在这个位置,我们需奥获取其余的字符,这就是将(.+)放入括号的原因。最后,我们需要返回那个字符区块,因此,使用[1]来定位它。

    //注:match返回匹配项的数组,这边的索引为1,返回什么呢?因为是i仅能返回一项,怎么会是1?

    通过实例运行发现,alert(url.match(/\?(.+)/i),返回两个值,一个带问号的全匹配?somekey=... 一个是括号中匹配项ssomekey=...

    奇怪?待研究~

    哈希值(Hash)

    现在我们将创建一个方法,返回 url 的哈希值,或者 # 号后面的内容。

    1. hash : function() {  
    2.     return url.match(/#(.+)/i)[1]; // returns "theHashGoesHere"  
    3. },   

    这次,我们搜索 # 号,同样,使用括号获取后面的字符,并使用 [1] 指向那个特定的子集。

    协议

    protocol 方法应返回,页面所用的协议,可能你已经猜到了。这种协议通常为 http 或 https。

    1. protocol : function() {  
    2.     return url.match(/(ht|f)tps?:/i)[0]; // returns 'http:'  
    3. },   

    这一个稍微更加复杂一点,因为存在几个选项:http、https 和 ftp。

    虽然可以使用这样的格式 (http|https|ftp),但使用 (ht|f)tps? 更为简洁,表示我们首先查找“ht”或“f”字符,下一步,匹配“tp”字符。最后的“s”是可选的,所以我们加了一个问号,表示问号前的字符出现零次或一次。

    Href

    这个是最后一个方法,返回页面的 url。

    1. href : function() {  
    2.     return url.match(/(.+\.[a-z]{2,4})/ig); // returns "http://www.somesite.com"  
    3. }   

    这里,我们对所有字符进行匹配,直到找到一个点号,这个点号后面有 2 - 4 个字符(表示 com、au、edu、name 等等)。重要的是意识到,对于这些表达式,我们可以写得很复杂也可以写得很简单,这在于我们要求有多严格。

    一个简单的函数

    1. function loc(url) {  
    2.     return {  
    3.          search : function() {  
    4.             return url.match(/\?(.+)/i)[1];  
    5.           },  
    6.   
    7.          hash : function() {  
    8.             return url.match(/#(.+)/i)[1];  
    9.          },  
    10.   
    11.          protocol : function() {  
    12.             return url.match(/(ht|f)tps?:/)[0];  
    13.          },  
    14.       
    15.          href : function() {  
    16.             return url.match(/(.+\.[a-z]{2,4})/ig);  
    17.          }  
    18.      }  
    19. }   

    使用上面这个函数,我们可以很简单地显示网址的每个部分:

    1. var l = loc('http://www.net.tutsplus.edu?key=value#hash');  
    2.   
    3. alert(l.href()); // http://www.net.tutsplus.com  
    4. alert(l.protocol()); // http:  
    5.   
    6. ...etc.   
posted on 2010-08-27 11:23  笃定  阅读(443)  评论(0编辑  收藏  举报