可判断yyyy-mm-dd hh:mm:ss,并可判断闰年和月
(([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29) (20|21|22|23|[0-1]?/d):[0-5]?/d:[0-5]?/d$
可判断YYYY-MM-DD YYYY/MM/DD YYYY.MM.DD并可判断闰年和月
^(?:(?:(?:(?:1[6-9]|[2-9]/d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))(//|-|/.)(?:0?2/1(?:29))$)|(?:(?:1[6-9]|[2-9]/d)?/d{2})(//|-|/.)(?:(?:(?:0?[13578]|1[02])/2(?:31))|(?:(?:0?[1,3-9]|1[0-2])/2(29|30))|(?:(?:0?[1-9])|(?:1[0-2]))/2(?:0?[1-9]|1/d|2[0-8]))$
可判断yyyy-mm-dd hh:mm:ss,并可判断闰年和月
^[ ]*[012 ]?[0123456789]?[0123456789]{2}[ ]*[-]{1}[ ]*[01]?[0123456789]{1}[ ]*[-]{1}[ ]*[0123]?[0123456789]{1}[ ]*[012]?[0123456789]{1}[ ]*[:]{1}[ ]*[012345]?[0123456789]{1}[ ]*[:]{1}[ ]*[012345]?[0123456789]{1}[ ]*$
---->上面三个正则表达式有问题,但可以取部分使用
多少年来,许多的编程语言和工具都包含对正则表达式的支持,.NET基础类库中包含有一个名字空间和一系列可以充分发挥规则表达式威力的类,而且它们也都与未来的Perl 5中的规则表达式兼容。
此外,regexp类还能够完成一些其他的功能,例如从右至左的结合模式和表达式的编辑等。
在这篇文章中,我将简要地介绍System.Text.RegularExpression中的类和方法、一些字符串匹配和替换的例子以及组结构的详细情况,最后,还会介绍一些你可能会用到的常见的表达式。
应该掌握的基础知识
规则表达式的知识可能是不少编程人员“常学常忘”的知识之一。在这篇文章中,我们将假定你已经掌握了规则表达式的用法,尤其是Perl 5中表达式的用法。.NET的regexp类是Perl 5中表达式的一个超集,因此,从理论上说它将作为一个很好的起点。我们还假设你具有了C#的语法和.NET架构的基本知识。
如果你没有规则表达式方面的知识,我建议你从Perl 5的语法着手开始学习。在规则表达式方面的权威书籍是由杰弗里·弗雷德尔编写的《掌握表达式》一书,对于希望深刻理解表达式的读者,我们强烈建议阅读这本书。
RegularExpression组合体
regexp规则类包含在System.Text.RegularExpressions.dll文件中,在对应用软件进行编译时你必须引用这个文件,例如:
csc r:System.Text.RegularExpressions.dll foo.cs
命令将创建foo.exe文件,它就引用了System.Text.RegularExpressions文件。
名字空间简介
在名字空间中仅仅包含着6个类和一个定义,它们是:
Capture: 包含一次匹配的结果;
CaptureCollection: Capture的序列;
Group: 一次组记录的结果,由Capture继承而来;
Match: 一次表达式的匹配结果,由Group继承而来;
MatchCollection: Match的一个序列;
MatchEvaluator: 执行替换操作时使用的代理;
Regex: 编译后的表达式的实例。
Regex类中还包含一些静态的方法:
Escape: 对字符串中的regex中的转义符进行转义;
IsMatch: 如果表达式在字符串中匹配,该方法返回一个布尔值;
Match: 返回Match的实例;
Matches: 返回一系列的Match的方法;
Replace: 用替换字符串替换匹配的表达式;
Split: 返回一系列由表达式决定的字符串;
Unescape:不对字符串中的转义字符转义。
简单匹配
我们首先从使用Regex、Match类的简单表达式开始学习。
Match m = Regex.Match("abracadabra", "(a|b|r)+");
我们现在有了一个可以用于测试的Match类的实例,例如:if (m.Success)...
如果想使用匹配的字符串,可以把它转换成一个字符串:
Console.WriteLine("Match="+m.ToString());
这个例子可以得到如下的输出: Match=abra。这就是匹配的字符串了。
字符串的替换
简单字符串的替换非常直观。例如下面的语句:
string s = Regex.Replace("abracadabra", "abra", "zzzz");
它返回字符串zzzzcadzzzz,所有匹配的字符串都被替换成了zzzzz。
现在我们来看一个比较复杂的字符串替换的例子:
string s = Regex.Replace(" abra ", @"^/s*(.*?)/s*$", "$1");
这个语句返回字符串abra,其前导和后缀的空格都去掉了。
上面的模式对于删除任意字符串中的前导和后续空格都非常有用。在C#中,我们还经常使用字母字符串,在一个字母字符串中,编译程序不把字符“ /” 作为转义字符处理。在使用字符“/”指定转义字符时,@"..."是非常有用的。另外值得一提的是$1在字符串替换方面的使用,它表明替换字符串只能包含被替换的字符串。
匹配引擎的细节
现在,我们通过一个组结构来理解一个稍微复杂的例子。看下面的例子:
string text = "abracadabra1abracadabra2abracadabra3";
string pat = @"
( # 第一个组的开始
abra # 匹配字符串abra
( # 第二个组的开始
cad # 匹配字符串cad
)? # 第二个组结束(可选)
) # 第一个组结束
+ # 匹配一次或多次
";
//利用x修饰符忽略注释
Regex r = new Regex(pat, "x");
//获得组号码的清单
int[] gnums = r.GetGroupNumbers();
//首次匹配
Match m = r.Match(text);
while (m.Success)
{
//从组1开始
for (int i = 1; i < gnums.Length; i++)
{
Group g = m.Group(gnums[i]);
//获得这次匹配的组
Console.WriteLine("Group"+gnums[i]+"=["+g.ToString()+"]");
//计算这个组的起始位置和长度
CaptureCollection cc = g.Captures;
for (int j = 0; j < cc.Count; j++)
{
Capture c = cc[j];
Console.WriteLine(" Capture" + j + "=["+c.ToString()
+ "] Index=" + c.Index + " Length=" + c.Length);
}
}
//下一个匹配
m = m.NextMatch();
}
这个例子的输出如下所示:
Group1=[abra]
Capture0=[abracad] Index=0 Length=7
Capture1=[abra] Index=7 Length=4
Group2=[cad]
Capture0=[cad] Index=4 Length=3
Group1=[abra]
Capture0=[abracad] Index=12 Length=7
Capture1=[abra] Index=19 Length=4
Group2=[cad]
Capture0=[cad] Index=16 Length=3
Group1=[abra]
Capture0=[abracad] Index=24 Length=7
Capture1=[abra] Index=31 Length=4
Group2=[cad]
Capture0=[cad] Index=28 Length=3
我们首先从考查字符串pat开始,pat中包含有表达式。第一个capture是从第一个圆括号开始的,然后表达式将匹配到一个abra。第二个capture组从第二个圆括号开始,但第一个capture组还没有结束,这意味着第一个组匹配的结果是abracad ,而第二个组的匹配结果仅仅是cad。因此如果通过使用?符号而使cad成为一项可选的匹配,匹配的结果就可能是abra或abracad。然后,第一个组就会结束,通过指定+符号要求表达式进行多次匹配。
现在我们来看看匹配过程中发生的情况。首先,通过调用Regex的constructor方法建立表达式的一个实例,并在其中指定各种选项。在这个例子中,由于在表达式中有注释,因此选用了x选项,另外还使用了一些空格。打开x选项,表达式将会忽略注释和其中没有转义的空格。
然后,取得表达式中定义的组的编号的清单。你当然可以显性地使用这些编号,在这里使用的是编程的方法。如果使用了命名的组,作为一种建立快速索引的途径这种方法也十分有效。
接下来是完成第一次匹配。通过一个循环测试当前的匹配是否成功,接下来是从group 1开始重复对组清单执行这一操作。在这个例子中没有使用group 0的原因是group 0是一个完全匹配的字符串,如果要通过收集全部匹配的字符串作为一个单一的字符串,就会用到group 0了。
我们跟踪每个group中的CaptureCollection。通常情况下每次匹配、每个group中只能有一个capture,但本例中的Group1则有两个capture:Capture0和Capture1。如果你仅需要Group1的ToString,就会只得到abra,当然它也会与abracad匹配。组中ToString的值就是其CaptureCollection中最后一个Capture的值,这正是我们所需要的。如果你希望整个过程在匹配abra后结束,就应该从表达式中删除+符号,让regex引擎知道我们只需要对表达式进行匹配。
基于过程和基于表达式方法的比较
一般情况下,使用规则表达式的用户可以分为以下二大类:第一类用户尽量不使用规则表达式,而是使用过程来执行一些需要重复的操作;第二类用户则充分利用规则表达式处理引擎的功能和威力,而尽可能少地使用过程。
对于我们大多数用户而言,最好的方案莫过于二者兼而用之了。我希望这篇文章能够说明.NET语言中regexp类的作用以及它在性能和复杂性之间的优、劣点。
基于过程的模式
我们在编程中经常需要用到的一个功能是对字符串中的一部分进行匹配或其他一些对字符串处理,下面是一个对字符串中的单词进行匹配的例子:
string text = "the quick red fox jumped over the lazy brown dog.";
System.Console.WriteLine("text=[" + text + "]");
string result = "";
string pattern = @"/w+|/W+";
foreach (Match m in Regex.Matches(text, pattern))
{
// 取得匹配的字符串
string x = m.ToString();
// 如果第一个字符是小写
if (char.IsLower(x[0]))
// 变成大写
x = char.ToUpper(x[0]) + x.Substring(1, x.Length-1);
// 收集所有的字符
result += x;
}
System.Console.WriteLine("result=[" + result + "]");
正象上面的例子所示,我们使用了C#语言中的foreach语句处理每个匹配的字符,并完成相应的处理,在这个例子中,新创建了一个result字符串。这个例子的输出所下所示:
text=[the quick red fox jumped over the lazy brown dog.]
result=[The Quick Red Fox Jumped Over The Lazy Brown Dog.]
基于表达式的模式
完成上例中的功能的另一条途径是通过一个MatchEvaluator,新的代码如下所示:
static string CapText(Match m)
{
//取得匹配的字符串
string x = m.ToString();
// 如果第一个字符是小写
if (char.IsLower(x[0]))
// 转换为大写
return char.ToUpper(x[0]) + x.Substring(1, x.Length-1);
return x;
}
static void Main()
{
string text = "the quick red fox jumped over the
lazy brown dog.";
System.Console.WriteLine("text=[" + text + "]");
string pattern = @"/w+";
string result = Regex.Replace(text, pattern,
new MatchEvaluator(Test.CapText));
System.Console.WriteLine("result=[" + result + "]");
}
同时需要注意的是,由于仅仅需要对单词进行修改而无需对非单词进行修改,这个模式显得非常简单。
- 作者: muskteer 2005年07月30日, 星期六 14:32 回复(0) | 引用(0) 加入博采
常用正则表达式集锦
在使用RegularExpressionValidator验证控件时的验证功能及其验证表达式介绍如下:
只能输入数字:“^[0-9]*$”
只能输入n位的数字:“^/d{n}$”
只能输入至少n位数字:“^/d{n,}$”
只能输入m-n位的数字:“^/d{m,n}$”
只能输入零和非零开头的数字:“^(0|[1-9][0-9]*)$”
只能输入有两位小数的正实数:“^[0-9]+(.[0-9]{2})?$”
只能输入有1-3位小数的正实数:“^[0-9]+(.[0-9]{1,3})?$”
只能输入非零的正整数:“^/+?[1-9][0-9]*$”
只能输入非零的负整数:“^/-[1-9][0-9]*$”
只能输入长度为3的字符:“^.{3}$”
只能输入由26个英文字母组成的字符串:“^[A-Za-z]+$”
只能输入由26个大写英文字母组成的字符串:“^[A-Z]+$”
只能输入由26个小写英文字母组成的字符串:“^[a-z]+$”
只能输入由数字和26个英文字母组成的字符串:“^[A-Za-z0-9]+$”
只能输入由数字、26个英文字母或者下划线组成的字符串:“^/w+$”
验证用户密码:“^[a-zA-Z]/w{5,17}$”正确格式为:以字母开头,长度在6-18之间,
只能包含字符、数字和下划线。
验证是否含有^%&',;=?$/"等字符:“[^%&',;=?$/x22]+”
只能输入汉字:“^[/u4e00-/u9fa5],{0,}$”
验证Email地址:“^/w+[-+.]/w+)*@/w+([-.]/w+)*/./w+([-.]/w+)*$”
验证InternetURL:“^http://([/w-]+/.)+[/w-]+(/[/w-./?%&=]*)?$”
验证电话号码:“^(/(/d{3,4}/)|/d{3,4}-)?/d{7,8}$”
正确格式为:“XXXX-XXXXXXX”,“XXXX-XXXXXXXX”,“XXX-XXXXXXX”,
“XXX-XXXXXXXX”,“XXXXXXX”,“XXXXXXXX”。
验证身份证号(15位或18位数字):“^/d{15}|/d{}18$”
验证一年的12个月:“^(0?[1-9]|1[0-2])$”正确格式为:“01”-“09”和“1”“12”
验证一个月的31天:“^((0?[1-9])|((1|2)[0-9])|30|31)$”
正确格式为:“01”“09”和“1”“31”。
=========================================================================================================
/// <SUMMARY>
/// 校验字符串是否只包含字母与数字
/// </SUMMARY>
/// <PARAM name="toVerified">需要校验的字符串</PARAM>
/// <RETURNS>true表示符合要求,false表示不符合要求</RETURNS>
public static bool IsOnlyLetterAndDigit(string toVerified)
{
Regex rx = new Regex(@"^[a-zA-Z0-9-]*$");
return rx.IsMatch(toVerified.Trim(), 0);
}
/// <SUMMARY>
/// 检验是否是整数
/// </SUMMARY>
/// <PARAM name="str">需要检验的字符串</PARAM>
/// <RETURNS>是否为整数:true是整数,false非整数</RETURNS>
public static bool IsInt(string str)
{
Regex rx = new Regex(@"^[0123456789]+$");
return rx.IsMatch(str);
}
/// <SUMMARY>
/// 校验是否为正的浮点数
/// </SUMMARY>
/// <PARAM name="price">需要检验的字符串</PARAM>
/// <RETURNS>是否为正浮点,是返回true,否则返回false</RETURNS>
public static bool IsFloat(string str)
{
Regex rx = new Regex(@"^[0-9]*(.)?[0-9]+$", RegexOptions.IgnoreCase);
return rx.IsMatch(str.Trim());
}
/// <SUMMARY>
/// 检验是否为数字
/// </SUMMARY>
/// <PARAM name="str">需要检验的字符串</PARAM>
/// <RETURNS>是否为数字:true代表是,false代表否</RETURNS>
public static bool IsNumber(string str)
{
Regex rx = new Regex(@"^[+-]?[0123456789]*[.]?[0123456789]*$");
return rx.IsMatch(str);
}
/// <SUMMARY>
/// 检验字符串是否为日期时间
/// </SUMMARY>
/// <PARAM name="str">需要检验的字符串</PARAM>
/// <RETURNS>是否为日期时间:true代表是,false代表否</RETURNS>
public static bool IsDateTime(string str)
{
// Regex rx = new Regex(@"^[ ]*[012 ]?[0123456789]?[0123456789]{2}[ ]*[-]{1}[ ]*[01]?[0123456789]{1}[ ]*[-]{1}[ ]*[0123]?[0123456789]{1}[ ]*[012]?[0123456789]{1}[ ]*[:]{1}[ ]*[012345]?[0123456789]{1}[ ]*[:]{1}[ ]*[012345]?[0123456789]{1}[ ]*$");
Regex rx = new Regex(@"(([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29)")
//Regex(@"[1-2]{1}[0-9]{3}((-|//){1}(([0]?[1-9]{1})|(1[0-2]{1}))((-|//){1}((([0]?[1-9]{1})|([1-2]{1}[0-9]{1})|(3[0-1]{1})))( (([0-1]{1}[0-9]{1})|2[0-3]{1}):([0-5]{1}[0-9]{1}):([0-5]{1}[0-9]{1})(/.[0-9]{3})?)?)?)?$");
return rx.IsMatch(str);
}
/// <SUMMARY>
/// 检验字符串是否为邮政编码
/// </SUMMARY>
/// <PARAM name="str">需要检验的字符串</PARAM>
/// <RETURNS>是否为邮政编码:true代表是,false代表否</RETURNS>
public static bool IsPostCode(string str)
{
Regex rx = new Regex(@"^[0123456789]{6}$");
return rx.IsMatch(str);
}
/// <SUMMARY>
/// 检验字符串是否为身份证号
/// </SUMMARY>
/// <PARAM name="str">需要检验的字符串</PARAM>
/// <RETURNS>是否为身份证号:true代表是,false代表否</RETURNS>
public static bool IsCode(string str)
{
Regex rx = new Regex(@"^[0123456789]{15,18}$");
return rx.IsMatch(str);
}
/// <SUMMARY>
/// 检验字符串是否为电子邮件
/// </SUMMARY>
/// <PARAM name="str">需要检验的字符串</PARAM>
/// <RETURNS>是否为电子邮件:true代表是,false代表否</RETURNS>
public static bool IsEMail(string str)
{
Regex rx = new Regex(@"w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*");
return rx.IsMatch(str);
}
/// <SUMMARY>
/// 检验字符串是否为中国地区的电话号码
/// </SUMMARY>
/// <PARAM name="str">需要检验的字符串</PARAM>
/// <RETURNS>是否为中国地区的电话号码:true代表是,false代表否</RETURNS>
public static bool IsPhoneNumber(string str)
{
Regex rx = new Regex(@"((d{3,4})|d{3,4}-)?d{7,8}(-d{3})*");
return rx.IsMatch(str);
}
/// <SUMMARY>
/// 检验字符串是否为汉字
/// </SUMMARY>
/// <PARAM name="str">需要检验的字符串</PARAM>
/// <RETURNS>是否为汉字:true代表是,false代表否</RETURNS>
public static bool IsChinese(string str)
{
Regex rx = new Regex(@"u4e00-u9fa5");
return rx.IsMatch(str);
}
/// <SUMMARY>
/// 检验字符串是否为双字节字符(包括汉字)
/// </SUMMARY>
/// <PARAM name="str">需要检验的字符串</PARAM>
/// <RETURNS>是否为双字节字符:true代表是,false代表否</RETURNS>
public static bool IsDoubleByteChar(string str)
{
Regex rx = new Regex(@"[^x00-xff]");
return rx.IsMatch(str);
}
/// <SUMMARY>
/// 检验字符串是否为URL地址
/// </SUMMARY>
/// <PARAM name="str">需要检验的字符串</PARAM>
/// <RETURNS>是否为URL地址:true代表是,false代表否</RETURNS>
public static bool IsURLAddress(string str)
{
Regex rx = new Regex(@"[a-zA-z]+://[^s]*");
return rx.IsMatch(str);
}
/// <SUMMARY>
/// 检验字符串是否为IP地址
/// </SUMMARY>
/// <PARAM name="str">需要检验的字符串</PARAM>
/// <RETURNS>是否为IP地址:true代表是,false代表否</RETURNS>
public static bool IsIPAddress(string str)
{
Regex rx = new Regex(@"d+.d+.d+.d+");
return rx.IsMatch(str);
}
/// <SUMMARY>
/// 清除字符串中的HTML标签(对于复杂的嵌套标签有时不准确)
/// </SUMMARY>
/// <PARAM name="toEvaluate">指定的要被处理的字符串</PARAM>
/// <RETURNS>清除HTML标签后的字符串</RETURNS>
public static string RemoveHtmlTags(string toEvaluate)
{
Regex rx = new Regex(@"s/<[a-zA-Z/][^>]*>//g", RegexOptions.IgnoreCase);
return rx.Replace(toEvaluate, "");
}