正则表达式概述

 正则表达式简明手册

一、简介

正则表达式这个名词,相信很多人都听说过,这个名词最早起源于1956 年, 一位叫 Stephen Kleene 的美
国数学家在 McCulloch 和 Pitts 早期工作的基础上,发表了一篇标题为“神经网事件的表示法”的论文,
引入了正则表达式的概念。
正则表达式就是用来描述他称为“正则集的代数”的表达式,因此采用“正则表达式”这个术语。

随后,发现可以将这一工作应用于使用Ken Thompson 的计算搜索算法的一些早期研究,Ken Thompson
是Unix 的主要发明人。正则表达式的第一个实用应用程序就是 Unix 中的qed 编辑器。

Q: 正则表达式,能够为我们做什么呢?

A: 基于文本的编辑器和搜索工具中的一个重要部分。正则表达式可以让用户通过使用一系列的特殊字符构
建匹配模式,然后把匹配模式与数据文件、程序输入以及WEB页面的表单输入等目标对象进行比较,根据
比较对象中是否包含匹配模式,执行相应的程序。
 
下面我们就一步一步的结合它的语法,来介绍正则表达式的使用。

二、初次接触正则表达式

我们先来了解正则表达式的一些基本概念。正则表达式作为一种表示语言,其定义了自己的一套描述方式,
来描述各种各样的字符类。下面摘取msdn中的一段定义。
(ms-help://MS.VSCC/MS.MSDNVS.2052/cpgenref/html/cpconcharacterclasses.htm)
 
字符转义表
字符类
含义
.
与除 /n 以外的任何字符匹配。如果通过 Singleline 选项(请参阅正则表达式选项)进行了修改,则句点
字符与任何字符匹配。
[aeiou] 与指定字符集中包含的任何单个字符匹配。

[^aeiou]与不在指定字符集中的任何单个字符匹配。
[0-9a-fA-F] 使用连字号 (–) 允许指定连续字符范围。

/p{name}

与 name 指定的命名字符类中的任何字符匹配。支持的名称为 Unicode 组和块范围。
例如 Ll£?Nd£?Z£?IsGreek£?IsBoxDrawing。

/P{name} 与在 {name} 中指定的组和块范围中未包含的文本匹配。


/w 与任何单词字符匹配。等效于 Unicode 字符类别

[/p{Ll}/p{Lu}/p{Lt}/p{Lo}/p{Nd}/p{Pc}]。如果通过 ECMAScript 选项指定了符合 ECMAScript 的行为,
则 /w 等同于 [a-zA-Z_0-9]。

 /W 与任何非单词字符匹配。等效于 Unicode 类别 [^/p{Ll}/p{Lu}/p{Lt}/p{Lo}/p{Nd}/p{Pc}]。如果通过
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 的 /p{Nd} 和非 Unicode 的 [0-9] 以及 ECMAScript 行为一样。

/D 与任何非数字匹配。与 Unicode 的 /P{Nd} 和非 Unicode 的 [^0-9] 以及 ECMAScript 行为一样。

 
上表列举了,正则表达式中最最基本的语法定义,了解这些,我们已经可以定义一些简单的规则了,例如:
1. 匹配所有的字符
当然是什么都不用写(@_@)

2. 匹配所有的英文字符
a) /w
b) [a-zA-Z_0-9]
3. 匹配十进制数字
a) /d
b) [0-9]
看上面的例子,是不是觉得很简单呢,不过,到目前为止,这样写出来的规则,还有一个很大的缺陷,就
是没有声明匹配字符的个数?

Q: 我希望要匹配的字符为5个英文字母
A: ???
光了解上面的知识是,无法解决这个的L。那正则表达式中是如何解决这个问题的呢,我们来看下面这个
表:
(ms-help://MS.VSCC/MS.MSDNVS.2052/cpgenref/html/cpconquantifiers.htm)

限定符表 限定符 说明

* 指定零个或更多个匹配;例如 /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})。
上表中列出了,正则表达式的限定方式,配合这些字符的使用,我们就可以很方便的编写更为强劲的正则
表达式了。

例如:

1. 匹配零个或多个所有的字符
*
2. 匹配一个或多个所有字符
+
3. 匹配零个或多个所有的英文字符
/w*
4. 匹配一个或多个所有的英文字符
[a-zA-Z0-9]+
5. 匹配3个十进制数字
/d{3}
6. 匹配最少3个十进制数字
/d{3,}
7. 匹配3个到6个十进制数字

/d{3,6}
现在我们可以解答上面问题了:
Q: 我希望要匹配的字符为5个英文字母
A: /w{5}

很高兴,我们已解决了上面的问题,不过,新的问题总是在不断的出现。我如何限制匹配字符出现在哪里
呢?

