从制造到创造
软件工程师成长之路
posts - 234,  comments - 64,  trackbacks - 0

  如果我们问那些UNIX系统的爱好者他们最喜欢什么,答案除了稳定的系统和可以远程启动之外,十有八九的人会提到正则表达式;如果我们再问他们最头痛的是什么,可能除了复杂的进程控制和安装过程之外,还会是正则表达式。那么正则表达式到底是什么?如何才能真正的掌握正则表达式并正确的加以灵活运用?本文将就此展开介绍,希望能够对那些渴望了解和掌握正则表达式的读者有所助益。

入门简介

  简单的说,正则表达式是一种可以用于模式匹配和替换的强有力的工具。我们可以在几乎所有的基于UNIX系统的工具中找到正则表达式的身影,例如,vi编辑器,Perl或PHP脚本语言,以及awk或sed shell程序等。此外,象JavaScript这种客户端的脚本语言也提供了对正则表达式的支持。由此可见,正则表达式已经超出了某种语言或某个系统的局限,成为人们广为接受的概念和功能。

  正则表达式可以让用户通过使用一系列的特殊字符构建匹配模式,然后把匹配模式与数据文件、程序输入以及WEB页面的表单输入等目标对象进行比较,根据比较对象中是否包含匹配模式,执行相应的程序。

  举例来说,正则表达式的一个最为普遍的应用就是用于验证用户在线输入的邮件地址的格式是否正确。如果通过正则表达式验证用户邮件地址的格式正确,用户所填写的表单信息将会被正常处理;反之,如果用户输入的邮件地址与正则表达的模式不匹配,将会弹出提示信息,要求用户重新输入正确的邮件地址。由此可见正则表达式在WEB应用的逻辑判断中具有举足轻重的作用。

