circlesport

  博客园 :: 首页 :: 联系 :: 订阅 订阅 :: 管理
  49 Posts :: 1 Stories :: 28 Comments :: 0 Trackbacks

公告

2007年7月23日 #

语法:??,*?,+?,{n}?,{n,m}?

  涵义:简单说,后面的这个?(lazy)告诉正则引擎,它前面的表达式匹配到最短的匹配项就不用匹配下去了,如??,?本身匹配0-1个匹配项,那么??就取最短的,匹配0个项就不匹配下去了,同理,*?匹配0个,+?匹配1个,{n}?匹配n个,{n,m}?匹配n个。当用@”\w*?”匹配”abcd”时,会有五次成功匹配,每次都匹配的结果都是空字符串为什么会是5次呢,这是因为正则引擎在匹配一个表达式时是一个字符一个字符对比下去的,每成功匹配一次,就前进一下。

  判断表达式

  语法:

  1、A|B,这个是最基本的,A或者B,其实这个不能算判断

  2、(?(expression)yes-expression|no-expression),其中no-expression为可选项,意为,如果expression成立,则要求匹配yes-expression,否则要求匹配no-expression

  3、(?(group-name)yes-expressioin|no-expression),其中no-expression为可选项,意为,如果名为group-name的组匹配成功,则要求匹配yes-expression,否则要求匹配no-expression

  判断表达式还是很好理解的,唯有一点要注意:@"(?(A)A|B)"不能匹配"AA",为什么呢?要怎么样写才能匹配呢,大家先想想……

  我们应该这样写Regex: @”(?(A)AA|B)”,请注意,判断式中的内容并不会做为yes-expression或no-expression表达式的一部分。

  .net 的正则引擎工作特点

  .net的正则引擎工作方式大多数和我们“想当然”的方式一样,只是有几点要注意:

  1、.NET Framework 正则表达式引擎尽可能的匹配多的字符(贪婪)。正是由于这一点,所以,不要用@"<.*>(.*)</.*>"这样的正则式来试图找出一个HTML文档中的所有innerText。(我也正是在网上看到有人这样写正则式才决定要写《正则表达式 高级技巧》的,呵呵)

  2、.NET Framework 正则表达式引擎是回溯的正则表达式匹配器,它并入了传统的非确定性有限自动机 (NFA) 引擎(例如 Perl、Python使用的引擎)。这使其有别于更快的、但功能更有限的纯正则表达式确定性有限自动机 (DFA) 引擎。.NET Framework 正则表达式引擎尽量匹配成功,所以,当@"\w+\.(.*)\.\w+"中的.*把www. .csdn.net中的.csdn.net都匹配完了,让后面的\.\w+没得字符去匹配时,引擎会进行回溯,以得到成功的匹配。
 
  NET Framework 正则表达式引擎还包括了一组完整的语法,让程序员能够操纵回溯引擎。包括:

  “惰性”限定符:??、*?、+?、{n,m}?。这些惰性限定符指示回溯引擎首先搜索最少数目的重复。与之相反,普通的“贪婪的”限定符首先尝试匹配最大数目的重复。

  从右到左匹配。这在从右到左而非从左到右搜索的情况下十分有用,或者在从模式的右侧部分开始搜索比从模式的左侧部分开始搜索更为有效的情况下十分有用。

  3、.NET Framework 正则表达式引擎在(expression1|expression2|expression3)这样情况下,expression1总是最先得到尝试,再依次是expression2和expression3

publicstaticvoidMain()
{
strings="THINisaasp.netdeveloper.";
Regexreg=newRegex(@"(\w{2}|\w{3}|\w{4})",RegexOptions.Compiled|RegexOptions.IgnoreCase);
MatchCollectionmc=reg.Matches(s);
foreach(Matchminmc)
Console.WriteLine(m.Value);
Console.ReadLine();
}

  输出结果是: ‘TH’ ‘IN’ ‘is’ ‘as’ ‘ne’ ‘de’ ‘ve’ ‘lo’ ‘pe’

  附表

转义符 说明
一般字符 除 .$ ^ { [ ( | ) * + ? \ 外,其他字符与自身匹配。
\a 与响铃(警报)\u0007 匹配。
\b 在正则表达式中,\b 表示单词边界(在 \w 和 \W 之间),不过,在 [] 字符类中,\b 表示退格符。在替换模式中,\b 始终表示退格符。
\t 与 Tab 符 \u0009 匹配。
\r 与回车符 \u000D 匹配。
\v 与垂直 Tab 符 \u000B 匹配。
\f 与换页符 \u000C 匹配。
\n 与换行符 \u000A 匹配。
\e 与 Esc 符 \u001B 匹配。
\040 将 ASCII 字符匹配为八进制数(最多三位);如果没有前导零的数字只有一位数或者与捕获组号相对应,则该数字为后向引用。例如,字符\040 表示空格。
\x20 使用十六进制表示形式(恰好两位)与 ASCII 字符匹配。
\cC 与 ASCII 控制字符匹配;例如,\cC 为 Ctrl-C。
\u0020 使用十六进制表示形式(恰好四位)与 Unicode 字符匹配。
\ 在后面带有不识别为转义符的字符时,与该字符匹配。例如,\* 与 \x2A 相同。
字符类 说明
. 匹配除 \n 以外的任何字符。如果已用 Singleline 选项做过修改,则句点字符可与任何字符匹配。
[ aeiou ] 与指定字符集中包含的任何单个字符匹配。
[^ aeiou ] 与不在指定字符集中的任何单个字符匹配。
[0-9a-fA-F] 使用连字号 (–) 允许指定连续字符范围。
\p{ name }

与 {name} 指定的命名字符类中的任何字符都匹配。支持的名称为 Unicode 组和块范围。例如,Ll、Nd、Z、IsGreek、IsBoxDrawing。可以使用 GetUnicodeCategory 方法找到某个字符所属的 Unicode 类别。

\P{ name } 与在 {name} 中指定的组和块范围不包括的文本匹配。
\w 与任何单词字符匹配。等效于 Unicode 字符类别 [\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}\p{Pc}\p{Lm}]。如果用 ECMAScript 选项指定了符合 ECMAScript 的行为,则 \w 等效于 [a-zA-Z_0-9]。
\W 与任何非单词字符匹配。等效于 Unicode 字符类别 [^\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}\p{Pc}\p{Lm}]。如果用 ECMAScript 选项指定了符合 ECMAScript 的行为,则 \W 等效于 [^a-zA-Z_0-9]。
\s 与任何空白字符匹配。等效于 Unicode 字符类别 [\f\n\r\t\v\x85\p{Z}]。如果用 ECMAScript 选项指定了符合 ECMAScript 的行为,则 \s 等效于 [ \f\n\r\t\v]。
\S 与任何非空白字符匹配。等效于 Unicode 字符类别 [^\f\n\r\t\v\x85\p{Z}]。如果用 ECMAScript 选项指定了符合 ECMAScript 的行为,则 \S 等效于 [^ \f\n\r\t\v]。
\d 与任何十进制数字匹配。对于 Unicode 类别的 ECMAScript 行为,等效于 \p{Nd},对于非 Unicode 类别的 ECMAScript 行为,等效于 [0-9]。
\D 与任何非数字匹配。对于 Unicode 类别的 ECMAScript 行为,等效于 \P{Nd},对于非 Unicode 类别的 ECMAScript 行为,等效于 [^0-9]。
断言 说明
>^ 指定匹配必须出现在字符串的开头或行的开头。
$ 指定匹配必须出现在以下位置:字符串结尾、字符串结尾处的 \n 之前或行的结尾。
\A 指定匹配必须出现在字符串的开头(忽略 Multiline 选项)。
\Z 指定匹配必须出现在字符串的结尾或字符串结尾处的 \n 之前(忽略 Multiline 选项)。
\z 指定匹配必须出现在字符串的结尾(忽略 Multiline 选项)。
\G 指定匹配必须出现在上一个匹配结束的地方。与 Match.NextMatch() 一起使用时,此断言确保所有匹配都是连续的。
\b 指定匹配必须出现在 \w(字母数字)和 \W(非字母数字)字符之间的边界上。匹配必须出现在单词边界上,即出现在由任何非字母数字字符分隔的单词中第一个或最后一个字符上。
\B 指定匹配不得出现在 \b 边界上。
限定符 说明
* 指定零个或更多个匹配;例如 \w* 或 (abc)*。等效于 {0,}。
+ 指定一个或多个匹配;例如 \w+ 或 (abc)+。等效于 {1,}。
? 指定零个或一个匹配;例如 \w? 或 (abc)?。等效于 {0,1}。
{ n } 指定恰好 n 个匹配;例如 (pizza){2}。
{ n ,} 指定至少 n 个匹配;例如 (abc){2,}。
{ n , m } 指定至少 n 个但不多于 m 个匹配。
*? 指定尽可能少地使用重复的第一个匹配(等效于 lazy *)。
+? 指定尽可能少地使用重复但至少使用一次(等效于 lazy +)。
?? 指定使用零次重复(如有可能)或一次重复 (lazy ?)。
{ n }? 等效于 {n} (lazy {n})。
{ n ,}? 指定尽可能少地使用重复但至少使用 n 次 (lazy {n,})。
{ n , m }? 指定介于 n 次和 m 次之间、尽可能少地使用重复 (lazy {n,m})。