Q: 我希望匹配以doc开头的字符串
A: ???
为了解决这个问题,我们先来看看这个表:
(ms-help://MS.VSCC/MS.MSDNVS.2052/cpgenref/html/cpconatomiczero-widthassertions.htm)

原子零宽度断言
断言
说明
^ 指定匹配必须出现在字符串的开头或行的开头。
有关更多信息,请参阅正则表达式选项中的 Multiline 选项。

$ 指定匹配必须出现在以下位置:字符串结尾、字符串结尾的 /n 之前或行的结尾。有关更多信息,请参阅
正则表达式选项中的 Multiline 选项。
/A 指定匹配必须出现在字符串的开头(忽略 Multiline 选项)。
/Z 指定匹配必须出现在字符串的结尾或字符串结尾的 /n 之前(忽略 Multiline 选项)。
/z 指定匹配必须出现在字符串的结尾(忽略 Multiline 选项)。
/G 指定匹配必须出现在当前搜索开始的位置(此位置通常是上一次搜索结束位置之后的第一个字符)。例如,
请考虑一个由分离的字符组组成的串联字符串,其中每一组的长度都为 n 个字符。在每个字符组中搜索匹
配时,如果正则表达式在 0、n、2n、3n 等字符位置找到匹配,则该正则表达式成功。
仅当匹配出现在定位组边界上时才会成功。
/b 指定匹配必须出现在 /w(字母数字)和 /W(非字母数字)字符之间的边界上。
匹配必须出现在单词边界上,即出现在由空格分隔的单词中第一个或最后一个字符上。
/B 指定匹配不得出现在 /b 边界上。

相信大家都注意到了,在这个表中第一个断言字符就是我们需要的@_@.

例如,^ 指定当前位置在行或字符串的开头。因此,正则表达式 ^FTP 只会返回那些在行的开头出现的字
符串“FTP”的匹配项。

看来上面碰到的问题,又可以解决了,让我们一起来解决上面的问题:

Q: 我希望匹配以doc开头的字符串

A: ^doc

以上我们初步了解了什么是正则表达式,已经了解其最基本的语法,当作热身@_@,接下来,才正式进入
主题,我们会从第二篇开始深入探讨正则表达式的使用。
在前一篇文章中,介绍了一些初步的正则表达式的基本概念,相信很多人对正则表达式的基本知识有所了
解,接下来,我们结合一些实际的编程示例来掩饰说明正则表达式的作用。
首先,我们先看几个实际的例子:
1. 验证输入字符是否全部为英文字符
javascript:
var ex = "^//w+$";
var re = new RegExp(ex,"i");
return re.test(str);

VBScript
Dim regEx,flag,ex

ex = "^/w+$"
Set regEx = New RegExp
regEx.IgnoreCase = True

regEx.Global = True
regEx.Pattern = ex
flag = regEx.Test( str )

C#
System.String ex = @"^/w+$";

System.Text.RegularExpressions.Regex reg = new Regex( ex ); bool flag = reg.IsMatch( str );

2. 验证邮件格式
C#
System.String ex = @"^/w+@/w+/./w+$";
System.Text.RegularExpressions.Regex reg = new Regex( ex );
bool flag = reg.IsMatch( str );

3. 更改日期的格式(用 dd-mm-yy 的日期形式代替 mm/dd/yy 的日期形式)
C#
String MDYToDMY(String input)
{
return Regex.Replace(input,
"
//b(?<month>//d{1,2})/(?<day>//d{1,2})/(?<year>//d{2,4})//b",
"${day}-${month}-${year}");
}
4. 从 URL 提取协议和端口号
C#
String Extension(String url)
{
Regex r = new Regex(@"^(?<proto>/w+)://[^/]+?(?<port>:/d+)?/",
RegexOptions.Compiled);
return r.Match(url).Result("${proto}${port}");
}

这里的例子可能是我们在网页开发中,通常会碰到的一些正则表达式,尤其在第一个例子中,给出了使用
javascript,vbScript,C#等不同语言的实现方式,大家不难看出,对于不同的语言来说,正则表达式没有区
别,只是正则表达式的实现类不同而已。而如何发挥正则表达式的公用,也要看实现类的支持。
(摘自msdn: Microsoft .NET 框架 SDK 提供大量的正则表达式工具,使您能够高效地创建、比较和修改
字符串,以及迅速地分析大量文本和数据以搜索、移除和替换文本模式。
ms-help://MS.VSCC/MS.MSDNVS.2052/cpgenref/html/cpconregularexpressionslanguageelements.htm)


下面我们逐个来分析这些例子:
1-2,这两个例子很简单,只是简单的验证字符串是否符合正则表达式规定的格式,其中使用的语法,在第
一篇文章中都已经介绍过了,这里做一下简单的描述。

第1个例子的表达式: ^/w+$

^ -- 表示限定匹配开始于字符串的开始
/w – 表示匹配英文字符
+ -- 表示匹配字符出现1次或多次
$ -- 表示匹配字符到字符串结尾处结束
验证形如asgasdfs的字符串

第2个例子的表达式: ^/w+@/w+./w+$
^ -- 表示限定匹配开始于字符串的开始
/w – 表示匹配英文字符
+ -- 表示匹配字符出现1次或多次
@ -- 匹配普通字符@
/. – 匹配普通字符.(注意.为特殊字符,因此要加上/转译)
$ -- 表示匹配字符到字符串结尾处结束
验证形如
dragontt@sina.com的邮件格式
第3 个例子中,使用了替换,因此,我们还是先来看看正则表达式中替换的定义:
(ms-help://MS.VSCC/MS.MSDNVS.2052/cpgenref/html/cpconsubstitutions.htm)
替换 字符 含义
 $123
替换由组号 123(十进制)匹配的最后一个子字符串。
${name}
替换由 (?<name> ) 组匹配的最后一个子字符串。
$$ 替换单个“$”字符。
$& 替换完全匹配本身的一个副本。
$` 替换匹配前的输入字符串的所有文本。
$' 替换匹配后的输入字符串的所有文本。
$+ 替换最后捕获的组。
$_ 替换整个输入字符串。
分组构造 (ms-help://MS.VSCC/MS.MSDNVS.2052/cpgenref/html/cpcongroupingconstructs.htm)
分组构造
定义 ( )
捕获匹配的子字符串(或非捕获组;有关更多信息,请参阅正则表达式选项中的 ExplicitCapture 选项。)
使用 () 的捕获根据左括号的顺序从 1 开始自动编号。捕获元素编号为零的第一个捕获是由整个正则表达
式模式匹配的文本。

(?<name> )

将匹配的子字符串捕获到一个组名称或编号名称中。用于 name 的字符串不能包含任何标点符号,并且不
能以数字开头。可以使用单引号替代尖括号,例如 (?'name')。


(?<name1-name2> )

平衡组定义。删除先前定义的 name2 组的定义并在 name1 组中存储先前定义的 name2 组和当前组之
间的间隔。如果未定义 name2 组,则匹配将回溯。由于删除 name2 的最后一个定义会显示 name2 的
先前定义,因此该构造允许将 name2 组的捕获堆栈用作计数器以跟踪嵌套构造(如括号)。在此构造中,
name1 是可选的。可以使用单引号替代尖括号,例如 (?'name1-name2')。
(?: ) 非捕获组。
(?imnsx-imnsx: ) 应用或禁用子表达式中指定的选项。例如,(?i-s: ) 将打开不区分大小写并禁用单行模式。有关更多信息,
请参阅正则表达式选项。
(?= ) 零宽度正预测先行断言。仅当子表达式在此位置的右侧匹配时才继续匹配。例如,/w+(?=/d) 与后跟数字
的单词匹配,而不与该数字匹配。此构造不会回溯。
(?! ) 零宽度负预测先行断言。仅当子表达式不在此位置的右侧匹配时才继续匹配。例如,/b(?!un)/w+/b 与不
以 un 开头的单词匹配。
(?<= ) 零宽度正回顾后发断言。仅当子表达式在此位置的左侧匹配时才继续匹配。例如,(?<=19)99 与跟在 19 后
面的 99 的实例匹配。此构造不会回溯。

(?<! ) 零宽度负回顾后发断言。仅当子表达式不在此位置的左侧匹配时才继续匹配。
(?> ) 非回溯子表达式(也称为贪婪子表达式)。
该子表达式仅完全匹配一次,然后就不会逐段参与回溯了。(也
就是说,该子表达式仅与可由该子表达式单独匹配的字符串匹配。)
我们还是先简单的了解一下这两个概念:

分组构造:

最基本的构造方式就是(),在左右括号中括起来的部分,就是一个分组;

更进一步的分组就是形如:(?<name> )的分组方式,这种方式与第一种方式的不同点,就是对分组的部分
进行了命名,这样就可以通过该组的命名来获取信息;
(还有形如(?= )等等的分组构造,我们这篇的例子中也没有使用到,下次我们在来介绍)


替换:

上面提到了两种基本的构造分组方式()以及(?<name> ),通过这两种分组方式,我们可以得到形如
$1,${name}的匹配结果。
这样说,可能概念上还是有些模糊,我们还是结合上面的例子来说:


第三个例子的正则表达式为://b(?<month>//d{1,2})/(?<day>//d{1,2})/(?<year>//d{2,4})//b

(解释一下,为什么这里都是//一起用:这里是C#的例子,在C#语言中/是转译字符,要想字符串中的/
不转译,就需要使用//或者在整个字符串的开始加上@标记,即上面等价与

@”/b(?<month>/d{1,2})/(?<day>/d{1,2})/(?<year>/d{2,4}/b”)

/b -- 是一种特殊情况。在正则表达式中,除了在 [] 字符类中表示退格符以外,/b 表示字边界(在 /w 和
/W 字符之间)。在替换模式中,/b 始终表示退格符

(?<month>/d{1,2}) – 构造一个名为month的分组,这个分组匹配一个长度为1-2的数字

/ -- 匹配普通的/字符

(?<day>/d{1,2}) --构造一个名为day的分组,这个分组匹配一个长度为1-2的数字

/ -- 匹配普通的/字符

(?<year>/d{2,4}/b”) --构造一个名为year的分组,这个分组匹配一个长度为2-4的数字
这里还不能够看出这些分组的作用,我们接着看这一句

${day}-${month}-${year}

${day} – 获得上面构造的名为day的分组匹配后的信息
- -- 普通的-字符

${month} --获得上面构造的名为month的分组匹配后的信息
- -- 普通的-字符

${year} --获得上面构造的名为year的分组匹配后的信息
举例来说:

将形如04/02/2003的日期使用例3种的方法替换 

(?<month>/d{1,2}) 分组将匹配到04由${month}得到这个匹配值
(?<day>/d{1,2}) 分组将匹配到02由${day}得到这个匹配值
(?<year>/d{1,2}) 分组将匹配到2003由${year}得到这个匹配值
了解了这个例子后,我们在来看第4个例子就很简单了。

第4个例子的正则
^(?<proto>/w+)://[^/]+?(?<port>:/d+)?/

^ -- 表示限定匹配开始于字符串的开始

(?<proto>/w+) – 构造一个名为proto的分组,匹配一个或多个字母

: -- 普通的:字符

// -- 匹配两个/字符

[^/] – 表示这里不允许是/字符

+? – 表示指定尽可能少地使用重复但至少使用一次匹配
(?<port>:/d+) – 构造一个名为port的分组,匹配形如:2134(冒号+一个或多个数字)

? – 表示匹配字符出现0次或1次
/ -- 匹配/字符


最后通过${proto}${port}来获取两个分组构造的匹配内容
(有关Regex对象的用法,参考
ms-help://MS.VSCC/MS.MSDNVS.2052/cpref/html/frlrfSystemTextRegularExpressionsRegexMembersTopic.htm)

 

 

好了,本次介绍的几个例子,也讲得差不多了,希望大家有所收获,下次,在就一些特殊的要求,进一步
探讨正则表达式的实现。

前面的文章中,介绍了正则表达式的基本语法,以及一些简单的例子。但这些并不是我们会遇到的全部问
题,有些时候我们不得不编写一些较为复杂的正则表达式来解决我们的实际问题。

 

这里,我先提几个问题,然后,我们逐个运用正则表达式的知识来解决。

 

1. 符合两种条件之一,都成立,例如:是纯数字或者纯字符

 

123(true),hello(true),234.test23(false)

 

2. 要得到不以数字开头的字符组合

 

如:How2234do>you234do,希望得到How和you而不是do,do

 

3. 得到以数字开头的字符组合

 


上例中,得到do和do

 

4. 要得到不以数字结尾的字符组合

 

还是上面的情况,要得到的是Ho,do,yo,do

 

5. 得到以数字结尾的字符组合

 

同上例,得到Ho,do,yo,do

 

6. 不允许字符中ab同时出现

 

例:nihaoma(true),above(false),agoodboy(true)

 

 

 

下面我们开始着手解决这些问题:

 

第一个:符合两种条件之一,都成立

 

这种要求可能代表着一种普遍的要求,我们先来看看这个表

 

替换构造

替换构造

定义

 

|

与由|(垂直条)字符分隔的术语中的任何一个术语匹配;例如 cat|dog|tiger。使用最左侧的成功匹配。

 

(?(expression)yes|no)

如果表达式在此位置匹配,则与“yes”部分匹配;否则,与“no”部分匹配。“no”部分可省略。表达式
可以是任何有效的表达式,但它将变为零宽度断言,因此该语法等效于 (?(?=expression)yes|no)。请注意,
如果表达式是命名组的名称或捕获组编号,则替换构造将解释为捕获测试(在本表的下一行对此进行了描
述)。若要避免在这些情况下产生混淆,则可以显式拼出内部 (?=expression)。

 

(?(name)yes|no)

如果命名捕获字符串有匹配,则与“yes”部分匹配;否则,与“no”部分匹配。“no”部分可省略。如果
给定的名称不与此表达式中使用的捕获组的名称或编号对应,则替换构造将解释为表达式测试(在本表的
上一行进行了描述)。

 

 

(ms-help://MS.VSCC/MS.MSDNVS.2052/cpgenref/html/cpconalternationconstructs.htm)

 

在这个表中,我们看到,正则中为了解决这一类问题,定义了|来表示或者的关系,就好像常见的或运算符


一样,现在我们来看看如何利用|来解决我们的问题。

 

1. 先为可选择的表达式撰写表达式:

 

a) 纯数字 – [0-9]*

 

b) 纯字母 – [a-zA-Z]*

 

2. 将可选条件用|连接起来就是我们所需的

 

^[0-9]*$|^[a-zA-Z]*$

 

(这里我特别对两个条件加上了^和$限定符,这在验证字符串是否完全符合要求时,是十分必要的,如果
不加这两个限定符,有兴趣的朋友可以自己试一下效果。

 

后面四个问题,其实是一类的,所以我们把它们放在一起处理。接下来我们来解决第二到第四个问题:

 

首先,我们回顾一下上次介绍的分组构造:

 

(?= )

零宽度正预测先行断言。仅当子表达式在此位置的右侧匹配时才继续匹配。例如,/w+(?=/d) 与后跟数字
的单词匹配,而不与该数字匹配。此构造不会回溯。

 

(?! )

零宽度负预测先行断言。仅当子表达式不在此位置的右侧匹配时才继续匹配。例如,/b(?!un)/w+/b 与不
以 un 开头的单词匹配。

 

(?<= )

零宽度正回顾后发断言。仅当子表达式在此位置的左侧匹配时才继续匹配。例如,(?<=19)99 与跟在 19 后
面的 99 的实例匹配。此构造不会回溯。

 

(?<! )

零宽度负回顾后发断言。仅当子表达式不在此位置的左侧匹配时才继续匹配。

 

可以看到,这个表的这四种规则,正好可以解决我们的问题。

 

@_@先解决我们的问题再说:

 

第二例:要得到不以数字开头的字符组合

 

(?<!/d)[a-zA-Z]{2,}

 

(?<!/d) -- 限定字符的开头不为数字才匹配

 


[a-zA-Z]{2,} – 描述匹配2个以上的字母

 

(注:这是取巧的做法,因为,按照我们的逻辑How2234do>you234do中的两个do的o字母也是符合的,
不过,这不是我们想要的,当然还有其他的解决办法,可以根据实际的情况来处理,这里是为了讲解这个
方法@_@)

 

第三例:得到以数字开头的字符组合

 

(?<=/d)[a-zA-Z]+

 

(?<=/d) – 限定为数字开头的字符才匹配

 

[a-zA-Z]+ -- 描述匹配1个或多个字母

第四例:要得到不以数字结尾的字符组合


[a-zA-Z]+(?!/d)

 

[a-zA-Z]+ -- 描述匹配1个或多个字母

 

(?!/d) – 限定不为数字结尾的字母才匹配

 

第五例:得到以数字结尾的字符组合


[a-zA-Z]+(?=/d)


[a-zA-Z]+ -- 描述匹配1个或多个字母


(?=/d) – 限定为数字结尾的字母才匹配


第六例:不允许字符中ab同时出现

 

^(?!.*?ab).*$

 

(?!.*?ab) – 限定不允许出现ab相连的字符


.* -- 任意字符


介绍到这里,我们这一次提出的问题,也都解决了,例子虽然简单,不过,再复杂的东西也是建立在简单
的基础上的。其实写正则表达式的关键就是要善于定制规则,用最简练正确的话来描述,然后用正则表达
式的语法写出来,就可以了(@_@)这就要靠大家多积累经验了。


正则表达式在UBB论坛中的应用

一、读者指引

 读者指引帮助你掌握本文的梗概。以免你看了大半才明白这篇文章不适合你。

 如果你正在用ASP写程序,或者你正在写一些诸如BBS、留言溥或表单数据检查之类的东东那就
值得一看。

 

 如果你对正则表达式已经了如指掌,那么你不必一行行的看,只要看看我写的模板,再比较一下,取
其精华就行了。

 如果你还是第一次接触正则表达式,那么你最好一行行的看,并逐条试验

 当你熟练的掌握了正则表达式的用法,你就会发现其乐无穷。


二、正则表达式的概念

 什么是UBB代码?什么是正则表达式?

 

 UBB代码是HTML的一个变种。一般情况下,UBB论坛不允许你使用HTML代码,而只能用UBB代码替代HTML代码。

 UBB代码是一套由流行的UBB标签组成了固定代码,代码有统一的格式。用户只要遵循代码规则
就可以实现用户想要的功能。如:

 想要显示粗体的how are you 字样,就应该输入[b] how are you[/b]而不是输入<b>how are you</b>


 你也许会问:ASP是怎样把[b] how are you[/b]转换为<b>how are you</b>的呢?

 回答这个问题就是:用正则表达式。


三、正则表达式的用途


有时我们在制作网站表单数据处理的时候(尤其是UBB论坛),都需要进行数据验证和字符串替代,特别
是UBB论坛要进行大量的数据安全性和字符串替代

邮于一般的论坛不支持HTML语法这就使得用户不能修改字体,不能贴图等等一些功能。这样使得论坛失去
了吸引用户的一个强有力的途径。可能说一个强大的论坛在吸引用户数量上还是很重要的。这样就出现了
一个UBB解决方案,即在论坛不支持HTML语法的情况下用户仍然可以定制自已贴子的样式,贴图,增加
链接,转贴网页等等诸多的功能,可能达到支持HTML语法同样的效果,而且这样可以使得论坛相对于HTML
的论坛安全性大大提高。用户基本不能对论坛过行任何恶意攻击。


四、正则表达式的语法规则和标记


 现在我们正式进入则表达式的学习,我会根据实例结合讲解正则表达式的用法,看完后你就会觉得写
UBB代码如此简单了,只要你一步一步的跟着我学 看完本文章后你就成为UBB高手了。激动人心的就
是你能写出自已的UBB标签来了,再也不用到别人那里去拷贝现成的代码和模板了。 还好VBScritp5.0
给我们提供了“正则表达式”对象,只要你的服务器安装了IE5.x,就可以运行了.


 字符描述:

 ^符号匹配字符串的开头。例如:

 ^abc 与“abc xyz”匹配,而不与“xyz abc”匹配


 $符号匹配字符串的结尾。例如:

 abc$ 与“xyz abc”匹配,而不与“abc xyz”匹配。

 注意:如果同时使用^符号和$符号,将进行精确匹配。例如:

 ^abc$ 只与“abc”匹配

 

 *符号匹配0个或多个前面的字符。例如:

 ab* 可以匹配“ab”、“abb”、“abbb”等

 

 +符号匹配至少一个前面的字符。例如:

 ab+ 可以匹配“abb”、“abbb”等,但不匹配“ab”。

 

 ?符号匹配0个或1个前面的字符。例如:

 ab?c? 可以且只能匹配“abc”、“abbc”、“abcc”和“abbcc”

 

 .符号匹配除换行符以外的任何字符。例如:

 (.)+ 匹配除换行符以外的所有字符串


 x|y匹配“x”或“y”。例如:

 abc|xyz 可匹配 “abc”或 “xyz”,而“ab(c|x)yz”匹配 “abcyz”和“abxyz”


 {n}匹配恰好n次(n为非负整数)前面的字符。例如:

 a{2} 可以匹配“aa“,但不匹配“a”

 {n,}匹配至少n次(n为非负整数)前面的字符。例如:

 a{3,} 匹配“aaa”、“aaaa”等,但不匹配“a”和“aa”。

 注意:a{1,}等价于a+

 a{0,}等价于a*

 {m,n}匹配至少m个,至多n个前面的字符。例如:

 a{1,3} 只匹配“a”、“aa”和“aaa”。

 注意:a{0,1}等价于a?
 [xyz]表示一个字符集,匹配括号中字符的其中之一。例如:

 [abc] 匹配“a”、“b”和“c”

 [^xyz]表示一个否定的字符集。匹配不在此括号中的任何字符。例如:

 [^abc] 可以匹配除“a”、“b”和“c”之外的任何字符

 [a-z]表示某个范围内的字符,匹配指定区间内的任何字符。例如:

 [a-z] 匹配从“a”到“z”之间的任何一个小写字母字符

 [^m-n]表示某个范围之外的字符,匹配不在指定范围内的字符。例如:

 [m-n] 匹配除从“m”到“n”之间的任何字符

 /符号是转义操作符。例如:

 /n 换行符

 /f 分页符

 /r 回车

 /t 制表符

 /v 垂直制表符


 // 匹配“/”

 // 匹配“/”

 /s 任何白字符,包括空格、制表符、分页符等。等价于“[ /f/n/r/t/v]”

 /S 任何非空白的字符。等价于“^/f/n/r/t/v]”

 /w 任何单词字符,包括字母和下划线。等价于“[A-Za-z0-9_]”

 /W 任何非单词字符。等价于“[^A-Za-z0-9_]”

 /b匹配单词的结尾。例如:

 ve/b 匹配单词“love”等,但不匹配“very”、“even”等

 /B匹配单词的开头。例如:

 ve/B 匹配单词“very”等,但不匹配“love”等

 /d匹配一个数字字符,等价于[0-9]。例如:

 abc/dxyz 匹配“abc2xyz”、“abc4xyz”等,但不匹配“abcaxyz”、“abc-xyz”等

 /D匹配一个非数字字符,等价于[^0-9]。例如:

 abc/Dxyz 匹配“abcaxyz”、“abc-xyz”等,但不匹配“abc2xyz”、“abc4xyz”等

 /NUM匹配NUM个(其中NUM为一个正整数),引用回到记住的匹配。例如:

 (.)/1 匹配两个连续相同的字符。

 /oNUM匹配n(其中n为一个小于256的八进制换码值)。例如:

 /o011 匹配制表符


 /xNUM匹配NUM(其中NUM为一个小于256的十六进制换码值)。例如:

/x41 匹配字符“A”


五、实例分析

1)在字符串中精确查找链接地址

((http|https|ftp):(|)((/w)+[.]){1,}(net|com|cn|org|cc|tv|[0-9]{1,3})(((//[/~]*|//[/~
]*)

(/w)+)|[.](/w)+)*(((([?](/w)+){1}[=]*))*((/w)+){1}([/&](/w)+[/=](/w)+)*)*)


我们知道,链接地址一般以http或者https或者ftp等形式出现。初步总结一下就是,链接地址必须符合
如下条件:

条件1

 以http://或者https://或者ftp://等开头(当然还有其它形式,这里只列出主要的)

条件2

 http://后面必须跟一个单词字符,紧接着单词字符后面的是"."(这样的组合必须出现一次或多次)。紧
跟着“.”后面的是域名后缀(如net或者com或者cn等,如果是以IP地址的形式出现就可以是数字)

条件3

 出现完整的链接地址后,还可以出现下一级或者更多级的目录(还要注意个人主页的地址有可能出现"~"
符号)

 

条件4

 链接地址末尾可以带参数。如典型的页数?PageNo=2&action=display等

现在我们用下面的代码来逐个匹配上面的条件——

1、((http|https|ftp):(|) 满足条件1

表示http:// http:// https:// https:// ftp:// ftp://都匹配(在这里考虑了某些用户可能把"//"输
成“//”的易发性错误)

注意:"|"表示“或者”,"/"是转义字符。“”表示"//",“”表示"//"

2、((/w)+[.]){1,}(net|com|cn|org|cc|tv|[0-9]{1,3}) 满足条件2

“((/w)+[.]){1,}”表示一个单词字符加一个点号可以出现1次或者多次(这里考虑了某些用户喜欢省略
www而将
http://www.w3c.com写成http://w3c.com)

“(net|com|cn|org|cc|tv|[0-9]{1,3})”表示必须要以net或者com或者cn或者org或者cc或者tv或
者三位以下的数字结束

[0-9]{1,3}表示三位以下的数字,因为ip地址的任何段不能超过255


3、(((//[/~]*|//[/~]*)(/w)+)|[.](/w)+)* 满足条件3

“(//[/~]*|//[/~]*)”表示可以出现"/~"或者是"/~",(其中“[/~]*”表示 ~ 可以出现也可以不出现),
因为不是每个链接地址都有下一级目录

“(/w)+)|[.](/w)+)”表示必须出现一个单词字符(即目录或者是一个带有扩展名的文件)

注意:最后还有一个“*”表示上面括号内的可以出现也可以不出现,否则就只能匹配有下一级目录的链接
地址了。

4、(((([?](/w)+){1}[=]*))*((/w)+){1}([/&](/w)+[/=](/w)+)*)*)满足条件4

“((([?](/w)+){1}[=]*))*((/w)+){1}”表示形如"?PageNo=2"的字符串可以出现也可以不出现,如果出现
则只能出现一次(因为不可能有两个“?”号出现)。

“([/&](/w)+[/=](/w)+)*)”表示形如“&action=display”的字符串可以出现也可以不出现(因为并不是
每个网页都带有两个以上的参数。

整个“((([?](/w)+){1}[=]*))*((/w)+){1}([/&](/w)+[/=](/w)+)*)*”表示形如
“?PageNo=2&action=display”的字符串可以出现也可以不出现(即链接地址可以有参数也可以没有参数)

把上面的组合起来,我们就可以匹配一个比较全面的链接地址了。比用简单的“(http:/S+)”来匹配
一个链接地址要好,读者可以自行行测试比较。当然,这段代码还有很多不足之处,希望大家能够继续改
进。

2)替代典型的UBB标签:[/b]

我们的目的就是要把[b]成对的替换成<b></b>下面来看我们实现它的模板

 (/[b/])(.+)(/[//b/])

这里用了"(.+)"来配匹到之间的整个字符串,在替代的时候我们要写成这样

 str=checkexp(re,str,"<b>$2</b>")

(注意:checkexp是我自定义的函数,将在后面给出。这个函数将把[/b]按照我们提供的模板进行替代。)

也许你会问这里出现一个"$2"是什么东东,呵注意了这个$2可是很重要的,它代表了"(.+)"所配匹的整个
字符串。

为什么是$2而不是$1、$3呢?因为$1代表(/[b/])所匹配的"[b]"字符串,$3代表(/[//b/])所匹配的""
字符串,显然这里我们需要的是$2而不是$1$3。

六)UBB正则表达模板实例

下面是我写的一个UBB函数,这个函数基本上能使你的论坛成为一个优秀的UBB代码论坛了。当然,
通过改进后,你可以得到一个更强大的UBB论坛。


Function ReThestr(face,str)

 dim re,str

 re="/>"

 str=checkexp(re,str,"&gt;")


 re="/<"

 str=checkexp(re,str,"&lt;")


 re="/n/r/n/"

 str=checkexp(re,str,"<P>")

 

 re=chr(32)

 str=checkexp(re,str,"&nbsp;")


 re="/r"

 str=checkexp(re,str," ")


 re="/[img/]((http:(|)){1}((/w)+[.]){1,3}(net|com|cn|org|cc|tv)(((//[/~]*|//[/~]*)

(/w)+)|[.](/w)+)*(/w)+[.]{1}(gif|jpg|png))/[//img/]" '查找图片地址

 str=checkexp(re,str," <img src='$1'> ")


 
re="/[w/](http:(|)((/w)+[.]){1,}(net|com|cn|org|cc|tv)(((//[/~]*|//[/~]*)(/w)+)|[.](
/w)+)*

(((([?](/w)+){1}[=]*))*((/w)+){1}([/&](/w)+[/=](/w)+)*)*)/[//w/]" '查找帧地址

 str=checkexp(re,str,"<iframe width='300' height='300' src='$1'></iframe>")

 
re="([^('>)])(<br>)*((http|https|ftp):(|)((/w)+[.]){1,}(net|com|cn|org|cc|tv|([0-9]{
1,3}))(((//[/~]*|//[/~]*)(/w)+)|[.](/w)+)*(((([?](/w)+){1}[=]*))*((/w)+){1}([/&](/w)+[/=](/w)+)*)*)" '查找链接地址

 str=checkexp(re,str,"$1$2 <a href='$3' target=_blank>$3</a> ")

 
re="([^(
http://|http://)])((www|cn)[.](/w)+[.]{1,}(net|com|cn|org|cc)(((//[/~]*|//[/~]*)(/w)
+)|[.](/w)+)*

(((([?](/w)+){1}[=]*))*((/w)+){1}([/&](/w)+[/=](/w)+)*)*)" '查找不以http://开头的地址

 str=checkexp(re,str,"$1 <a href='http://$2' target=_blank>$2</a> ")


 re="([^(=)])((/w)+[@]{1}((/w)+[.]){1,3}(/w)+)" '查找邮件地址

 str=checkexp(re,str," <a href='mailto:$2'>$2</a> ")


 re="/[0-F]{6})/]((.)+)/[//color/]" '替换字体色彩

 str=checkexp(re,str,"<font color='$1'>$4</font>")


 re="/[size=([0-9]{1})/]((.)+)/[//size/]" '替换字体大小

 str=checkexp(re,str,"<font size='$1'>$2</font>")

 

 re="/((.)+)/[//font/]" '替换字体

 str=checkexp(re,str,"<font face='$1'>$3</font>")


 re="(/[b/])(.+)(/[//b/])" '加粗字体

 str=checkexp(re,str,"<b>$2</b>")
 

 re="(/[u/])(.+)(/[//u/])" '下画线

 str=checkexp(re,str,"<u>$2</u>")


 re="(/[li/])(.+)(/[//li/])" '列表

 str=checkexp(re,str,"<li>$2</li>")


 re="(/[QUOTE/])(.+)(/[//QUOTE/])" '引用

 str=checkexp(re,str,"<BLOCKQUOTE>引用:<HR SIZE=1>$2<HR SIZE=1></BLOCKQUOTE>")


 re="/[email=((/w)+[@]{1}((/w)+[.]){1,3}(/w)+)/](.+)(/[//email/])" '邮件

 str=checkexp(re,str,"<a href=mailto:$1>$6</a>")


 re="(/[center/])(.+)(/[//center/])" '居中

 str=checkexp(re,str,"<center>$2</center>")


 re="fuck"

 str=checkexp(re,str,"***")


 re="操"

 str=checkexp(re,str,"***")

 re="sex"

 str=checkexp(re,str,"***")

 re="TMD"

 str=checkexp(re,str,"***")

 re="shit"

 str=checkexp(re,str,"***")

 ReThestr=str
end function

UBB代码如下:

[i] [/i] [u] [/u]

[url] [/url] [email=] [/email] [img] [/img]

引用:

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

[li] [/li] [font=impact] [color=Yellow]
 
测试代码如下:

 [img] [/img]http://cn.yahoo.com

aol.com 192.168.0.1

www.yahoo.com how are you[/b]

page2000.xiloo.com/~page2000?PageNo=2&action=del

lucaihui@cmmail.com 大家好http://page2000.shit

<font color=red>http://test.com</font>http://test

全部符合预想结果


七)ASP正则表达式对像函数如下:

Function CheckExp(patrn,strng,tagstr)

 Dim regEx,Matches

 Set regEx=New RegExp'建立一个新对像

 regEx.Pattern=patrn'设置模板

 regEx.IgnoreCase=true'搜索是否区分大小写的 true表是不区分 flase表示区分

 regEx.Global=True'搜索是否应用于整个字符串

 Matches=regEx.replace(strng,tagstr)'匹配并替代字符串

 CheckExp=Matches返回函数结果

end function

 
把上面的两个函数存为一个页面(如ubbcode.asp),这样就组成一个完整的UBB函数了。

在你的论坛上加上这个函数,就成了支持UBB代码的论坛了。只要在用时调用这个函数就行了。调用形
式如 text=ReThestr(text)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值