基本语法

  在对正则表达式的功能和作用有了初步的了解之后,我们就来具体看一下正则表达式的语法格式。

  正则表达式的形式一般如下:

  /love/

  其中位于“/”定界符之间的部分就是将要在目标对象中进行匹配的模式。用户只要把希望查找匹配对象的模式内容放入“/”定界符之间即可。为了能够使用户更加灵活的定制模式内容,正则表达式提供了专门的“元字符”。所谓元字符就是指那些在正则表达式中具有特殊意义的专用字符,可以用来规定其前导字符(即位于元字符前面的字符)在目标对象中的出现模式。

  较为常用的元字符包括: “+”, “*”,以及 “?”。其中,“+”元字符规定其前导字符必须在目标对象中连续出现一次或多次,“*”元字符规定其前导字符必须在目标对象中出现零次或连续多次,而“?”元字符规定其前导对象必须在目标对象中连续出现零次或一次。

  下面,就让我们来看一下正则表达式元字符的具体应用。

  /fo+/

  因为上述正则表达式中包含“+”元字符,表示可以与目标对象中的 “fool”, “fo”, 或者 “football”等在字母f后面连续出现一个或多个字母o的字符串相匹配。

  /eg*/

  因为上述正则表达式中包含“*”元字符,表示可以与目标对象中的 “easy”, “ego”, 或者 “egg”等在字母e后面连续出现零个或多个字母g的字符串相匹配。

  /Wil?/

  因为上述正则表达式中包含“?”元字符,表示可以与目标对象中的 “Will”, 或者 “Wilson”,等在字母i后面连续出现零个或一个字母l的字符串相匹配。

  除了元字符之外,用户还可以精确指定模式在匹配对象中出现的频率。例如,

  /jim{2,6}/

  上述正则表达式规定字符m可以在匹配对象中连续出现2-6次,因此,上述正则表达式可以同jimmy或jimmmmmy等字符串相匹配。

  在对如何使用正则表达式有了初步了解之后,我们来看一下其它几个重要的元字符的使用方式。

  s:用于匹配单个空格符,包括tab键和换行符;

  S:用于匹配除单个空格符之外的所有字符;

  d:用于匹配从0到9的数字;

  w:用于匹配字母,数字或下划线字符;

  W:用于匹配所有与w不匹配的字符;

  . :用于匹配除换行符之外的所有字符。

  (说明:我们可以把s和S以及w和W看作互为逆运算)

  下面,我们就通过实例看一下如何在正则表达式中使用上述元字符。

  /s+/

  上述正则表达式可以用于匹配目标对象中的一个或多个空格字符。

  /d000/

  如果我们手中有一份复杂的财务报表,那么我们可以通过上述正则表达式轻而易举的查找到所有总额达千元的款项。

  除了我们以上所介绍的元字符之外,正则表达式中还具有另外一种较为独特的专用字符,即定位符。定位符用于规定匹配模式在目标对象中的出现位置。

  较为常用的定位符包括: “^”, “$”, “” 以及 “B”。其中,“^”定位符规定匹配模式必须出现在目标字符串的开头,“$”定位符规定匹配模式必须出现在目标对象的结尾,定位符规定匹配模式必须出现在目标字符串的开头或结尾的两个边界之一,而“B”定位符则规定匹配对象必须位于目标字符串的开头和结尾两个边界之内,即匹配对象既不能作为目标字符串的开头,也不能作为目标字符串的结尾。同样,我们也可以把“^”和“$”以及“”和“B”看作是互为逆运算的两组定位符。举例来说:

  /^hell/

  因为上述正则表达式中包含“^”定位符,所以可以与目标对象中以 “hell”, “hello”或 “hellhound”开头的字符串相匹配。

  /ar$/

  因为上述正则表达式中包含“$”定位符,所以可以与目标对象中以 “car”, “bar”或 “ar” 结尾的字符串相匹配。

  /bom/

  因为上述正则表达式模式以“”定位符开头,所以可以与目标对象中以 “bomb”, 或 “bom”开头的字符串相匹配。

  /man/

  因为上述正则表达式模式以“”定位符结尾,所以可以与目标对象中以 “human”, “woman”或 “man”结尾的字符串相匹配。

  为了能够方便用户更加灵活的设定匹配模式,正则表达式允许使用者在匹配模式中指定某一个范围而不局限于具体的字符。例如:

  /[A-Z]/

  上述正则表达式将会与从A到Z范围内任何一个大写字母相匹配。

  /[a-z]/

  上述正则表达式将会与从a到z范围内任何一个小写字母相匹配。

  /[0-9]/

  上述正则表达式将会与从0到9范围内任何一个数字相匹配。

  /([a-z][A-Z][0-9])+/

  上述正则表达式将会与任何由字母和数字组成的字符串,如 “aB0” 等相匹配。这里需要提醒用户注意的一点就是可以在正则表达式中使用 “()” 把字符串组合在一起。“()”符号包含的内容必须同时出现在目标对象中。因此,上述正则表达式将无法与诸如 “abc”等的字符串匹配,因为“abc”中的最后一个字符为字母而非数字。

  如果我们希望在正则表达式中实现类似编程逻辑中的“或”运算,在多个不同的模式中任选一个进行匹配的话,可以使用管道符 “|”。例如:

  /to|too|2/

  上述正则表达式将会与目标对象中的 “to”, “too”, 或 “2” 相匹配。

  正则表达式中还有一个较为常用的运算符,即否定符 “[^]”。与我们前文所介绍的定位符 “^” 不同,否定符 “[^]”规定目标对象中不能存在模式中所规定的字符串。例如:

  /[^A-C]/

  上述字符串将会与目标对象中除A,B,和C之外的任何字符相匹配。一般来说,当“^”出现在 “[]”内时就被视做否定运算符;而当“^”位于“[]”之外,或没有“[]”时,则应当被视做定位符。

  最后,当用户需要在正则表达式的模式中加入元字符,并查找其匹配对象时,可以使用转义符“”。例如:

  /Th*/

  上述正则表达式将会与目标对象中的“Th*”而非“The”等相匹配。

  使用实例

  在对正则表达式有了较为全面的了解之后,我们就来看一下如何在Perl,PHP,以及JavaScript中使用正则表达式。

  通常,Perl中正则表达式的使用格式如下:

  operator / regular-expression / string-to-replace / modifiers

  运算符一项可以是m或s,分别代表匹配运算和替换运算。

  其中,正则表达式一项是将要进行匹配或替换操作的模式,可以由任意字符,元字符,或定位符等组成。替换字符串一项是使用s运算符时,对查找到的模式匹配对象进行替换的字符串。最后的参数项用来控制不同的匹配或替换方式。例如:

  s/geed/good/

  将会在目标对象中查找第一个出现的geed字串,并将其替换为good。如果我们希望在目标对象的全局范围内执行多次查找—替换操作的话,可以使用参数 “g”,即s/love/lust/g。

  此外,如果我们不需要限制匹配的大小写形式的话,可以使用参数 “i ”。例如,

  m/JewEL/i

  上述正则表达式将会与目标对象中的jewel,Jewel,或JEWEL相匹配。

  在Perl中,使用专门的运算符“=~”指定正则表达式的匹配对象。例如:

  $flag =~ s/abc/ABC/

  上述正则表达式将会把变量$flag中的字串abc替换为ABC。

  下面,我们就在Perl程序中加入正则表达式,验证用户邮件地址格式的有效性。代码如下:

  #!/usr/bin/perl
  # get input
  print “What's your email address? ”;
  $email = <>
  chomp($email);
  # match and display result
  if($email =~ /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/)
  {
  print(“Your email address is correct! ”);
  }
  else
   {
    print(“Please try again! ”);
   }

  如果用户更偏爱PHP的话,可以使用ereg()函数进行模式匹配操作。ereg()函数的使用格式如下:
   ereg(pattern, string)

  其中,pattern代表正则表达式的模式,而string则是执行查找替换操作的目标对象。同样是验证邮件地址,使用PHP编写的程序代码如下:

  <?php
   if (ereg(“^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+”,$email))
    { echo “Your email address is correct!”;}
   else
    { echo “Please try again!”;}
   ?>
  最后,我们在来看一下JavaScript。JavaScript 1.2中带有一个功能强大的RegExp()对象,可以用来进行正则表达式的匹配操作。其中的test()方法可以检验目标对象中是否包含匹配模式,并相应的返回true或false。

  我们可以使用JavaScript编写以下脚本,验证用户输入的邮件地址的有效性。

  <html>
   <head>
    < language="Javascript1.2">
     <!-- start hiding
     function verifyAddress(obj)
     {
      var email = obj.email.value;
      var pattern = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/;
      flag = pattern.test(email);
      if(flag)
      {
       alert(“Your email address is correct!”);
       return true;
      }
      else
       {
        alert(“Please try again!”);
        return false;
        }
      }
     // stop hiding -->
    </script>
   </head>
   <body>
   <form onSubmit="return verifyAddress(this);">
   <input name="email" type="text">
   <input type="submit" value="提交">
   </form>
  </body>
 </html>