--完--
posted @ 2007-07-23 11:22 无极.net 阅读(165) 评论(0) 编辑

反向引用,指把匹配出来的组引用到表达式本身其它地方,比如,在匹配HTML的标记时,我们匹配出一个<a>,我们要把匹配出来的a引用出来,用来找到</a>,这个时候就要用到反向引用。

  语法

  a、反向引用编号的组,语法为\number

  b、反向引用命名的组,语法为\k<name>

  举例

  a、匹配成对的HTML标签

@"<(?<tag>[^\s>]+)[^>]*>.*</\k<tag>>"

  b、匹配两个两个重叠出现的字符

public static void Main()
{
string s = "aabbc11asd";
Regex reg = new Regex(@"(\w)\1");
MatchCollection matches = reg.Matches(s);
foreach(Match m in matches)
Console.WriteLine(m.Value);
Console.ReadLine();
}

  返回结果为aa bb 11

  辅助匹配组

  以下几种组结构,括号中的Pattern都不作为匹配结果的一部分进行保存

  1、正声明(?=)

  涵义:括号中的模式必须出现在声明右侧,但不作为匹配的一部分

public static void Main()
{
string s = "C#.net,VB.net,PHP,Java,JScript.net";
Regex reg = new Regex(@"[\w\#]+(?=\.net)",RegexOptions.Compiled);
MatchCollection mc = reg.Matches(s);
foreach(Match m in mc)
Console.WriteLine(m.Value);
Console.ReadLine();
//输出 C# VB JScript
}

  可以看到匹配引擎要求匹配.net,但却不把.net放到匹配结果中

  2、负声明(?!)

  涵义:括号中的模式必须不出现在声明右侧

  下例演示如何取得一个<a>标签对中的全部内容,即使其中包含别的HTML tag。

public static void Main()
{
string newsContent = @"url:<a href=""1.html""><img src=""1.gif"">test<span style=""color:red;"">Regex</span></a>.";
Regex regEnd = new Regex(@"<\s*a[^>]*>([^<]|<(?!/a))*<\s*/a\s*>",RegexOptions.Multiline);

Console.WriteLine(regEnd.Match(newsContent).Value);
//Result: <a href="1.html"><img src="1.gif">test<span style="color:red;">Regex</span></a>
Console.ReadLine();
}

  3、反向正声明(?<=)

  涵义:括号中的模式必须出现在声明左侧,但不作为匹配的一部分

  4、反向负声明(?<!)

  涵义:括号中的模式必须不出现在声明左侧

  非回溯匹配

  语法:(?>)

  涵义:该组匹配后,其匹配的字符不能通过回溯用于后面的表达式的匹配。呵呵,光看这句话肯定搞不懂,我当初为了搞懂这个也花了不少的时间,还是通过实例来说明吧:
"www.csdn.net" 可以通过@"\w+\.(.*)\.\w+"来匹配,却不能通过@"\w+\.(?>.*)\.\w+"来匹配!为什么呢?

  原因是正则匹配是贪婪的,匹配时它会尽可能多的匹配最多的结果,所以,上例两个正则式中的.*都会把csdn.net匹配完, 这个时候,第一个表达式在开始匹配时发现\.\w+没得字符给它匹配了,所以它会进行回溯,所谓回溯,就是把.*匹配的结果往回推,回推留出来的字符再用来匹配\.\w+,直到\.\w+匹配成功,整个表达式返回成功的匹配结果。而第二个表达式,因使用的是非回溯匹配,所以,.*匹配完后,不允许通过回溯来匹配\.\w+,所以整个表达式匹配失败。

  请注意,回溯匹配是很浪费资源的一种匹配方式,所以,请尽量避免您的正则式要通过回溯来成功匹配,如上例,可以换成@"\w+\.([^\.]+\.)+\w+"+"。
posted @ 2007-07-23 11:15 无极.net 阅读(144) 评论(0) 编辑

正则表达式中的组是很重要的一个概念,它是我们通向高级正则应用的的桥梁。

  组的概念

  一个正则表达式匹配结果可以分成多个部分,这就是组(Group)的目的。能够灵活的使用组后,你会发现Regex真是很方便,也很强大。

  先举个例子

public static void Main()
{
 string s = "2005-2-21";
 Regex reg = new Regex(@"(?<y>\d{4})-(?<m>\d{1,2})-(?<d>\d{1,2})",RegexOptions.Compiled);
 Match match = reg.Match(s);
 int year = int.Parse(match.Groups["y"].Value);
 int month = int.Parse(match.Groups["m"].Value);
 int day = int .Parse(match.Groups["d"].Value);
 DateTime time = new DateTime(year,month,day);
 Console.WriteLine(time);
 Console.ReadLine();
}

  以上的例子通过组来实现分析一个字符串,并把其转化为一个DateTime实例,当然,这个功能用DateTime.Parse方法就能很方便的实现。

  在这个例子中,我把一次Match结果用(?<name>)的方式分成三个组"y","m","d"分别代表年、月、日。

  现在我们已经有了组的概念了,再来看如何分组,很简单的,除了上在的办法,我们可以用一对括号就定义出一个组,比如上例可以改成:

public static void Main()
{
 string s = "2005-2-21";
 Regex reg = new Regex(@"(\d{4})-(\d{1,2})-(\d{1,2})",RegexOptions.Compiled);
 Match match = reg.Match(s);
 int year = int.Parse(match.Groups[1].Value);
 int month = int.Parse(match.Groups[2].Value);
 int day = int .Parse(match.Groups[3].Value);
 DateTime time = new DateTime(year,month,day);
 Console.WriteLine(time);
 Console.ReadLine();
}

  从上例可以看出,第一个括号对包涵的组被自动编号为1,后面的括号依次编号为2、3……

public static void Main()
{
 string s = "2005-2-21";
 Regex reg = new Regex(@"(?<2>\d{4})-(?<1>\d{1,2})-(?<3>\d{1,2})",RegexOptions.Compiled);
 Match match = reg.Match(s);
 int year = int.Parse(match.Groups[2].Value);
 int month = int.Parse(match.Groups[1].Value);
 int day = int .Parse(match.Groups[3].Value);
 DateTime time = new DateTime(year,month,day);
 Console.WriteLine(time);
 Console.ReadLine();
}

  再看上例,我们用(?<数字>)的方式手工给每个括号对的组编号,(注意我定义1和2的位置时不是从左到右定义的)

  通过以上三例,我们知道了给Regex定义Group的三种办法以及相应的引用组匹配结果的方式。

  然后,关于组定义,还有两点请注意:

  1、因为括号用于定义组了,所以如果要匹配"("和")",请使用"\("和"\)"(关于所有特殊字符的定义,请查看相关Regex expression帮助文档)。

  2、如果定义Regex时,使用了ExplicitCapture选项,则第二个例子不会成功,因为此选项要求显式定义了编号或名字的组才捕获并保存结果,如果你没有定义ExplicitCapture选项,而有时又定义了类式于(A|B)这样的部分在表达式,而这个(A|B)你又并不想捕获结果,那么可以使用“不捕获的组”语法,即定义成(?:)的方式,针对于(A|B),你可以这样来定义以达到不捕获并保存它到Group集合中的目的--(?:A|B)。
posted @ 2007-07-23 11:10 无极.net 阅读(92) 评论(0) 编辑

