正则表达式入门(2)
示例2
e-mail的匹配
就从博客园的联系方式页面来找就好了,地址是 http://www.cnblogs.com/ContactUs.aspx
准备工作:
获取页面内容,方法如下:
static string DownloadString(Uri url, Encoding encoding) { WebClient client; using (client = new WebClient()) { return encoding.GetString(client.DownloadData(url)); } }
只适用于简单的情况,已经足够了,下面是示例
static void example2() { Uri url; Encoding encoding; string content; Regex regex; MatchCollection matches; string expression; url = new Uri("http://www.cnblogs.com/ContactUs.aspx"); encoding = Encoding.UTF8;//博客园是utf-8的,直接指定了 content = DownloadString(url, encoding); expression = "[-_0-9A-Z]{1,20}(@|&\\#64;)[-0-9A-Z]+(\\.com|\\.net)"; regex = new Regex(expression, RegexOptions.IgnoreCase | RegexOptions.Singleline); matches = regex.Matches(content); foreach (Match match in matches) { Console.WriteLine(string.Concat("'", match.Value.Replace("@", "@"), "'")); } }
e-mail 的格式为 {用户名}@{域名}
为了看起来比较简单,我们假设用户名只能由"-","_",0-9的数字和A-Z的字母 组成
所以用户名部分就是[-_0-9A-Z],但是有一点需要注意,第一个"-"和后面的"-"的含义是不一样的,第一个的意思是"-"这个字符本身,后面的代表的是范围,范围是通过"a-b"这样的方式来确定的,如果需要匹配"-"这个字符本身,尽量不要放在歧义的位置。
然后是第二部分 "(@|&\\#64;)" 是包含在括号之间的,原因在于这里由2部分组成,其中用|分开,表示多选一,"@" 或者 "@"(@的ascii就是64,可以用"@", "#"字符在正则中需要转义,需要转义的字符可以在msdn中找到,地址是 http://msdn.microsoft.com/zh-cn/library/system.text.regularexpressions.regex.escape(v=vs.110).aspx,需要转义的字符只要在前面加上"\\"即可)如果外面没有括号来限制,那这个多选分支的范围就扩大了,把"|"两边作为一个多选分支,那就变成了 "{用户名}@" 或者 "@{域名}",不是我们想要的情况。
[-0-9A-Z]+(\\.com|\\.net)
最后是域名部分,目前只接受".net"或者".com"这2个顶级域名 类似 "cnblogs.com" 这样的形式
前面部分由至少一个的 数字,字母或者"-"组成,后面只允许".com"或者".net"还是用|来做到
最后 RegexOptions.IgnoreCase 表示忽略大小写,不然,这个表达式可能要写成
"[-_0-9A-Za-z]{1,20}(@|&\\#64;)[-0-9A-Za-z]+(\\.[cC][oO][mM]|\\.[nN][eE][tT])"
不够直观
好了,看一下结果
'contact@cnblogs.com'
'contact@cnblogs.com'
一共有2个匹配
不过,如果要把用户名和域名都取出来好像有点麻烦
稍微改进一下,代码如下:
static void example2() { Uri url; Encoding encoding; string content; Regex regex; MatchCollection matches; string expression; url = new Uri("http://www.cnblogs.com/ContactUs.aspx"); encoding = Encoding.UTF8; content = DownloadString(url, encoding); expression = "([-_0-9A-Z]{1,20})(@|&\\#64;)([-0-9A-Z]+(\\.com|\\.net))"; regex = new Regex(expression, RegexOptions.IgnoreCase | RegexOptions.Singleline); matches = regex.Matches(content); foreach (Match match in matches) { Console.WriteLine(string.Format( "e-mail: '{0}', userName: '{1}', domain: '{2}'", match.Groups[0].Value.Replace("@", "@"), match.Groups[1].Value, match.Groups[3].Value )); } }
先看看改动
"[-_0-9A-Z]{1,20}" 变成了 "([-_0-9A-Z]{1,20})"
"[-0-9A-Z]+(\\.com|\\.net)" 变成了 "([-0-9A-Z]+(\\.com|\\.net))"
只是在外面加了对括号
默认情况下,会为每对括号创建一个捕获分组(capture group)
看一下完整的表达式 "([-_0-9A-Z]{1,20})(@|&\\#64;)([-0-9A-Z]+(\\.com|\\.net))"
第一个分组是:"([-_0-9A-Z]{1,20})"
第二个分组是:"(@|&\\#64;)"
第三个分组是:"([-0-9A-Z]+(\\.com|\\.net))"
第四个分组是:"(\\.com|\\.net)"
其中第四个是嵌套在第三个之中
这样,就可以通过Match.Groups[下标]来获取对应的分组了,其中Groups[0]就是完整的匹配
这样还是不够直观,再做一次更改
改动后代码如下:
static void example2() { Uri url; Encoding encoding; string content; Regex regex; MatchCollection matches; string expression; url = new Uri("http://www.cnblogs.com/ContactUs.aspx"); encoding = Encoding.UTF8; content = DownloadString(url, encoding); expression = "(?<userName>[-_0-9A-Z]{1,20})(@|&\\#64;)(?<domain>[-0-9A-Z]+(\\.com|\\.net))"; regex = new Regex(expression, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.ExplicitCapture); matches = regex.Matches(content); foreach (Match match in matches) { Console.WriteLine(string.Format( "e-mail: '{0}', userName: '{1}', domain: '{2}'", match.Groups[0].Value.Replace("@", "@"), match.Groups["userName"].Value, match.Groups["domain"].Value )); } }
"([-_0-9A-Z]{1,20})"变成了"(?<userName>[-_0-9A-Z]{1,20})"
"([-0-9A-Z]+(\\.com|\\.net))"变成了"(?<domain>[-0-9A-Z]+(\\.com|\\.net))"
都是在括号开始的地方加上了?<名称>
这是命名分组捕获可以用以下方式调用
match.Groups["userName"]
match.Groups["domain"]