posted @ 2006-10-13 23:57 CoderDream 阅读(109) | 评论 (0)编辑 收藏

正则表达式之道

原著:Steve Mansour
sman@scruznet.com
Revised: June 5, 1999
(copied by jm /at/ jmason.org from http://www.scruz.net/%7esman/regexp.htm, after the original disappeared! )

翻译:Neo Lee
neo.lee@gmail.com
2004年10月16日


英文版原文

译者按:原文因为年代久远,文中很多链接早已过期(主要是关于vi、sed等工具的介绍和手册),本译文中已将此类链接删除,如需检查这些链接可以查看上面链接的原文。除此之外基本照原文直译,括号中有“译者按”的部分是译者补充的说明。如有内容方面的问题请直接和Steve Mansor联系,当然,如果你只写中文,也可以和我联系。


目 录

什么是正则表达式
范例
   简单
   中级(神奇的咒语)
   困难(不可思议的象形文字)
不同工具中的正则表达式


什么是正则表达式

一个正则表达式,就是用某种模式去匹配一类字符串的一个公式。很多人因为它们看上去比较古怪而且复杂所以不敢去使用——很不幸,这篇文章也不能够改变这一点,不过,经过一点点练习之后我就开始觉得这些复杂的表达式其实写起来还是相当简单的,而且,一旦你弄懂它们,你就能把数小时辛苦而且易错的文本处理工作压缩在几分钟(甚至几秒钟)内完成。正则表达式被各种文本编辑软件、类库(例如Rogue Wave的tools.h++)、脚本工具(像awk/grep/sed)广泛的支持,而且像Microsoft的Visual C++这种交互式IDE也开始支持它了。

我们将在如下的章节中利用一些例子来解释正则表达式的用法,绝大部分的例子是基于vi中的文本替换命令和grep文件搜索命令来书写的,不过它们都是比较典型的例子,其中的概念可以在sed、awk、perl和其他支持正则表达式的编程语言中使用。你可以看看不同工具中的正则表达式这一节,其中有一些在别的工具中使用正则表达式的例子。还有一个关于vi中文本替换命令(s)的简单说明附在文后供参考。

正则表达式基础

正则表达式由一些普通字符和一些元字符(metacharacters)组成。普通字符包括大小写的字母和数字,而元字符则具有特殊的含义,我们下面会给予解释。

在最简单的情况下,一个正则表达式看上去就是一个普通的查找串。例如,正则表达式"testing"中没有包含任何元字符,,它可以匹配"testing"和"123testing"等字符串,但是不能匹配"Testing"。

要想真正的用好正则表达式,正确的理解元字符是最重要的事情。下表列出了所有的元字符和对它们的一个简短的描述。

元字符 描述


.
匹配任何单个字符。例如正则表达式r.t匹配这些字符串:ratrutr t,但是不匹配root。 
$
匹配行结束符。例如正则表达式weasel$ 能够匹配字符串"He's a weasel"的末尾,但是不能匹配字符串"They are a bunch of weasels."。 
^
匹配一行的开始。例如正则表达式^When in能够匹配字符串"When in the course of human events"的开始,但是不能匹配"What and When in the"。
*
匹配0或多个正好在它之前的那个字符。例如正则表达式.*意味着能够匹配任意数量的任何字符。
\
这是引用府,用来将这里列出的这些元字符当作普通的字符来进行匹配。例如正则表达式\$被用来匹配美元符号,而不是行尾,类似的,正则表达式\.用来匹配点字符,而不是任何字符的通配符。
[ ] 
[c1-c2]
[^c1-c2]
匹配括号中的任何一个字符。例如正则表达式r[aou]t匹配ratrotrut,但是不匹配ret。可以在括号中使用连字符-来指定字符的区间,例如正则表达式[0-9]可以匹配任何数字字符;还可以制定多个区间,例如正则表达式[A-Za-z]可以匹配任何大小写字母。另一个重要的用法是“排除”,要想匹配除了指定区间之外的字符——也就是所谓的补集——在左边的括号和第一个字符之间使用^字符,例如正则表达式[^269A-Z] 将匹配除了2、6、9和所有大写字母之外的任何字符。
\< \>
匹配词(word)的开始(\<)和结束(\>)。例如正则表达式\<the能够匹配字符串"for the wise"中的"the",但是不能匹配字符串"otherwise"中的"the"。注意:这个元字符不是所有的软件都支持的。
\( \)
将 \( 和 \) 之间的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 \1\9 的符号来引用。
|
将两个匹配条件进行逻辑“或”(Or)运算。例如正则表达式(him|her) 匹配"it belongs to him"和"it belongs to her",但是不能匹配"it belongs to them."。注意:这个元字符不是所有的软件都支持的。
+
匹配1或多个正好在它之前的那个字符。例如正则表达式9+匹配9、99、999等。注意:这个元字符不是所有的软件都支持的。
?
匹配0或1个正好在它之前的那个字符。注意:这个元字符不是所有的软件都支持的。
\{i\}
\{i,j\}
匹配指定数目的字符,这些字符是在它之前的表达式定义的。例如正则表达式A[0-9]\{3\} 能够匹配字符"A"后面跟着正好3个数字字符的串,例如A123、A348等,但是不匹配A1234。而正则表达式[0-9]\{4,6\} 匹配连续的任意4个、5个或者6个数字字符。注意:这个元字符不是所有的软件都支持的。


最简单的元字符是点,它能够匹配任何单个字符(注意包括新行符)。假定有个文件test.txt包含以下几行内容:

    he is a rat
    he is in a rut
    the food is Rotten
    I like root beer
我们可以使用grep命令来测试我们的正则表达式,grep命令使用正则表达式去尝试匹配指定文件的每一行,并将至少有一处匹配表达式的所有行显示出来。命令
    grep r.t test.txt
在test.txt文件中的每一行中搜索正则表达式r.t,并打印输出匹配的行。正则表达式r.t匹配一个r接着任何一个字符再接着一个t。所以它将匹配文件中的ratrut,而不能匹配Rotten中的Rot,因为正则表达式是大小写敏感的。要想同时匹配大写和小写字母,应该使用字符区间元字符(方括号)。正则表达式[Rr]能够同时匹配Rr。所以,要想匹配一个大写或者小写的r接着任何一个字符再接着一个t就要使用这个表达式:[Rr].t

要想匹配行首的字符要使用抑扬字符(^)——又是也被叫做插入符。例如,想找到text.txt中行首"he"打头的行,你可能会先用简单表达式he,但是这会匹配第三行的the,所以要使用正则表达式^he,它只匹配在行首出现的h

有时候指定“除了×××都匹配”会比较容易达到目的,当抑扬字符(^)出现在方括号中是,它表示“排除”,例如要匹配he ,但是排除前面是t or s的情性(也就是theshe),可以使用:[^st]he

可以使用方括号来指定多个字符区间。例如正则表达式[A-Za-z]匹配任何字母,包括大写和小写的;正则表达式[A-Za-z][A-Za-z]* 匹配一个字母后面接着0或者多个字母(大写或者小写)。当然我们也可以用元字符+做到同样的事情,也就是:[A-Za-z]+ ,和[A-Za-z][A-Za-z]*完全等价。但是要注意元字符+ 并不是所有支持正则表达式的程序都支持的。关于这一点可以参考后面的正则表达式语法支持情况

要指定特定数量的匹配,要使用大括号(注意必须使用反斜杠来转义)。想匹配所有1001000的实例而排除1010000,可以使用:10\{2,3\},这个正则表达式匹配数字1后面跟着2或者3个0的模式。在这个元字符的使用中一个有用的变化是忽略第二个数字,例如正则表达式0\{3,\} 将匹配至少3个连续的0。

简单的例子

这里有一些有代表性的、比较简单的例子。

vi 命令作用


:%s/ */ /g把一个或者多个空格替换为一个空格。
:%s/ *$//去掉行尾的所有空格。
:%s/^/ /在每一行头上加入一个空格。
:%s/^[0-9][0-9]* //去掉行首的所有数字字符。
:%s/b[aeio]g/bug/g将所有的bagbegbigbog改为bug。 
:%s/t\([aou]\)g/h\1t/g将所有tagtogtug分别改为hathothug(注意用group的用法和使用\1引用前面被匹配的字符)。

中级的例子(神奇的咒语)

例1

将所有方法foo(a,b,c)的实例改为foo(b,a,c)。这里a、b和c可以是任何提供给方法foo()的参数。也就是说我们要实现这样的转换:

之前 之后
foo(10,7,2)foo(7,10,2)
foo(x+13,y-2,10)foo(y-2,x+13,10)
foo( bar(8), x+y+z, 5)foo( x+y+z, bar(8), 5)

下面这条替换命令能够实现这一魔法:

    :%s/foo(\([^,]*\),\([^,]*\),\([^)]*\))/foo(\2,\1,\3)/g