因为.net的基本正则语法和Perl5基本相同,所以基本语法你可以去下载一下M$的JS帮助文档,上面有详细的说明\d表示什么,{,5}表示什么,\[表示什么……,这里我只想提醒大家一点,为了避免和反向引用相冲突,在你用\nn表示八进制的ASCII码时,请在\后加0,就是说,\40在表示ASCII码时,请这样写\040。

  替换

  Regex类有一个静态的Replace方法,其实例也有一个Replace方法,这个方法很强大,因为它可以传入一个delegate,这样,你可以自定义每次捕获匹配时,如何处理捕获的内容。

public static void Main()
{
string s = "1 12 3 5";
s = Regex.Replace(s,@"\d+",new MatchEvaluator(CorrectString),RegexOptions.Compiled|RegexOptions.IgnoreCase);
Console.WriteLine(s);
Console.ReadLine();
}
private static string CorrectString(Match match)
{
string matchValue = match.Value;
if(matchValue.Length == 1)
matchValue = "0" + matchValue;
return matchValue;
}

  以上这段代码说明了如果使用delegate MatchEvaluator 来处理正则的Match结果,该代码返回"01 12 03 05"。Replace方法除了使用delegate来处理捕获的Match,还可以用字符串来替换Match的结果,而用字符串来替换Match结果除了把Match结果静态的替换成一个固定的文本外,还可以使用以下语法来更方便的实现你需要的功能:


$number   把匹配的第number组替换成替换表达式,还有这句话怎么写也表达不清楚意思,还是来个例子吧:

public static void Main()
{
string s = "1 12 3 5";
s = Regex.Replace(s,@"(\d+)(?#这个是注释)","0$1",RegexOptions.Compiled|RegexOptions.IgnoreCase);
Console.WriteLine(s);
Console.ReadLine();
}

  这段代码返回的是 “01 012 03 05”

  就是说,对组一的每个匹配结果都用"0$1"这个表达式来替换,"0$1"中"$1"由组1匹配的结果代入
${name}   把匹配的组名为"name"的组替换成表达式,

  上例的Regex expression改成@"(?<name>\d+)(?#这个是注释)"后面的替换式改为"0${name}"结果是一样的
$$   做$的转义符,如上例表达式改成@"(?<name>\d+)(?#这个是注释)"和"$$${name}",则结果为"$1 $12 $3 $5"
$&   替换整个匹配
$`   替换匹配前的字符
$'   替换匹配后的字符
$+   替换最后匹配的组
$_   替换整个字符串
  
  后面的选项,大家自己写个例子体味一下。

  *注,上例中的(?#这个是注释)说明了正则的内联注释语法为(?#)

  表达项选项

  正则表达式选项RegexOptions有如下一下选项,详细说明请参考联机帮助

RegexOptions枚举值 内联标志 简单说明
ExplicitCapture n 只有定义了命名或编号的组才捕获
IgnoreCase i 不区分大小写
IgnorePatternWhitespace x 消除模式中的非转义空白并启用由 # 标记的注释。
MultiLine m 多行模式,其原理是修改了^和$的含义
SingleLine s 单行模式,和MultiLine相对应
  
  这里我提到内联标志,是因为相对于用RegexOptions在new Regex时定义Regex表达式的全局选项来说,内联标志可以更小粒度(以组为单位)的定义匹配选项,从而更方便表达我们的思想

  语法是这样的:(?i:expression)为定义一个选项,(?-i:expression)为删除一个选项,(?i-s:expression)则定义i,删除s,是的,我们可以一次定义很多个选项。这样,通过内联选项,你就可以在一个Regex中定义一个组为匹分大小写的,一个组不匹分大小写的,是不是很方便呢?

posted @ 2007-07-23 11:04 无极.net 阅读(395) 评论(0) 编辑

摘要:本文给出了在C#下利用正则表达式实现字符串搜索功能的方法,通过对.NET框架下的正则表达式的研究及实例分析,总结了正则表达式的元字符、规则、选项等。

  关键字:正则表达式、元字符、字符串、匹配


  1、正则表达式简介

  正则表达式提供了功能强大、灵活而又高效的方法来处理文本。正则表达式的全面模式匹配表示法可以快速地分析大量的文本以找到特定的字符模式;提取、编辑、替换或删除文本子字符串;或将提取的字符串添加到集合以生成报告。对于处理字符串(例如 HTML 处理、日志文件分析和 HTTP 标头分析)的许多应用程序而言,正则表达式是不可缺少的工具。

  .NET 框架正则表达式并入了其他正则表达式实现的最常见功能,被设计为与 Perl 5 正则表达式兼容,.NET 框架正则表达式还包括一些在其他实现中尚未提供的功能,.NET 框架正则表达式类是基类库的一部分,并且可以和面向公共语言运行库的任何语言或工具一起使用。

  2、字符串搜索

  正则表达式语言由两种基本字符类型组成:原义(正常)文本字符和元字符。正是元字符组为正则表达式提供了处理能力。当前,所有的文本编辑器都有一些搜索功能,通常可以打开一个对话框,在其中的一个文本框中键入要定位的字符串,如果还要同时进行替换操作,可以键入一个替换字符串,比如在Windows操作系统中的记事本、Office系列中的文档编辑器都有这种功能。这种搜索最简单的方式,这类问题很容易用String类的String.Replace()方法来解决,但如果需要在文档中识别某个重复的,该怎么办?编写一个例程,从一个String类中选择重复的字是比较复杂的,此时使用语言就很适合。

  一般表达式语言是一种可以编写搜索表达式的语言。在该语言中,可以把文档中要搜索的文本、转义序列和特定含义的其他字符组合在一起,例如序列\b表示一个字的开头和结尾(子的边界),如果要表示正在查找的以字符th开头的字,就可以编写一般表达式\bth(即序列字符界是-t-h)。如果要搜索所有以th结尾的字,就可以编写th\b(序列t-h-字边界)。但是,一般表达式要比这复杂得多,例如,可以在搜索操作中找到存储部分文本的工具性程序(facility)。

  3、.NET 框架的正则表达式类

  下面通过介绍 .NET 框架的正则表达式类,熟悉一下.NET框架下的正则表达式的使用方法。

  3.1 Regex 类表示只读正则表达式

  Regex 类包含各种静态方法,允许在不显式实例化其他类的对象的情况下使用其他正则表达式类。以下代码示例创建了 Regex 类的实例并在初始化对象时定义一个简单的正则表达式。请注意,使用了附加的反斜杠作为转义字符,它将 \s 匹配字符类中的反斜杠指定为原义字符。

Regex r; // 声明一个 Regex类的变量
r = new Regex("\\s2000"); // 定义表达式

  3.2 Match 类表示正则表达式匹配操作的结果

  以下示例使用 Regex 类的 Match 方法返回 Match 类型的对象,以便找到输入字符串中第一个匹配。此示例使用 Match 类的 Match.Success 属性来指示是否已找到匹配。

Regex r = new Regex("abc"); // 定义一个Regex对象实例
Match m = r.Match("123abc456"); // 在字符串中匹配
if (m.Success)
{
 Console.WriteLine("Found match at position " + m.Index); //输入匹配字符的位置
}

  3.3 MatchCollection 类表示非重叠匹配的序列

  该集合为只读的,并且没有公共构造函数。MatchCollection 的实例是由 Regex.Matches 属性返回的。使用 Regex 类的 Matches 方法,通过在输入字符串中找到的所有匹配填充 MatchCollection。下面代码示例演示了如何将集合复制到一个字符串数组(保留每一匹配)和一个整数数组(指示每一匹配的位置)中。

MatchCollection mc;
String[] results = new String[20];
int[] matchposition = new int[20];
Regex r = new Regex("abc"); //定义一个Regex对象实例
mc = r.Matches("123abc4abcd");
for (int i = 0; i < mc.Count; i++) //在输入字符串中找到所有匹配
{
 results[i] = mc[i].Value; //将匹配的字符串添在字符串数组中
 matchposition[i] = mc[i].Index; //记录匹配字符的位置
}

  3.4 GroupCollection 类表示捕获的组的集合

  该集合为只读的,并且没有公共构造函数。GroupCollection 的实例在 Match.Groups 属性返回的集合中返回。下面的控制台应用程序查找并输出由正则表达式捕获的组的数目。

using System;
using System.Text.RegularExpressions;
public class RegexTest
{
 public static void RunTest()
 {
  Regex r = new Regex("(a(b))c"); //定义组
  Match m = r.Match("abdabc");
  Console.WriteLine("Number of groups found = " + m.Groups.Count);
 }
 public static void Main()
 {
  RunTest();
 }
}

  该示例产生下面的输出:

Number of groups found = 3

  3.5 CaptureCollection 类表示捕获的子字符串的序列

  由于限定符,捕获组可以在单个匹配中捕获多个字符串。Captures属性(CaptureCollection 类的对象)是作为 Match 和 group 类的成员提供的,以便于对捕获的子字符串的集合的访问。例如,如果使用正则表达式 ((a(b))c)+(其中 + 限定符指定一个或多个匹配)从字符串"abcabcabc"中捕获匹配,则子字符串的每一匹配的 Group 的 CaptureCollection 将包含三个成员。

  下面的程序使用正则表达式 (Abc)+来查找字符串"XYZAbcAbcAbcXYZAbcAb"中的一个或多个匹配,阐释了使用 Captures 属性来返回多组捕获的子字符串。

using System;
using System.Text.RegularExpressions;
public class RegexTest
{
 public static void RunTest()
 {
  int counter;
  Match m;
  CaptureCollection cc;
  GroupCollection gc;
  Regex r = new Regex("(Abc)+"); //查找"Abc"
  m = r.Match("XYZAbcAbcAbcXYZAbcAb"); //设定要查找的字符串
  gc = m.Groups;
  //输出查找组的数目
  Console.WriteLine("Captured groups = " + gc.Count.ToString());
  // Loop through each group.
  for (int i=0; i < gc.Count; i++) //查找每一个组
  {
   cc = gc[i].Captures;
   counter = cc.Count;
   Console.WriteLine("Captures count = " + counter.ToString());
   for (int ii = 0; ii < counter; ii++)
   {
    // Print capture and position.
    Console.WriteLine(cc[ii] + " Starts at character " +
    cc[ii].Index); //输入捕获位置
   }
  }
 }
 public static void Main() {
  RunTest();
 }
}

  此例返回下面的输出结果:

Captured groups = 2
Captures count = 1
AbcAbcAbc Starts at character 3
Captures count = 3
Abc Starts at character 3
Abc Starts at character 6
Abc Starts at character 9

  3.6 Capture 类包含来自单个子表达式捕获的结果

  在 Group 集合中循环,从 Group 的每一成员中提取 Capture 集合,并且将变量 posn 和 length 分别分配给找到每一字符串的初始字符串中的字符位置,以及每一字符串的长度。

Regex r;
Match m;
CaptureCollection cc;
int posn, length;
r = new Regex("(abc)*");
m = r.Match("bcabcabc");
for (int i=0; m.Groups[i].Value != ""; i++)
{
 cc = m.Groups[i].Captures;
 for (int j = 0; j < cc.Count; j++)
 {
  posn = cc[j].Index; //捕获对象位置
  length = cc[j].Length; //捕获对象长度
 }
}


图1:对象关系

  把组合字符组合起来后,每次都会返回一个组对象,就可能并不是我们希望的结果。如果希望把组合字符作为搜索模式的一部分,就会有相当大的系统开销。对于单个的组,可以用以字符序列"?:"开头的组禁止这么做,就像URI样例那样。而对于所有的组,可以在RegEx.Matches()方法上指定RegExOptions.ExplicitCapture标志。

4、利用正则表达式实现字符串搜索

  4.1 在C#中使用.NET一般表达式引擎

  下面将通过一个样例的开发,执行并显示一些搜索的结果,说明一般表达式的一些特性,以及如何在C#中使用.NET一般表达式引擎。说明使用字符串时应在前面加上符号@。

String Text=@"I can not find my position in Beijing";

  把这个文本称为输入字符串,为了说明一般表达式.NET类,本文先进行一次纯文本的搜索,这次搜索不带任何转义序列或一般表达式命令。假定要查找所有字符串ion,把这个搜索字符串称为模式。使用一般表达式和上面声明的变量Text,编写出下面的代码:

String Pattern = "ion";
MatchCollection Matches = Regex.Matches(Text,Pattern,RegexOptions);
foreach(Match NextMatch in Matches)
{ Console.WriteLine(NextMatch.Index); }

  在这段代码中,使用了System.Text.RegularExpressions名称空间中Regex类的静态方法Match()。这个方法的参数是一些输入文本、一个模式和RegexOptions每句中的一组可选标志。Matches()返回MatchCollection,每个匹配都用一个Match对象来表示。在上面的代码中,只是在集合中迭代,使用Match类的Index属性,返回输入文本中匹配所在的索引。运行这段代码,将得到1个匹配项。

  一般集合的功能主要取决于模式字符串。原因是模式字符串不仅仅包含纯文本。如前所述。还包含元字符和转义序列,元字符是给出命令的特殊字符,而转义序列的工作方式与C#的转义序列相同,它们都是以反斜杠\开头的字符,具有特殊的含义。例如,假定要查找以n开头的字,就可以使用转义序列\b,它表示一个字的边界(字的边界是以某个字母数字标的字符开头,或者后面是一个空白字符或标点符号),下面编写如下代码:

String Pattern = @"\bn";
MatchCollection Matches = Regex.Matches(Text,Pattern,RegexOptions.IgnoreCase|
RegexOptions.ExplicitCapture);

  要在运行时把\b传递给.NET一般表达式引擎,反斜杠\不应被C#编译器解释为转义序列。如果要查找以序列ion结尾的字,可以使用下面的代码:

String Pattern = @"ion\b";

  如果要查找以字母n开头,以序列ion结尾的所有字,需要一个以\bn开头,以ion\b结尾的模式,中间内容怎么办?需要告诉计算机n和ion中间的内容可以是任意长度的字符,只要字符不是空白即可,正确的模式如下所示:

String Pattern = @"\bn\S*ion\b";

  4.2 特定字符或转义序列

  大多数重要的正则表达式语言运算符都是非转义的单个字符。转义符 \(单个反斜杠)通知正则表达式分析器反斜杠后面的字符不是运算符。例如,分析器将星号 (*) 视为重复限定符,而将后跟星号的反斜杠 (\*) 视为 Unicode 字符 002A。

  使用一般表达式要习惯的一点是,查看像这样怪异的字符序列,但这个序列的工作是非常逻辑化的。转义序列\S表示任何不适空白的字符。*称为数量词,其含义是前面的字符可以重复任意次,包括0次。序列\S*表示任何不适空白的字符。因此,上面的模式匹配于以n开头,以ion结尾的任何单个字。下表中列出的字符转义在正则表达式和替换模式中都会被识别。

  表1:特定字符或转义序列

特定字符或转义序列 含义 样例 匹配的样例
^ 输入文本的开头 ^B B,但只能是文本中的第一个字符
$ 输入文本的结尾 X$ X,但只能是文本中的最后一个字符
. 除了换行字符(\n)以外的所有单个字符 i.ation isation、ization
* 可以重复0次或多次的前导字符 ra*t rat、raat等
+ 可以重复1次或多次的前导字符 ra+t rt、rat、raat等
可以重复0次或1次的前导字符 ra?t 只有rt和rat匹配
\s 任何空白字符 \sa [space]a,\ta,\na(\t和\n与C#的\t和\n含义相同)
\S 任何不是空白的字符 \SF aF,rF,cF,但不能是\tf
\b 字边界 ion\b 以ion结尾的任何字
\B 不是字边界的位置 \BX\B 字中间的任何X

  如果要搜索一个元字符,也可以通过带有反斜杠的转义字符来表示。例如,.表示除了换行字符以外的任何字符,而\.表示一个点。
可以把可替换的字符放在方括号中,请求匹配包含这些字符。例如,[1|c]表示字符可以是1或者是c。如果要搜索map或者man,可以使用序列"ma[n|p]"(仅指引号内字符,下面雷同)。在方括号中,也可以制定一个范围,例如"[a-z]"表示所有的小写字母(使用连字号 (-) 允许指定连续字符范围),"[B-F]"表示B到F之间的所有大写字母,"[0-9]"表示一个数字,如果要搜索一个整数(该序列只包含0到9的字符),就可以编写"[0-9]+"(注意,使用+字符表示至少要有这样一个数字,但可以有多个数字,所以9、83和3443等都是匹配的。)
下面看看一般表达式的结果,编写一个实例RegularExpressionsZzy。建立几个一般表达式,显示其结果,让用户了解一下表达式是如何工作的。

  该实例的核心是一个方法WriteMatches(),它把MatchCollection中的所有匹配以比较详细的方式显示出来。对于每个匹配,它都会显示该匹配在输入字符串中所在的索引,匹配的字符串和一个略长的字符串,其中包含输入文本中至多8个外围字符,其中至少有5个字符放在匹配的前面,至多5个字符放在匹配的后面(如果匹配的位置在输入文本的开头或结尾5个字符内,则结果中匹配前后的字符就会少于4个)。换言之,靠近输入文本末尾的匹配应是"and messaging ofd",匹配的前后各有5个字符,但位于输入文本的最后一个字上的匹配就应是"g of data",匹配的字后只有一个字符。因为在该字符的后面是字符串的结尾。这个长字符串可以更清楚地表明一般表达式是在什么地方查找到匹配的:

static void WriteMatches(string text, MatchCollection matches)
{
 Console.WriteLine("Original text was: \n\n" + text + "\n");
 Console.WriteLine("No. of matches: " + matches.Count);
 foreach (Match nextMatch in matches)
 {
  int Index = nextMatch.Index;
  string result = nextMatch.ToString();
  int charsBefore = (Index < 5) ? Index : 5;
  int fromEnd = text.Length - Index - result.Length;
  int charsAfter = (fromEnd < 5) ? fromEnd : 5;
  int charsToDisplay = charsBefore + charsAfter + result.Length;
  Console.WriteLine("Index: {0}, \tString: {1}, \t{2}",Index, result,
  text.Substring(Index - charsBefore, charsToDisplay));
 }
}

  在这个方法中,处理过程是确定在较长的字符串中有多少个字符可以显示,而无需超限输入文本的开头或结尾。注意在Match对象上使用了另一个属性Value,它包含标识该匹配的字符串,而且,RegularExpressionsZzy只包含名为Find_po,Find_n等的方法,这些方法根据本文执行某些搜索操作。

  4.3 正则表达式选项

  可以使用影响匹配行为的选项修改正则表达式模式。可以通过两种基本方法设置正则表达式选项:其一是可以在 Regex(pattern, options) 构造函数中的 options 参数中指定,其中 options 是 RegexOptions 枚举值的按位"或"组合;其二是使用内联 (?imnsx-imnsx:) 分组构造或 (?imnsx-imnsx) 其他构造在正则表达式模式内设置它们。

  在内联选项构造中,一个选项或一组选项前面的减号 (-) 用于关闭这些选项。例如,内联构造 (?ix-ms) 将打开 IgnoreCase 和 IgnorePatternWhiteSpace 选项而关闭 Multiline 和 Singleline 选项。

  表2:RegexOptions 枚举的成员以及等效的内联选项字符

RegexOption 成员 内联字符 说明
None 指定不设置任何选项。
IgnoreCase i 指定不区分大小写的匹配。
Multiline m 指定多行模式。更改 ^ 和 $ 的含义,以使它们分别与任何行的开头和结尾匹配,而不只是与整个字符串的开头和结尾匹配。
ExplicitCapture n 指定唯一有效的捕获是显式命名或编号的 (?<name>...) 形式的组。这允许圆括号充当非捕获组,从而避免了由 (?:...) 导致的语法上的笨拙。
Compiled 指定正则表达式将被编译为程序集。生成该正则表达式的 Microsoft 中间语言 (MSIL) 代码;以较长的启动时间为代价,得到更快的执行速度。
Singleline s 指定单行模式。更改句点字符 (.) 的含义,以使它与每个字符(而不是除 \n 外的所有字符)匹配。
IgnorePatternWhitespace x 指定从模式中排除非转义空白并启用数字符号 (#) 后面的注释。请注意,空白永远不会从字符类中消除。
RightToLeft 指定搜索是从右向左而不是从左向右进行的。具有此选项的正则表达式将移动到起始位置的左边而不是右边。(因此,起始位置应指定为字符串的结尾而不是开头。)为了避免构造具有无限循环的正则表达式的可能性,此选项不能在中流指定。但是,(?<) 回顾后发构造提供了可用作子表达式的类似替代物。
ECMAScript 指定已为表达式启用了符合 ECMAScript 的行为。此选项仅可与 IgnoreCase 和 Multiline 标志一起使用。将 ECMAScript 同任何其他标志一起使用将导致异常。

  例如,Find_po在字开头处查找以"po"开头的字符串:

static void Find_po()
{
 string text = @" I can not find my position in Beijing ";
 string pattern = @"\bpo\S*ion\b";
 MatchCollection matches = Regex.Matches(text, pattern, RegexOptions.IgnoreCase
| RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture);
 WriteMatches(text, matches);
}

  这段代码还使用了名称空间RegularExpressions:

using System;
using System.Text.RegularExpressions;

  4.4 匹配、组和捕获

  一般表达式的一个很好的特性是可以把字符组合起来,方式与C#中的复合语句一样。在C#中,可以通过把任意数量的语句放在花括号中的方式把它们组合在一起。其结果就像一个复合语句那样。在一般表达式模式中,也可以把任何字符组合起来(包括元字符和转义序列),像处理一个字符那样处理它们。唯一的区别是要使用圆括号,而不是花括号,得到的序列成为一个组。

  例如,模式"(an)+"定位序列an的任以重复。量词+只应用于它前面的一个字符,但因为我们把字符组合起来了,所以它现在把重复的an作为一个单元来对待。"(an)."应用到输入文本"bananas came to Europe late in the annals of history"上,会从bananas中选择出anan。另一方面,如果使用an+,则将从annals中选择ann,从bananas中选择出两个an。为什么(an)+选择的是anan,而没有把单个的an作为一个匹配。匹配规则是不能重复的,如果有可能重复,在默认情况下就选择较长的匹配。

  但是,组的功能要比这强大得多。在默认情况下,把模式的一部分组合为一个组时,就要求一般表达式引擎记住可以按照这个组来匹配,也可以按照整个模式来匹配。换言之,可以把组当作一个要匹配的模式,如果要把字符串分解为各个部分,这种模式就是非常有效的。
例如,URI的格式是"<protocol>://<address>:<port>",其中端口是可选的。它的一个样例是http://www.comprg.com.cn:8080。假定要从一个URI中提取协议、地址和端口,而且紧邻URI的后面可能有空白(但没有标点符号),就可以使用下面的表达式:"\b(\S+)://(\S+)(?::(\S+))?\b"

  该表达式的工作方式如下:首先,前导和尾部的\b序列确保只需要考虑完全是字的文本部分,在这个文本部分中,第一组"(\S+)://"会选择一个或多个不适空白的字符,其后是"://"。在HTTPURI的开头会选择出http://。花括号表示把http存储为一个组。后面的"(\S+)"则在上述URI中选择www. comprg.com.cn,这个组在遇到词的结尾时或标记另一个组的冒号"(:)"时结束。

  下一个组选择端口(本例是:8080)。后面的?表示这个组在匹配中是可选的,如果没有:xxxx,也不会妨碍匹配的标记。

  这是非常重要的,因为端口在URI中一般不指定,实际上,在大多数情况下,URI是没有端口号的。但是,事情会比较复杂。如果要求冒号可以出现,也可以不出现,但不希望把这个冒号也存储在组中。为此,可以嵌套两个组:内部的"(\S+)"组选择冒号后面的内容(本例中是8080),外面的组包含内部的组,后面是一个冒号,该冒号又在序列"?:"的后面。这个序列表示该组不应保存(只需要保存"8080",不需要保存":8080")。不要把这两个冒号混淆了,第一个冒号是序列"?:"的一部分,表示不保存这个组,第二个冒号是要搜索的文本。

  在这个字符串上运行该模式:I always visit http://www. comprg.com.cn 得到的匹配是http://www. comprg.com.cn。在这个匹配中,仅提到了三个组,还有第四个组表示匹配本身。理论上,每个组都可以选择0次、1次或者多次匹配。单个的匹配就称为捕获。在第一个组"(\S+)",有一个捕获http。第二个组也有一个捕获www. comprg.com.cn,但第三个组没有捕获,因为在这个URI中没有端口号。注意该字符串在其本身上包含第二个http://。虽然它匹配于第一个组,但不会被搜索出来,因为整个搜索表达式不匹配于这部分文本。
再比如下面这个例子,以下代码示例使用 Match.Result 来从 URL提取协议和端口号。例如,"http://www.yahoo.com.cn:8080/index.html"将返回"http:8080"。

String Extension(String url)
{
 Regex r = new Regex(@"^(?<proto>\w+)://[^/]+?(?<port>:\d+)?/",
 RegexOptions.Compiled);
 return r.Match(url).Result("${proto}${port}");
}

  5、小结

  .NET 框架正则表达式类是基类库的一部分,并且可以和面向公共语言运行库的任何语言或工具(包括 ASP.NET 和 Visual Studio .NET)一起使用。本文给出了在C#下利用正则表达式实现字符串搜索功能的方法,通过对.NET框架下的正则表达式的研究及实例分析,总结了正则表达式的规则、选项等,方便以后朋友们的应用。
posted @ 2007-07-23 10:53 无极.net 阅读(168) 评论(0) 编辑

2007年7月2日 #

/// <summary>
    /// 截获定长的字符串
    /// </summary>
    /// <param name="source">源字符串</param>
    /// <param name="length">需要截获的长度</param>
    /// <returns>截获后的字符串</returns>
    static public string FixLenth ( string source, int length)
    {
      return FixLenth ( source, length, "...");
    }
    /// <summary>
    /// 截获定长的字符串
    /// </summary>
    /// <param name="source">源字符串</param>
    /// <param name="length">需要截获的长度</param>
    /// <param name="postfix">如果字符串被截短,需要添加什么样的后缀</param>
    /// <returns>截获后的字符串</returns>
    static public string FixLenth ( string source, int length, string postfix)
    {
      if ( source == null )
        throw new ArgumentNullException( "source" );
      if ( postfix == null )
        postfix = "...";

      if ( length < postfix.Length )
        throw new ArgumentOutOfRangeException( "length" );
        

      int postfixLength = System.Text.Encoding.GetEncoding("GB2312").GetByteCount( postfix);
      int srcLength = System.Text.Encoding.GetEncoding("GB2312").GetByteCount( source);

      if ( srcLength > length)
      {
        for ( int i = source.Length; i>0; i--)
        {
          srcLength = System.Text.Encoding.GetEncoding("GB2312").GetByteCount( source.Substring(0,i) );

          if ( srcLength <= length - postfixLength)
            return source.Substring(0,i) + postfix;
        }
        return "";
      }
      else
        return source;
    }


--------------------------------------------------------

能给个思路吗?
我想知道是怎么做的

--------------------------------------------------------

GB2312编码会将一个中文字编码成两个字节,而英文字母只会编码成一个字节,那么根据编码后的字节数就能知道是否满足截断的要求了。那么,弄个循环不断的缩短截断长度直到满足要求为止就行了。

--------------------------------------------------------

就像这样
int postfixLength = System.Text.Encoding.GetEncoding("GB2312").GetByteCount( postfix);
      int srcLength = System.Text.Encoding.GetEncoding("GB2312").GetByteCount( source);
posted @ 2007-07-02 15:33 无极.net 阅读(175) 评论(1) 编辑

2007年4月25日 #


 1
 2        /// <summary>
 3        /// 生成缩略图
 4        /// </summary>
 5        /// <param name="oldpath">原图片地址</param>
 6        /// <param name="newpath">新图片地址</param>
 7        /// <param name="tWidth">缩略图的宽</param>
 8        /// <param name="tHeight">缩略图的高</param>

 9        private void  GreateMiniImage(string oldpath,string newpath,int tWidth, int tHeight)
10        {
11        
12            try
13            {
14
15                System.Drawing.Image image = System.Drawing.Image.FromFile(oldpath);
16                double bl=1d;
17                if((image.Width<=image.Height)&&(tWidth>=tHeight))
18                {
19                    bl=Convert.ToDouble(image.Height)/Convert.ToDouble(tHeight);
20                }

21                else if((image.Width>image.Height)&&(tWidth<tHeight))
22                {
23                    bl=Convert.ToDouble(image.Width)/Convert.ToDouble(tWidth);
24            
25                }

26                else
27            
28                    if((image.Width<=image.Height)&&(tWidth<=tHeight))
29                {
30                    if(image.Height/tHeight>=image.Width/tWidth)
31                    {
32                        bl=Convert.ToDouble(image.Width)/Convert.ToDouble(tWidth);
33                    
34                    }

35                    else
36                    {
37                        bl=Convert.ToDouble(image.Height)/Convert.ToDouble(tHeight);
38                    }

39                }

40                else
41                {
42                    if(image.Height/tHeight>=image.Width/tWidth)
43                    {
44                        bl=Convert.ToDouble(image.Height)/Convert.ToDouble(tHeight);
45                    
46                    }

47                    else
48                    {
49                        bl=Convert.ToDouble(image.Width)/Convert.ToDouble(tWidth);
50                    
51                    }

52            
53                }

54
55            
56                Bitmap b = new Bitmap(image ,Convert.ToInt32(image.Width/bl), Convert.ToInt32(image.Height/bl));
57
58                b.Save(newpath);
59                b.Dispose();
60                image.Dispose();
61                
62
63            }

64            catch
65            {
66            
67                
68            }

69            
70        }
posted @ 2007-04-25 22:37 无极.net 阅读(385) 评论(4) 编辑

2007年3月30日 #

是用的FLASH调用数据库的数据。代码如下:
<script type="text/javascript">
<!--


imgUrl1 = "http://www.moobol.com/livePic/manage/huxw/070225/083809_1.jpg";
imgText1 = "";
imgLink1 = escape("/molive/liveAction.do?method=findLiveList&liveId=12577");

imgUrl2 = "http://www.moobol.com/livePic/manage/kefeng/070224/000559_1.jpg";
imgText2 = "战士看美女";
imgLink2 = escape("/molive/liveAction.do?method=findLiveList&liveId=12661");

imgUrl3 = "http://www.moobol.com/livePic/manage/kefeng/070224/003118_1.jpg";
imgText3 = "沈阳商厦∶“有钱能使鬼推磨”";
imgLink3 = escape("/molive/liveAction.do?method=findLiveList&liveId=12650");

imgUrl4 = "http://www.moobol.com/livePic/manage/kefeng/070224/005915_1.jpg";
imgText4 = "直播福建省建阳农村婚礼习俗";
imgLink4 = escape("/molive/liveAction.do?method=findLiveList&liveId=12614");

imgUrl5 = "http://www.moobol.com/livePic/manage/kefeng/070224/010913_1.jpg";
imgText5 = "水源被污染 鱼儿就遭殃";
imgLink5 = escape("/molive/liveAction.do?method=findLiveList&liveId=12620");


var focus_width = 319;
var focus_height = 230;
var text_height = 18;
var swf_height = focus_height + text_height;
var pics = imgUrl1 + "|" + imgUrl2 + "|" + imgUrl3 + "|" + imgUrl4 + "|" + imgUrl5;
var links = imgLink1 + "|" + imgLink2 + "|" + imgLink3 + "|" + imgLink4 + "|" + imgLink5;
var texts = imgText1 + "|" + imgText2 + "|" + imgText3 + "|" + imgText4 + "|" + imgText5;
document.write('<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0" width="'+ focus_width +'" height="'+ swf_height +'">');
document.write('<param name="allowScriptAccess" value="sameDomain"><param name="movie" value="/molive/webjsp/flash/pixviewer.swf"><param name="quality" value="high"><param name="bgcolor" value="#E3E3E3">');
document.write('<param name="menu" value="false"><param name=wmode value="opaque">');
document.write('<param name="FlashVars" value="pics='+pics+'&links='+links+'&texts='+texts+'&borderwidth='+focus_width+'&borderheight='+focus_height+'&textheight='+text_height+'">');
document.write('<embed src="/molive/webjsp/flash/pixviewer.swf" wmode="opaque" FlashVars="pics='+pics+'&links='+links+'&texts='+texts+'&borderwidth='+focus_width+'&borderheight='+focus_height+'&textheight='+text_height+'" menu="false" bgcolor="#E3E3E3" quality="high" width="'+ focus_width +'" height="'+ focus_height +'" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />');
document.write('</object>');
-->
</script>
你把那个FLASH用迅雷下载下来!然后把上述代码改动一下,就行了。
代码主要修改如下:
imgUrl1 = "http://www.moobol.com/livePic/manage/huxw/070225/083809_1.jpg";
imgText1 = "";
imgLink1 = escape("/molive/liveAction.do?method=findLiveList&liveId=12577");
用数据库提取5条相关记录。即:
循环开始
imgurl<%=k%>="rs("jilu")"
imgtext<%=k%>=rs("title")
imgLink<%=k%>=escape("<%=rs("url")%>")
循环结束

效果如下网址:
http://www.moobol.com/molive/homePageAction.do?method=findHomePage
posted @ 2007-03-30 16:50 无极.net 阅读(6194) 评论(4) 编辑

2007年3月20日 #

如何使用本教程

别被下面那些复杂的表达式吓倒,只要跟着我一步一步来,你会发现正则表达式其实并没有你想像中的那么困难。当然,如果你看完了这篇教程之后,发现自己明白了很多,却又几乎什么都记不得,那也是很正常的——我认为,没接触过正则表达式的人在看完这篇教程后,能把提到过的语法记住80%以上的可能性为零。这里只是让你明白基本的原理,以后你还需要多练习,多查资料,才能熟练掌握正则表达式。

除了作为入门教程之外,本文还试图成为可以在日常工作中使用的正则表达式语法参考手册(就作者本人的经历来说,这个目标还是完成得不错的)。

文本格式约定:专业术语 元字符/语法格式 正则表达式 正则表达式中的一部分(用于分析) 用于在其中搜索的字符串 对正则表达式或其中一部分的说明

什么是正则表达式?

在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。

很可能你使用过Windows/Dos下用于文件查找的通配符(wildcard),也就是*?。如果你想查找某个目录下的所有的Word文档的话,你会搜索*.doc。在这里,*会被解释成任意的字符串。和通配符类似,正则表达式也是用来进行文本匹配的工具,只不过比起通配符,它能更精确地描述你的需求——当然,代价就是更复杂——比如你可以编写一个正则表达式,用来查找所有以0开头,后面跟着2-3个数字,然后是一个连字号“-”,最后是7或8位数字的字符串(像010-123456780376-7654321)。

正则表达式是用于进行文本匹配的工具,所以本文里多次提到了在字符串里搜索/查找,这种说法的意思是在给定的字符串中,寻找与给定的正则表达式相匹配的部分。有可能字符串里有不止一个部分满足给定的正则表达式,这时每一个这样的部分被称为一个匹配。匹配在本文里可能会有三种意思:一种是形容词性的,比如说一个字符串匹配一个表达式;一种是动词性的,比如说在字符串里匹配正则表达式;还有一种是名词性的,就是刚刚说到的“字符串中满足给定的正则表达式的一部分”。

入门

学习正则表达式的最好方法是从例子开始,理解例子之后再自己对例子进行修改,实验。下面给出了不少简单的例子,并对它们作了详细的说明。

假设你在一篇英文小说里查找hi,你可以使用正则正则表达式hi

这是最简单的正则表达式了,它可以精确匹配这样的字符串:由两个字符组成,前一个字符是h,后一个是i。通常,处理正则表达式的工具会提供一个忽略大小写的选项,如果选中了这个选项,它可以匹配hi,HI,Hi,hI这四种情况中的任意一种。

不幸的是,很多单词里包含hi这两个连续的字符,比如him,history,high等等。用hi来查找的话,这里边的hi也会被找出来。如果要精确地查找hi这个单词的话,我们应该使用\bhi\b

\b是正则表达式规定的一个特殊代码(好吧,某些人叫它元字符,metacharacter),代表着单词的开头或结尾,也就是单词的分界处。虽然通常英文的单词是由空格或标点符号或换行来分隔的,但是\b并不匹配这些单词分隔符中的任何一个,它只匹配一个位置。(如果需要更精确的说法,\b匹配这样的位置:它的前一个字符和后一个字符不全是\w

假如你要找的是hi后面不远处跟着一个Lucy,你应该用\bhi\b.*\bLucy\b

这里,.是另一个元字符,匹配除了换行符以外的任意字符*同样是元字符,不过它代表的不是字符,也不是位置,而是数量--它指定*前边的内容可以连续重复出现任意次以使整个表达式得到匹配。因此,.*连在一起就意味着任意数量的不包含换行的字符。现在\bhi\b.*\bLucy\b的意思就很明显了:先是一个单词hi,然后是任意个任意字符(但不能是换行),最后是Lucy这个单词

如果同时使用其它的一些元字符,我们就能构造出功能更强大的正则表达式。比如下面这个例子:

0\d\d-\d\d\d\d\d\d\d\d匹配这样的字符串:以0开头,然后是两个数字,然后是一个连字号“-”,最后是8个数字(也就是中国的电话号码。当然,这个例子只能匹配区号为3位的情形)。

这里的\d是一个新的元字符,匹配任意的数字(0,或1,或2,或……)-不是元字符,只匹配它本身——连字号。

为了避免那么多烦人的重复,我们也可以这样写这个表达式:0\d{2}-\d{8}

这里\d后面的{2}({8})的意思是前面\d必须连续重复匹配2次(8次)

测试正则表达式

如果你不觉得正则表达式很难读写的话,要么你是一个天才,要么,你不是地球人。正则表达式的语法很令人头疼,即使对经常使用它的人来说也是如此。由于难于读写,容易出错,所以很有必要创建一种工具来测试正则表达式。

由于在不同的环境下正则表达式的一些细节是不相同的,本教程介绍的是Microsoft .Net 2.0下正则表达式的行为,所以,我向你介绍一个.Net下的工具Regex Tester。首先你确保已经安装了.Net Framework 2.0,然后下载Regex Tester,下载完后打开压缩包,直接运行RegexTester.exe。

下面是Regex Tester运行时的截图:

Regex Tester运行时的截图

元字符

现在你已经知道几个很有用的元字符了,如\b,.,*,还有\d.当然还有更多的元字符,比如\s匹配任意的空白符,包括空格,制表符(Tab),换行符,中文全角空格等\w匹配字母或数字或下划线或汉字

下面来试试更多的例子:

\ba\w*\b匹配以字母a开头的单词——先是某个单词开始处(\b),然后是字母a,然后是任意数量的字母或数字(\w*),最后是单词结束处(\b)(好吧,现在我们说说正则表达式里的单词是什么意思吧:就是几个连续的\w。不错,这与学习英文时要背的成千上万个同名的东西的确关系不大)。

\d+匹配1个或更多连续的数字。这里的+是和*类似的元字符,不同的是*匹配重复任意次(可能是0次),而+则匹配重复1次或更多次

\b\w{6}\b 匹配刚好6个字母/数字的单词

表1.常用的元字符
代码 说明
. 匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线或汉字
\s 匹配任意的空白符
\d 匹配数字
\b 匹配单词的开始或结束
^ 匹配字符串的开始
$ 匹配字符串的结束

元字符^(和6在同一个键位上的符号)以及$\b有点类似,都匹配一个位置。^匹配你要用来查找的字符串的开头,$匹配结尾。这两个代码在验证输入的内容时非常有用,比如一个网站如果要求你填写的QQ号必须为5位到12位数字时,可以使用:^\d{5,12}$

这里的{5,12}和前面介绍过的{2}是类似的,只不过{2}匹配只能不多不少重复2次{5,12}则是重复的次数不能少于5次,不能多于12次,否则都不匹配。

因为使用了^$,所以输入的整个字符串都要用来和\d{5,12}来匹配,也就是说整个输入必须是5到12个数字,因此如果输入的QQ号能匹配这个正则表达式的话,那就符合要求了。

和忽略大小写的选项类似,有些正则表达式处理工具还有一个处理多行的选项。如果选中了这个选项,^$的意义就变成了匹配行的开始处和结束处

字符转义

如果你想查找元字符本身的话,比如你查找.,或者*,就出现了问题:你没法指定它们,因为它们会被解释成其它的意思。这时你就必须使用\来取消这些字符的特殊意义。因此,你应该使用\.\*。当然,要查找\本身,你也得用\\.

例如:www\.unibetter\.com匹配www.unibetter.comc:\\windows匹配c:\windows,2\^8匹配2^8(通常这是2的8次方的书写方式)。

重复

你已经看过了前面的*,+,{2},{5,12}这几个匹配重复的方式了。下面是正则表达式中所有指定重复的方式:

表2.常用的限定符
代码/语法 说明
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

下面是一些使用重复的例子:

Windows\d+匹配Windows后面跟1个或更多数字

13\d{9}匹配以13后面跟9个数字(中国的手机号)

^\w+匹配一行的第一个单词(或整个字符串的第一个单词,具体匹配哪个意思得看选项设置)

字符类

要想查找数字,字母或数字,空白是很简单的,因为已经有了对应这些字符集的元字符,但是如果你想匹配没有预定义元字符的字符集比如元音字母(a,e,i,o,u),怎么办?

很简单,你只需要在中括号里列出它们就行了,像[aeiou]就匹配任何一个元音字母[.?!]匹配标点符号(.或?或!)(英文语句通常只以这三个标点结束)。注意,我们不需要写成[\.\?!]。

我们也可以轻松地指定一个字符范围,像[0-9]代表的含意与\d就是完全一致的:一位数字,同理[a-z0-9A-Z_]也完全等同于\w(如果只考虑英文的话)。

下面是一个更复杂的表达式:\(?0\d{2}[) -]?\d{8}

这个表达式可以匹配几种格式的电话号码,像(010)88886666,或022-22334455,或02912345678等。我们对它进行一些分析吧:首先是一个转义字符\(,它能出现0次或1次(?),然后是一个0,后面跟着2个数字(\d{2}),然后是)-空格中的一个,它出现1次或不出现(?),最后是8个数字(\d{8})。不幸的是,它也能匹配010)12345678(022-87654321这样的“不正确”的格式。要解决这个问题,请在本教程的下面查找答案。

反义

有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外,其它任意字符都行的情况,这时需要用到反义

表3.常用的反义代码
代码/语法 说明
\W 匹配任意不是字母,数字,下划线,汉字的字符
\S 匹配任意不是空白符的字符
\D 匹配任意非数字的字符
\B 匹配不是单词开头或结束的位置
[^x] 匹配除了x以外的任意字符
[^aeiou] 匹配除了aeiou这几个字母以外的任意字符

例子:\S+匹配不包含空白符的字符串

<a[^>]+>匹配用尖括号括起来的以a开头的字符串

替换

好了,现在终于到了解决3位或4位区号问题的时间了。正则表达式里的替换指的是有几种规则,如果满足其中任意一种规则都应该当成匹配,具体方法是用|把不同的规则分隔开。听不明白?没关系,看例子:

0\d{2}-\d{8}|0\d{3}-\d{7}这个表达式能匹配两种以连字号分隔的电话号码:一种是三位区号,8位本地号(如010-12345678),一种是4位区号,7位本地号(0376-2233445)

\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}这个表达式匹配3位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔。你可以试试用替换|把这个表达式扩展成也支持4位区号的。

\d{5}-\d{4}|\d{5}这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字,或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题:使用替换时,顺序是很重要的。如果你把它改成\d{5}|\d{5}-\d{4}的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配替换时,将会从左到右地测试每个分枝条件,如果满足了某个分枝的话,就不会去管其它的替换条件了。

Windows98|Windows2000|WindosXP这个例子是为了告诉你替换不仅仅能用于两种规则,也能用于更多种规则。

分组

我们已经提到了怎么重复单个字符(直接在字符后面加上限定符就行了);但如果想要重复一个字符串又该怎么办?你可以用小括号来指定子表达式(也叫做分组),然后你就可以指定这个子表达式的重复次数了,你也可以对子表达式进行其它一些操作(后面会有介绍)。

(\d{1,3}\.){3}\d{1,3}是一个简单的IP地址匹配表达式。要理解这个表达式,请按下列顺序分析它:\d{1,3}匹配1到3位的数字(\d{1,3}\.}{3}匹配三位数字加上一个英文句号(这个整体也就是这个分组)重复3次,最后再加上一个一到三位的数字(\d{1,3})。

不幸的是,它也将匹配256.300.888.999这种不可能存在的IP地址(IP地址中每个数字都不能大于255)。如果能使用算术比较的话,或许能简单地解决这个问题,但是正则表达式中并不提供关于数学的任何功能,所以只能使用冗长的分组,选择,字符类来描述一个正确的IP地址:((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)

理解这个表达式的关键是理解2[0-4]\d|25[0-5]|[01]?\d\d?,这里我就不细说了,你自己应该能分析得出来它的意义。

后向引用

使用小括号指定一个子表达式后,匹配这个子表达式的文本可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。

后向引用用于重复搜索前面某个分组匹配的文本。例如,\1代表分组1匹配的文本。难以理解?请看示例:

\b(\w+)\b\s+\1\b可以用来匹配重复的单词,像go go, kitty kitty。首先是一个单词,也就是单词开始处和结束处之间的多于一个的字母或数字(\b(\w+)\b),然后是1个或几个空白符(\s+,最后是前面匹配的那个单词(\1)。

你也可以自己指定子表达式的组号或组名。要指定一个子表达式的组名,请使用这样的语法:(?<Word>\w+),这样就把\w+的组名指定为Word了。要反向引用这个分组捕获的内容,你可以使用\k<Word>,所以上一个例子也可以写成这样:\b(?<Word>\w+)\b\s*\k<Word>\b

使用小括号的时候,还有很多特定用途的语法。下面列出了最常用的一些:

表4.分组语法
捕获
(exp) 匹配exp,并捕获文本到自动命名的组里
(?<name>exp) 匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name'exp)
(?:exp) 匹配exp,不捕获匹配的文本
位置指定
(?=exp) 匹配exp前面的位置
(?<=exp) 匹配exp后面的位置
(?!exp) 匹配后面跟的不是exp的位置
(?<!exp) 匹配前面不是exp的位置
注释
(?#comment) 这种类型的组不对正则表达式的处理产生任何影响,只是为了提供让人阅读注释

我们已经讨论了前两种语法。第三个(?:exp)不会改变正则表达式的处理方式,只是这样的组匹配的内容不会像前两种那样被捕获到某个组里面

位置指定

接下来的四个用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们用于指定一个位置,就像\b,^,$那样,因此它们也被称为零宽断言。最好还是拿例子来说明吧:

(?=exp)也叫零宽先行断言,它匹配文本中的某些位置,这些位置的后面能匹配给定的后缀exp。比如\b\w+(?=ing\b),匹配以ing结尾的单词的前面部分(除了ing以外的部分),如果在查找I'm singing while you're dancing.时,它会匹配singdanc

(?<=exp)也叫零宽后行断言,它匹配文本中的某些位置,这些位置的前面能给定的前缀匹配exp。比如(?<=\bre)\w+\b会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book时,它匹配ading

假如你想要给一个很长的数字中每三位间加一个逗号(当然是从右边加起了),你可以这样查找需要在前面和里面添加逗号的部分:((?<=\d)\d{3})*\b。请仔细分析这个表达式,它可能不像你第一眼看出来的那么简单。

下面这个例子同时使用了前缀和后缀:(?<=\s)\d+(?=\s)匹配以空白符间隔的数字(再次强调,不包括这些空白符)

负向位置指定

前面我们提到过怎么查找不是某个字符或不在某个字符类里的字符的方法(反义)。但是如果我们只是想要确保某个字符没有出现,但并不想去匹配它时怎么办?例如,如果我们想查找这样的单词--它里面出现了字母q,但是q后面跟的不是字母u,我们可以尝试这样:

\b\w*q[^u]\w*\b匹配包含后面不是字母u的字母q的单词。但是如果多做测试(或者你思维足够敏锐,直接就观察出来了),你会发现,如果q出现在单词的结尾的话,像Iraq,Benq,这个表达式就会出错。这是因为[^u]总是匹配一个字符,所以如果q是单词的最后一个字符的话,后面的[^u]将会匹配q后面的单词分隔符(可能是空格,或者是句号或其它的什么),后面的\w+\b将会匹配下一个单词,于是\b\w*q[^u]\w*\b就能匹配整个Iraq fighting负向位置指定能解决这样的问题,因为它只匹配一个位置,并不消费任何字符。现在,我们可以这样来解决这个问题:\b\w*q(?!u)\w*\b

零宽负向先行断言(?!exp),只会匹配后缀exp不存在的位置\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字

同理,我们可以用(?<!exp),零宽负向后行断言来查找前缀exp不存在的位置(?<![a-z])\d{7}匹配前面不是小写字母的七位数字(实验时发现错误?注意你的“区分大小写”先项是否选中)。

一个更复杂的例子:(?<=<(\w+)>).*(?=<\/\1>)匹配不包含属性的简单HTML标签内里的内容(<?(\w+)>)指定了这样的前缀:被尖括号括起来的单词(比如可能是<b>),然后是.*(任意的字符串),最后是一个后缀(?=<\/\1>)。注意后缀里的\/,它用到了前面提过的字符转义;\1则是一个反向引用,引用的正是捕获的第一组,前面的(\w+)匹配的内容,这样如果前缀实际上是<b>的话,后缀就是</b>了。整个表达式匹配的是<b>和</b>之间的内容(再次提醒,不包括前缀和后缀本身)。

注释

小括号的另一种用途是能过语法(?#comment)来包含注释。例如:2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199)

要包含注释的话,最好是启用“忽略模式里的空白符”选项,这样在编写表达式时能任意的添加空格,Tab,换行,而实际使用时这些都将被忽略。启用这个选项后,在#后面到这一行结束的所有文本都将被当成注释忽略掉。

例如,我们可以前面的一个表达式写成这样:

      (?<=    # 查找前缀,但不包含它
<(\w+)> # 查找尖括号括起来的字母或数字(标签)
)       # 前缀结束
.*      # 匹配任意文本
(?=     # 查找后缀,但不包含它
<\/\1>  # 查找尖括号括起来的内容:前面是一个"/",后面是先前捕获的标签
)       # 后缀结束

贪婪与懒惰

当正则表达式中包含能接受重复的限定符(指定数量的代码,例如*,{5,12}等)时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。考虑这个表达式:a.*b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪匹配。

有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。这样.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。现在看看懒惰版的例子吧:

a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aabab(为什么第一个匹配是aab而不是ab?简单地说,最先开始的区配最有最大的优先权——The Match That Begins Earliest Wins)。

表5.懒惰限定符
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复

处理选项

上面介绍了几个选项如忽略大小写,处理多行等,这些选项能用来改变处理正则表达式的方式。下面是.net中常用的正则表达式选项:

表6.常用的处理选项
名称 说明
IgnoreCase(忽略大小写) 匹配时不区分大小写。
Multiline(多行模式) 更改^$的含义,使它们分别在任意一行的行首和行尾匹配,而不仅仅在整个字符串的开头和结尾匹配。
Singleline(单行模式) 更改.的含义,使它与每一个字符匹配(包括换行符\n)。
IgnorePatternWhitespace(忽略空白) 忽略表达式中的非转义空白并启用由#标记的注释。
RightToLeft(从右向左查找) 匹配从右向左而不是从左向右进行。
ExplicitCapture(显式捕获) 仅捕获已被显式命名的组。
ECMAScript(JavaScript兼容模式) 使表达式的行为与它在JavaScript里的行为一致。

一个经常被问到的问题是:是不是只能同时使用多行模式和单行模式中的一种?答案是:不是。这两个选项之间没有任何关系,除了它们的名字比较相似(以至于让人感到疑惑)以外。

平衡组

如果想要匹配可嵌套的层次性结构的话,就得使用平衡组了(注意:这里介绍的平衡组只适用于.Net里的正则表达式,其它语言或库可能没有实现这个功能,或者有不同的语法)。举个例子吧,如何把“xx <aa <bbb> <bbb> aa> yy”这样的字符串里,最长的括号内的内容捕获出来?

这里需要用到以下的语法构造:

  • (?<group>) 把捕获的内容命名为group,并压入堆栈
  • (?<-group>) 从堆栈上弹出最后压入堆栈的名为group的捕获内容,如果堆栈本来为空,则本分组的匹配失败
  • (?(group)yes|no) 如果堆栈上存在以名为group的捕获内容的话,继续匹配yes部分的表达式,否则继续匹配no部分
  • (?!) 零宽负向先行断言,由于没有后缀表达式,试图匹配总是失败

如果你不是一个程序员(或者你是一个对堆栈的概念不熟的程序员),你就这样理解上面的三种语法吧:第一个就是在黑板上写一个(或再写一个) "group",第二个就是从黑板上擦掉一个"group",第三个就是看黑板上写的还有没有"group",如果有就继续匹配yes部分,否则就匹配 no部分。

我们需要做的是每碰到了左括号,就在黑板上写一个"group",每碰到一个右括号,就擦掉一个,到了最后就看看黑板上还有没有-如果有那就证明左括号比右括号多,那匹配就应该失败(为了能看得更清楚一点,我用了(?'group')的语法):

<                         #最外层的左括号
[^<>]*                #最外层的左括号后面的不是括号的内容
(
(
(?'Open'<)    #碰到了左括号,在黑板上写一个"Open"
[^<>>]*       #匹配左括号后面的不是括号的内容
)+
(
(?'-Open'>)   #碰到了右括号,擦掉一个"Open"
[^<>]*        #匹配右括号后面不是括号的内容
)+
)*
(?(Open)(?!))         #在遇到最外层的右括号前面,判断黑板上还有没有没擦掉的"Open";如果还有,则匹配失败
>                         #最外层的右括号

还有些什么东西没提到

我已经描述了构造正则表达式的大量元素,还有一些我没有提到的东西。下面是未提到的元素的列表,包含语法和简单的说明。你可以在网上找到更详细的参考资料来学习它们--当你需要用到它们的时候。如果你安装了MSDN Library,你也可以在里面找到关于.net下正则表达式详细的文档。

表7.尚未详细讨论的语法
\a 报警字符(打印它的效果是电脑嘀一声)
\b 通常是单词分界位置,但如果在字符类里使用代表退格
\t 制表符,Tab
\r 回车
\v 竖向制表符
\f 换页符
\n 换行符
\e Escape
\0nn ASCII代码中八进制代码为nn的字符
\xnn ASCII代码中十六进制代码为nn的字符
\unnnn Unicode代码中十六进制代码为nnnn的字符
\cN ASCII控制字符。比如\cC代表Ctrl+C
\A 字符串开头(类似^,但不受处理多行选项的影响)
\Z 字符串结尾或行尾(不受处理多行选项的影响)
\z 字符串结尾(类似$,但不受处理多行选项的影响)
\G 当前搜索的开头
\p{name} Unicode中命名为name的字符类,例如\p{IsGreek}
(?>exp) 贪婪子表达式
(?<x>-<y>exp) 平衡组
(?im-nsx:exp) 在子表达式exp中改变处理选项
(?im-nsx) 为表达式后面的部分改变处理选项
(?(exp)yes|no) 把exp当作零宽正向先行断言,如果在这个位置能匹配,使用yes作为此组的表达式;否则使用no
(?(exp)yes) 同上,只是使用空表达式作为no
(?(name)yes|no) 如果命名为name的组捕获到了内容,使用yes作为表达式;否则使用no
(?(name)yes) 同上,只是使用空表达式作为no

一些我认为你可能已经知道的术语的参考

字符
程序处理文字时最基本的单位,可能是字母,数字,标点符号,空格,换行符,汉字等等。
字符串
0个或更多个字符的序列。
文本
文字,字符串。
匹配
符合规则,检验是否符合规则,符合规则的部分。

网上的资源及本文参考文献

posted @ 2007-03-20 13:08 无极.net 阅读(213) 评论(2) 编辑

2007年3月15日 #

摘要: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta htt...阅读全文
posted @ 2007-03-15 17:45 无极.net 阅读(4785) 评论(0) 编辑