现在让我们把它打散来加以分析。写出这个表达式的基本思路是找出foo()和它的括号中的三个参数的位置。第一个参数是用这个表达式来识别的::\([^,]*\),我们可以从里向外来分析它: 

[^,] 除了逗号之外的任何字符
[^,]*0或者多个非逗号字符
\([^,]*\)将这些非逗号字符标记为\1,这样可以在之后的替换模式表达式中引用它
\([^,]*\),我们必须找到0或者多个非逗号字符后面跟着一个逗号,并且非逗号字符那部分要标记出来以备后用。

现在正是指出一个使用正则表达式常见错误的最佳时机。为什么我们要使用[^,]*这样的一个表达式,而不是更加简单直接的写法,例如:.*,来匹配第一个参数呢?设想我们使用模式.*来匹配字符串"10,7,2",它应该匹配"10,"还是"10,7,"?为了解决这个两义性(ambiguity),正则表达式规定一律按照最长的串来,在上面的例子中就是"10,7,",显然这样就找出了两个参数而不是我们期望的一个。所以,我们要使用[^,]*来强制取出第一个逗号之前的部分。

这个表达式我们已经分析到了:foo(\([^,]*\),这一段可以简单的翻译为“当你找到foo(就把其后直到第一个逗号之前的部分标记为\1”。然后我们使用同样的办法标记第二个参数为\2。对第三个参数的标记方法也是一样,只是我们要搜索所有的字符直到右括号。我们并没有必要去搜索第三个参数,因为我们不需要调整它的位置,但是这样的模式能够保证我们只去替换那些有三个参数的foo()方法调用,在foo()是一个重载(overoading)方法时这种明确的模式往往是比较保险的。然后,在替换部分,我们找到foo()的对应实例,然后利用标记好的部分进行替换,是的第一和第二个参数交换位置。

例2

假设有一个CSV(comma separated value)文件,里面有一些我们需要的信息,但是格式却有问题,目前数据的列顺序是:姓名,公司名,州名缩写,邮政编码,现在我们希望讲这些数据重新组织,以便在我们的某个软件中使用,需要的格式为:姓名,州名缩写-邮政编码,公司名。也就是说,我们要调整列顺序,还要合并两个列来构成一个新列。另外,我们的软件不能接受逗号前后面有任何空格(包括空格和制表符)所以我们还必须要去掉逗号前后的所有空格。

这里有几行我们现在的数据:

    Bill Jones,     HI-TEK Corporation ,  CA, 95011
    Sharon Lee Smith,  Design Works Incorporated,  CA, 95012
    B. Amos   ,  Hill Street Cafe,  CA, 95013
    Alexander Weatherworth,  The Crafts Store,  CA, 95014
    ...
我们希望把它变成这个样子:
    Bill Jones,CA 95011,HI-TEK Corporation
    Sharon Lee Smith,CA 95012,Design Works Incorporated
    B. Amos,CA 95013,Hill Street Cafe
    Alexander Weatherworth,CA 95014,The Crafts Store
    ...
我们将用两个正则表达式来解决这个问题。第一个移动列和合并列,第二个用来去掉空格。

下面就是第一个替换命令:

    :%s/\([^,]*\),\([^,]*\),\([^,]*\),\(.*\)/\1,\3 \4,\2/
这里的方法跟例1基本一样,第一个列(姓名)用这个表达式来匹配:\([^,]*\),即第一个逗号之前的所有字符,而姓名内容被用\1标记下来。公司名和州名缩写字段用同样的方法标记为\2\3,而最后一个字段用\(.*\)来匹配("匹配所有字符直到行末")。替换部分则引用上面标记的那些内容来进行构造。

下面这个替换命令则用来去除空格:

    :%s/[ \t]*,[ \t]*/,/g
我们还是分解来看:[ \t]匹配空格/制表符,[ \t]* 匹配0或多个空格/制表符,[ \t]*,匹配0或多个空格/制表符后面再加一个逗号,最后,[ \t]*,[ \t]*匹配0或多个空格/制表符接着一个逗号再接着0或多个空格/制表符。在替换部分,我们简单的我们找到的所有东西替换成一个逗号。这里我们使用了结尾的可选的g参数,这表示在每行中对所有匹配的串执行替换(而不是缺省的只替换第一个匹配串)。

例3

假设有一个多字符的片断重复出现,例如:
Billy tried really hard
Sally tried really really hard
Timmy tried really really really hard
Johnny tried really really really really hard
而你想把"really"、"really really",以及任意数量连续出现的"really"字符串换成一个简单的"very"(simple is good!),那么以下命令:
:%s/\(really \)\(really \)*/very /
就会把上述的文本变成:
Billy tried very hard
Sally tried very hard
Timmy tried very hard
Johnny tried very hard
表达式\(really \)*匹配0或多个连续的"really "(注意结尾有个空格),而\(really \)\(really \)* 匹配1个或多个连续的"really "实例。

困难的例子(不可思议的象形文字)

Coming soon.


不同工具中的正则表达式

OK,你已经准备使用RE(regular expressions,正则表达式),但是你并准备使用vi。所以,在这里我们给出一些在其他工具中使用RE的例子。另外,我还会总结一下你在不同程序之间使用RE可能发现的区别。

当然,你也可以在Visual C++编辑器中使用RE。选择Edit->Replace,然后选择"Regular expression"选择框,Find What输入框对应上面介绍的vi命令:%s/pat1/pat2/g中的pat1部分,而Replace输入框对应pat2部分。但是,为了得到vi的执行范围和g选项,你要使用Replace All或者适当的手工Find Next and Replace(译者按:知道为啥有人骂微软弱智了吧,虽然VC中可以选中一个范围的文本,然后在其中执行替换,但是总之不够vi那么灵活和典雅)。

sed

Sed是Stream EDitor的缩写,是Unix下常用的基于文件和管道的编辑工具,可以在手册中得到关于sed的详细信息。

这里是一些有趣的sed脚本,假定我们正在处理一个叫做price.txt的文件。注意这些编辑并不会改变源文件,sed只是处理源文件的每一行并把结果显示在标准输出中(当然很容易使用重定向来定制):

sed脚本 描述


sed 's/^$/d' price.txt删除所有空行
sed 's/^[ \t]*$/d' price.txt删除所有只包含空格或者制表符的行
sed 's/"//g' price.txt删除所有引号

awk

awk是一种编程语言,可以用来对文本数据进行复杂的分析和处理。可以在手册中得到关于awk的详细信息。这个古怪的名字是它作者们的姓的缩写(Aho,Weinberger和Kernighan)。

在Aho,Weinberger和Kernighan的书The AWK Programming Language中有很多很好的awk的例子,请不要让下面这些微不足道的脚本例子限制你对awk强大能力的理解。我们同样假定我们针对price.txt文件进行处理,跟sed一样,awk也只是把结果显示在终端上。 

awk脚本 描述


awk '$0 !~ /^$/' price.txt删除所有空行
awk 'NF > 0' price.txtawk中一个更好的删除所有行的办法
awk '$2 ~ /^[JT]/ {print $3}' price.txt打印所有第二个字段是'J'或者'T'打头的行中的第三个字段
awk '$2 !~ /[Mm]isc/ {print $3 + $4}' price.txt针对所有第二个字段不包含'Misc'或者'misc'的行,打印第3和第4列的和(假定为数字)
awk '$3 !~ /^[0-9]+\.[0-9]*$/ {print $0}' price.txt打印所有第三个字段不是数字的行,这里数字是指d.d或者d这样的形式,其中d是0到9的任何数字
awk '$2 ~ /John|Fred/ {print $0}' price.txt如果第二个字段包含'John'或者'Fred'则打印整行

grep

grep是一个用来在一个或者多个文件或者输入流中使用RE进行查找的程序。它的name编程语言可以用来针对文件和管道进行处理。可以在手册中得到关于grep的完整信息。这个同样古怪的名字来源于vi的一个命令,g/re/p,意思是global regular expression print。

下面的例子中我们假定在文件phone.txt中包含以下的文本,——其格式是姓加一个逗号,然后是名,然后是一个制表符,然后是电话号码:

    Francis, John           5-3871
    Wong, Fred              4-4123
    Jones, Thomas           1-4122
    Salazar, Richard        5-2522

grep命令 描述


grep '\t5-...1' phone.txt把所有电话号码以5开头以1结束的行打印出来,注意制表符是用\t表示的
grep '^S[^ ]* R' phone.txt打印所有姓以S打头和名以R打头的行
grep '^[JW]' phone.txt打印所有姓开头是J或者W的行
grep ', ....\t' phone.txt打印所有姓是4个字符的行,注意制表符是用\t表示的
grep -v '^[JW]' phone.txt打印所有不以J或者W开头的行
grep '^[M-Z]' phone.txt打印所有姓的开头是M到Z之间任一字符的行
grep '^[M-Z].*[12]' phone.txt打印所有姓的开头是M到Z之间任一字符,并且点号号码结尾是1或者2的行

egrep

egrep是grep的一个扩展版本,它在它的正则表达式中支持更多的元字符。下面的例子中我们假定在文件phone.txt中包含以下的文本,——其格式是姓加一个逗号,然后是名,然后是一个制表符,然后是电话号码:
    Francis, John           5-3871
    Wong, Fred              4-4123
    Jones, Thomas           1-4122
    Salazar, Richard        5-2522

egrep command Description


egrep '(John|Fred)' phone.txt打印所有包含名字John或者Fred的行
egrep 'John|22$|^W' phone.txt打印所有包含John 或者以22结束或者以W的行
egrep 'net(work)?s' report.txt从report.txt中找到所有包含networks或者nets的行


正则表达式语法支持情况

命令或环境.[ ]^$\( \)\{ \}?+|( )
vi X  X  X  X  X      
Visual C++ X  X  X  X  X      
awk X  X  X  X    X  X  X  X 
sed X  X  X  X  X  X     
Tcl X  X  X  X  X   X  X  X  X 
ex X  X  X  X  X  X     
grep X  X  X  X  X  X     
egrep X  X X  X  X   X  X  X  X 
fgrep X  X  X  X  X      
perl X X X X X  X X X X

 


vi替换命令简介

Vi的替换命令:
    :ranges/pat1/pat2/g
其中
    : 这是Vi的命令执行界面。
    range 是命令执行范围的指定,可以使用百分号(%)表示所有行,使用点(.)表示当前行,使用美元符号($)表示最后一行。你还可以使用行号,例如10,20表示第10到20行,.,$表示当前行到最后一行,.+2,$-5表示当前行后两行直到全文的倒数第五行,等等。

    s 表示其后是一个替换命令。

    pat1 这是要查找的一个正则表达式,这篇文章中有一大堆例子。

    pat2 这是希望把匹配串变成的模式的正则表达式,这篇文章中有一大堆例子。

    g 可选标志,带这个标志表示替换将针对行中每个匹配的串进行,否则则只替换行中第一个匹配串。

网上有很多vi的在线手册,你可以访问他们以获得更加完整的信息。


[回到主页]

posted @ 2006-10-13 23:53 CoderDream 阅读(134) | 评论 (0)编辑 收藏

       正则表达式(regular expression)对象包含一个正则表达式模式(pattern)。它具有用正则表达式模式去匹配或代替一个串(string)中特定字符(或字符集合)的属性(properties)和方法(methods)。 要为一个单独的正则表达式添加属性,可以使用正则表达式构造函数(constructor function),无论何时被调用的预设置的正则表达式拥有静态的属性(the predefined RegExp object has static properties that are set whenever any regular expression is used, 我不知道我翻得对不对,将原文列出,请自行翻译)。

  • 创建:
    一个文本格式或正则表达式构造函数
    文本格式: /pattern/flags
    正则表达式构造函数: new RegExp("pattern"[,"flags"]);
  • 参数说明:
    pattern -- 一个正则表达式文本
    flags -- 如果存在,将是以下值:
    g: 全局匹配
    i: 忽略大小写
    gi: 以上组合

[注意] 文本格式的参数不用引号,而在用构造函数时的参数需要引号。如:/ab+c/i new RegExp("ab+c","i")是实现一样的功能。在构造函数中,一些特殊字符需要进行转意(在特殊字符前加"\")。如:re = new RegExp("\\w+")

正则表达式中的特殊字符

字符 含意
\

做为转意,即通常在"\"后面的字符不按原来意义解释,如/b/匹配字符"b",当b前面加了反斜杆后/\b/,转意为匹配一个单词的边界。
-或-
对正则表达式功能字符的还原,如"*"匹配它前面元字符0次或多次,/a*/将匹配a,aa,aaa,加了"\"后,/a\*/将只匹配"a*"。

^ 匹配一个输入或一行的开头,/^a/匹配"an A",而不匹配"An a"
$ 匹配一个输入或一行的结尾,/a$/匹配"An a",而不匹配"an A"
* 匹配前面元字符0次或多次,/ba*/将匹配b,ba,baa,baaa
+ 匹配前面元字符1次或多次,/ba*/将匹配ba,baa,baaa
? 匹配前面元字符0次或1次,/ba*/将匹配b,ba
(x) 匹配x保存x在名为$1...$9的变量中
x|y 匹配x或y
{n} 精确匹配n次
{n,} 匹配n次以上
{n,m} 匹配n-m次
[xyz] 字符集(character set),匹配这个集合中的任一一个字符(或元字符)
[^xyz] 不匹配这个集合中的任何一个字符
[\b] 匹配一个退格符
\b 匹配一个单词的边界
\B 匹配一个单词的非边界
\cX 这儿,X是一个控制符,/\cM/匹配Ctrl-M
\d 匹配一个字数字符,/\d/ = /[0-9]/
\D 匹配一个非字数字符,/\D/ = /[^0-9]/
\n 匹配一个换行符
\r 匹配一个回车符
\s 匹配一个空白字符,包括\n,\r,\f,\t,\v等
\S 匹配一个非空白字符,等于/[^\n\f\r\t\v]/
\t 匹配一个制表符
\v 匹配一个重直制表符
\w 匹配一个可以组成单词的字符(alphanumeric,这是我的意译,含数字),包括下划线,如[\w]匹配"$5.98"中的5,等于[a-zA-Z0-9]
\W 匹配一个不可以组成单词的字符,如[\W]匹配"$5.98"中的$,等于[^a-zA-Z0-9]。

说了这么多了,我们来看一些正则表达式的实际应用的例子:

E-mail地址验证:
 function test_email(strEmail) {
  var myReg = /^[_a-z0-9]+@([_a-z0-9]+\.)+[a-z0-9]{2,3}$/;
  if(myReg.test(strEmail)) return true;
  return false;
 }
HTML代码的屏蔽
 function mask_HTMLCode(strInput) {
   var myReg = /<(\w+)>/;
   return strInput.replace(myReg, "&lt;$1&gt;");
 }

正则表达式对象的属性及方法
  预定义的正则表达式拥有有以下静态属性:input, multiline, lastMatch, lastParen, leftContext, rightContext和$1到$9。其中input和multiline可以预设置。其他属性的值在执行过exec或test方法后被根据不同条件赋以不同的值。许多属性同时拥有长和短(perl风格)的两个名字,并且,这两个名字指向同一个值。(JavaScript模拟perl的正则表达式)
正则表达式对象的属性
属性含义
$1...$9如果它(们)存在,是匹配到的子串
$_参见input
$*参见multiline
$&参见lastMatch
$+参见lastParen
$`参见leftContext
$’         参见rightContext
constructor   创建一个对象的一个特殊的函数原型
global      是否在整个串中匹配(bool型)
ignoreCase    匹配时是否忽略大小写(bool型)
input       被匹配的串
lastIndex    最后一次匹配的索引
lastParen    最后一个括号括起来的子串
leftContext   最近一次匹配以左的子串
multiline    是否进行多行匹配(bool型)
prototype    允许附加属性给对象
rightContext   最近一次匹配以右的子串
source      正则表达式模式
lastIndex    最后一次匹配的索引

正则表达式对象的方法

方法含义
compile     正则表达式比较
exec       执行查找
test       进行匹配
toSource     返回特定对象的定义(literal representing),其值可用来创建一个新的对象。重载Object.toSource方法得到的。
toString     返回特定对象的串。重载Object.toString方法得到的。
valueOf     返回特定对象的原始值。重载Object.valueOf方法得到

例子
<script language = "JavaScript">
var myReg = /(\w+)\s(\w+)/;
var str  = "John Smith";
var newstr = str.replace(myReg, "$2, $1");
document.write(newstr);
</script>
将输出"Smith, John"
posted @ 2006-10-13 23:49 CoderDream 阅读(67) | 评论 (0)编辑 收藏

<2006年10月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

常用链接

留言簿(5)

我参与的团队

随笔分类(231)

随笔档案(229)

文章分类(3)

文章档案(3)

收藏夹(440)

友情链接

搜索

  •  

积分与排名

  • 积分 - 60371
  • 排名 - 170

最新评论

阅读排行榜

评论排行榜