正则表达式

  • 正则表达式是用来进行文本处理的技术,是语言无关的,在几乎所有语言中都有实现。javascript中还会用得到。
  • 一个正则表达式是由普通字符串以及特殊字符字符(称为元字符)组成的文字模式,该模式描述在查找文字主体时,待匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行比较。
  • 就像通配符“*.jpg”、“%ab%”,它是对字符串进行匹配的特殊字符串。
  • 正则表达式是非常复杂的,不要希望一次能掌握,理解正则表达式能做什么(字符串的匹配,字符串的提取,字符串的替换),掌握常用的正则表达式语法,以后用到再查就行。
  • 找工作的亮点,后面项目中的采集器,敏感词过滤、URLRewite、Validator也会涉及到正则表达式。
  • 正则表达式是对字符串进行操作的
  • 理解元字符是一个必须攻克的难关

(1).:匹配除\n之外的任何一个字符。例如正则表达式“b.g”能匹配如下字符串:“big”、“bug”、“b g”,但是不匹配“buug”,"b..g"可以匹配“buug”。

(2)[]:匹配括号中的任何一个字符。例如正则表达式“b[aui]g”匹配bug、big和bag,但是不匹配beg、baug。也可以在括号中使用连字符“-”来指定字符的区间。例如正则表达式[0-9]可以匹配任何数字字符,这样正则表达式"a[0-9]c"等价于"a[0123456789]c"就可以匹配“a0c”、“a1c”,"a2c"等字符串;还可以制定多个区间,例如“[A-Za-z]”可以匹配任何大小的字母,“[A-Za-z0-9]”,可以匹配任何的大小写字母或者数字。

匹配11-20之间的数字,正则表达式为[12][0-9],不能写成[10-29],因为这里的10和29只是表示字符1 或0 2或9

(2)|:将这个匹配条件进行逻辑"或"运算。‘z|food’能匹配“z”或“food”。‘(z|f)ood’则匹配“zood”或“food”。注意^$。[ab]等价于a|b。匹配的时候将"|"左右两边分别作为一个整体

(3)():将()之间括起来的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域,这个元字符在字符串提取的时候非常有用。把一个字符串表示为一个整体。改变优先级,定义提取组两个作用。

限定符:限定前面的表达式出现的次数。

(4)*:匹配多个在它之前的子表达式,和通配符没有关系。例如正则表达式“zo*”等同于“z(o*)”能匹配“z”,"zo"以及"zoo",因此"*“意味着能够匹配任意字符串。

”z(b|c)*“可以匹配zb,zbc,zcb,zcccc,zbbbcc。”z(ab)*"能匹配zab,zabab(用括号表示优先级)。

(5)+:匹配前面的子表达式一次或多次,和*对比(0到多次)。例如正则表达式,9+匹配9,99,999等。"zo+“以及”zoo",不能匹配"z“。

(6)?:匹配前面的子表达式零次或一次。例如”do(es)?"可以匹配"do“,”does"。一般用来匹配"可选部分“。

(7){n}:匹配确定的n次。”zo{2}“-zoo。例如”e{2}“不能匹配”bed“中的”e“,但是能匹配”seed“中的两个”e“。

(8){n,}:至少匹配n次。例如”e{2,}“不能bed中的”e“,但能匹配”seeeeeed“中的所有”e“。

(9){n,m}:最少匹配n次最多匹配m次。”e{1,3}“

 (10)^匹配一行的开始,例如正则表达式”^regex“能够匹配字符串“^regex我会用”的开始,但是不能匹配“我会用regex”。^另外一种意思:非[^0-9],放在中括号里面表示非,其余地方都表示为匹配一行的开始

(11)$匹配行结束符。例如正则表达式“浮云$”,能够匹配字符串“一切都是浮云”的末尾,但是不能匹配字符串“浮云呀”。

  •  简写表达式

♦注意简写表达式是不考虑转义符的。这里的\就表示字符\,而不是c#级别的\,在c#代码中使用@或\双重转义。区分c#级别的转义和正则表达式级别的转义,恰好c#的转义\和正则表达式级别的转义符都是\而已。正则表达式的转义是在c#之后的(层层盘剥)。把c#的转义想成%就明白了@“\."就是"\.“这个普通的字符串,只不过在正则表达式来看,它有了特殊的含义@”\d“或者”\\d“。

-》\d:代表一个数字,等同于[0-9]

-》\D:代表非数字,等同于[^0-9]

-》\s:代表换行符、Tab制表符等空白字符(一般我们用[\s\S]来匹配任意字符串)

-》\S:代表非空白字符

-》\w:匹配字母或数字或下划线或汉字,即能组成单词的字符。

-》\W:非\w,等同于[^\W]

d:digital;s:space;w:word。大写就是”非“

  • 简单正则表达式的练习

  1.匹配正确的邮政编码   ^\d{6}$

  2.判断是是不是身份证号码(15位数字或18位数字后面可能有X)^(\d{15}|\d{18}[xX]?)$   或者^(\d{15}|\d{17}[dxX])$

  3.匹配国内电话号

    -》三位或四位区号-八位或七位电话号码 ^((\d{3}|\d{4})-(\d{8}|\d{7}))$

    -》11位数字的手机号码 ^\d{11}$(不严格)

    -》服务电话10086、10010、95599、95588等  ^((100|955)\d{2})$(不严格)

  •  .Net的正则表达式

♦正则表达式在.Net中就是用字符串表示,这个字符串格式比较特殊,无论多么特殊,在c#语言看来都是普通的字符串,具体什么含义由Regex类内部进行分析。

♦如何匹配大于10小于20的字符串^[1][1-9]$(正则表达式是对字符串的操作)

♦正则表达式(Regular Expression)的主要类:Regex

♦常用的四种情况:(C#语法)

-》判断是否匹配Regex.IsMatch(”字符串","正则表达式“)

-》字符串提取:Regex.Match(”字符串","要提取的字符串的正则表达式")//只能提取一个(提取一次)

-》字符串提取(循环提取所有)Regex.Matchs()//可以提取所有匹配的字符串

-》字符串替换Regex.Replace("字符串",“正则”,“替换内容”)

   string str = "fjdjfdfd fj123jdkfjdjfdjf45dfjkdjfkdj1233";
            Match m=Regex.Match(str,@"\d+");//返回第一个匹配的字符串
            if (m.Success)
            {
                Console.WriteLine("匹配到的字符串为{0}",m.Value);
                Console.WriteLine("匹配的字符串的起始位置是{0}",m.Index);
                Console.WriteLine("匹配到的字符串长度为{0}", m.Length);
            }
            MatchCollection mc=Regex.Matches(str,@"\d+");//匹配所有的字符串
            Console.WriteLine("==================");
            //用for循环遍历
            for (int i = 0; i < mc.Count; i++)
            {
                Match m1 = mc[i];
                Console.WriteLine("匹配到的字符串为{0}", m1.Value);
                Console.WriteLine("匹配的字符串的位置是{0}", m1.Index);
                Console.WriteLine("匹配到的字符串长度为{0}", m1.Length);
                Console.WriteLine();
            }
            Console.WriteLine("===================");
            //foreach遍历
            foreach(Match item in mc){
                Console.WriteLine(item.ToString());
            }
            Console.ReadKey();
View Code
  • 难点:利用圆括号对匹配的结果进行分组

(案例,提取文本文件里特定格式的字符串)

        //读取html文本<table style="width: 100%; height: 488px;">
        //<tr>
        //    <td>
        //        <img alt="" src="hotgirls/00_00.jpg" /></td>
        //    <td>
        //        <img alt="" src="hotgirls/00_01.jpg" /></td>
        //    <td>.........  里的图片路径
            string html = System.IO.File.ReadAllText("美女们.htm",Encoding.Default);
            string regex = "src=\"(.+)\"";//正则表达式中圆括号有分组的功能,从左往右数圆括号,一次为Groups[1]、Groups[2]、Groups[3]......
            MatchCollection mc=Regex.Matches(html, regex);
            foreach (Match m in mc)
            {
                Console.WriteLine(m.Groups[1]);
            }
            Console.ReadKey();

再如例子

          string str = "012345";
                                 //   12  3    4    5
            string regex = @"((\d(\d))(\d))(\d)";
            Match m = Regex.Match(str, regex);
            Console.WriteLine(m.Value);//结果为0123
            Console.WriteLine("第1组为:"+m.Groups[1]);//结果为0123
            Console.WriteLine("第2组为:" + m.Groups[2]);//结果为012
            Console.WriteLine("第3组为:" + m.Groups[3]);//结果为01
            Console.WriteLine("第4组为:" + m.Groups[4]);//结果1
            Console.WriteLine("第5组为:" + m.Groups[5]);//结果为0
            Console.ReadKey();

上述代码中,利用了正则表达式圆括号分组功能,直接将匹配到的字符串进行分组以提取我们自己所需要的内容。

所以,我们可以尝试用正则匹配一个文件的全路径的文件名和后缀名

           string file = @"C:\123\4565\789\abcd.mp3";
            string regex = @"\\([^\\]+)(\.[^\\]+)$";
            MatchCollection m = Regex.Matches(file, regex);
            foreach (Match item in m)
            {
                Console.WriteLine(item.Value);//匹配结果为abcd.mp3
                Console.WriteLine(item.Groups[1].Value);//匹配结果为abcd
                Console.WriteLine(item.Groups[2].Value);//匹配结果为.mp3
            }
            Console.ReadKey();

易错题目:

            string s=@"((\d)(\w))+";
            string r="1a2b3c";
            Match m = Regex.Match(r,s);
            MatchCollection mc   =Regex.Matches(r,s);
            Console.WriteLine(m.Groups[0]);//结果为1a2b3c
            Console.WriteLine(m.Groups[1]);//结果为3c,因为第一组(即第一个括号)是匹配一个数字和一个字符。本来最先匹配到的是1a,但后面陆续被2b,3c覆盖了所以最后结果为3c。好比一个变量第一次存储1a,第二次改为存储2b,第三次为3c
            Console.WriteLine(m.Groups[2]);//结果为3
            Console.WriteLine(m.Groups[3]);//结果为c
            Console.ReadKey();
        }

若上述易错题中,将((\d)(\w)+),那么匹配的第一组就是1a2b3c,因为第一组(第一个括号内)匹配的表达式为为一个或者多个\d\w

  • 匹配IP地址,4段用.分割的最多三位数字。192.168.54.77,假设333.3333.3.33也是正确的 ^(\d{1,3}(\.\d{1,3}){3})$

  说明:以上匹配IP地址的正则表达式其实严格意义上说是不正确的。IP地址的每个节点其实都是0-255的数字

  严谨的IP:^(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])$

  6.匹配Url(比如:http://www.test.com/a.htm、ftp://127.0.0.1/1.txt)

   说明:url是uniform resource locator

   组成://地址/目录?参数1=参数值&参数1=参数值....

   简单解释:协议http(hyper text translate protocal)传输协议  网站 ;协议 ftp(file translate protocal ) 下载;

        端口:入口

        目录:定位一台计算机下的目录文件下

  匹配url的正则表达式:^\W+://.+$ 

   

  string regex =  @"^(https?|ftp)://([^:]+?)(:\d+)?(/[^\?]+?(\?\w+=\w+(&\w+=\w+)*)?)?$";
           // regex =   @"(.+?)://(.+?)(:\d+)?(/.+?)\?(.+)";//也可以
            string url1 = @"http://www.123.com:8081/1.html?k=1&y=2";
            string url2 = @"http://www.123.com/yangzhongke?wd=1";
            Console.WriteLine(Regex.IsMatch(url1,regex));//结果为true
            Console.WriteLine(Regex.IsMatch(url2,regex));//结果为true
            Console.ReadKey();

 

  • 正则表达式的贪婪模式(难点)
//贪婪模式:从左往右递减,正则表达式只有两个权级,一个是尽可能匹配,一个尽可能不匹配
            string  str="12345";
            string regex=@"(\d+)(\d+)(\d+)";
            Match mc=Regex.Match(str,regex);
            Console.WriteLine(@"第一个\d+匹配到的为:"+mc.Groups[1]);//结果匹配为123
            Console.WriteLine(@"第二个\d+匹配到的为:"+mc.Groups[2]);//结果匹配为4
            Console.WriteLine(@"第三个\d+匹配到的为:"+mc.Groups[3]);//结果匹配为5
            Console.WriteLine("==========================");

            string regex1 = @"(\d*?)(\d*)(\d+)";//问号表示取消贪婪模式
            Match mc1 = Regex.Match(str, regex1);
            Console.WriteLine(@"第一个\d+匹配到的为:" + mc1.Groups[1]);//结果为不匹配
            Console.WriteLine(@"第二个\d+匹配到的为:" + mc1.Groups[2]);//结果匹配为1234
            Console.WriteLine(@"第三个\d+匹配到的为:" + mc1.Groups[3]);//结果匹配为5
            Console.ReadKey();

 

那么,接下来我们可以尝试用正则表达式来实现Path类的几个静态方法:GetDirectoryName()、GetFileName()、GetExtension()、GetFileNameWitoutExtension()

            string file = @"C:\123\4565\789\abcd.mp3";       
            string regex = @"((.:)\\.+\\)((.+)(\..+))";
            Match mc = Regex.Match(file, regex);
            Console.WriteLine("匹配的原始数据为:" + mc.Value);
            Console.WriteLine("文件所在的磁盘为:" + mc.Groups[2]);
            Console.WriteLine("文件的全路径为:" + mc.Groups[1]);
            Console.WriteLine("文件的文件名为:" + mc.Groups[3]);
            Console.WriteLine("文件的后缀名为:" + mc.Groups[5]);
            Console.WriteLine("文件的去后缀名的文件名为:" + mc.Groups[4]);
                    // @"((.:)\\.+\\)((.+)(\..+))";
            Console.WriteLine("===============");

            string file1= @"C:\abcd.mp3";
            regex = @"((.:)\\(.+\\)?)((.+)(\..+))";
            Match mc1 = Regex.Match(file1,regex);
            Console.WriteLine("匹配的原始数据为:" + mc1.Value);
            Console.WriteLine("文件所在的磁盘为:" + mc1.Groups[2]);
            Console.WriteLine("文件的全路径为:" + mc1.Groups[1]);
            Console.WriteLine("文件的文件名为:" + mc1.Groups[4]);
            Console.WriteLine("文件的后缀名为:" + mc1.Groups[6]);
            Console.WriteLine("文件的去后缀名的文件名为:" + mc1.Groups[5]);
            Console.ReadKey();
  •  案例讲解

1.练习从网站上下载图片

搭建一个服务器(运行CassiniDev.exe),将我们计算机硬盘里存储的html文件的物理路径复制到CasiniDev PysicalPath下面的文本框内,点击start,即可以生成一个可访问的网址

步骤:

(1)运行CassiniDev.exe

 (2)将default.htm所在的物理路径放入CassiniDev 地下的PysicalPath下的文本框内

(3)点击start

代码: 

        WebClient wc =new WebClient();//用来从网站上下载资源的类
            string html = wc.DownloadString("http://localhost:8080/default.htm");
            string r= @"<img alt="""" src=""(.+)"" />";
               // r = @"<img.+?src=""(.+?)""";
            MatchCollection mc = Regex.Matches(html, r);
            foreach (Match item in mc)
            {
                Console.WriteLine(item.Groups[1]);
                string path = "http://localhost:8080/" + item.Groups[1];
                //下载使用WebClient的DownLoadFile(网上路径,本地路径);
                string fileName =@"D:\"+Regex.Match(item.Groups[1].Value, @"/(.+?)$").Groups[1].Value;
                wc.DownloadFile(path,fileName );
            }
            Console.ReadKey();

 2.从网页上提取Email

            WebClient wc = new WebClient();
            wc.Encoding = Encoding.UTF8;//因为该html文件的charset=UTF-8;
            string htmlText = wc.DownloadString("http://localhost:8080/大家留下email交友吧_email_天涯社区.htm");
            string regex = @"[\da-zA-Z\.\-_]+@\w+\.[a-zA-Z]+";
            MatchCollection mc = Regex.Matches(htmlText, regex);
            foreach (Match item in mc)
            {
                Console.WriteLine(item.Value);
                //System.IO.File.WriteAllText(@"E:\上课\第12天\课堂代码\下载Email\bin\Debug\结果.txt",item.Value);
            }
            Console.ReadKey();

 3.从招聘网页上提取岗位名称

           WebClient wc = new WebClient();
            string htmltxt = wc.DownloadString("http://localhost:8080/【上海,IT-管理,计算机软件招聘,求职】-前程无忧.htm");
            string regex = @"<a href=""http://search.51job.com/job/\d+,c.html"" onclick=""zzSearch.acStatRecJob\( 1 \);"" class=""jobname"" target=""_blank"">(.+)</a>";
            MatchCollection mc = Regex.Matches(htmltxt,regex);
            foreach( Match item in mc)
            {
                Console.WriteLine(item.Groups[1]);
            }
            Console.ReadKey();
  •  正则表达式替换的简单说明

说明:Regex.Replace(string 需要操作的字符串,string 需要替换的字符串,string替换为的字符串)跟string <string>.Replace()相似。正则表达式相对功能更强大

代码:

//将 "2013年8月9日"替换为“2013-8-9”
            //解法一
            string str = "2013年8月9日";
            str = Regex.Replace(str, "年|月", "-");
            str = Regex.Replace(str, "", "");
            Console.WriteLine(str);
            //解法二
            string str1 = "2013年9月9日";
            //正则表达式中,凡是组里面的数组,都可以用"$组号"进行引用
            str1 = Regex.Replace(str1,@"(\d+)年(\d+)月(\d+)日","$1-$2-$3");
            Console.WriteLine(str1);
            //“$组号”引用匹配组的再举例
            string str2=Regex.Replace("123",@"\d","$0$0");
            Console.WriteLine(str2);
            Console.ReadKey();
  • 匹配替换案例:

1.“压缩”文件(简单压缩)

        //要缩文件,将js文本中的空格及注释都去掉。
            string txt = System.IO.File.ReadAllText(@"E:\上课\第12天\课堂代码\正则表达式的替换\bin\Debug\jkuery_20131031.js");
            txt = Regex.Replace(txt, @"//.+", "");
            txt = Regex.Replace(txt,@"\s+","");
            System.IO.File.WriteAllText(@"E:\上课\第12天\课堂代码\正则表达式的替换\bin\Debug\结果.js",txt);
            //我们还可以分析词法,将所有变量名都替换掉

 2.将网址文本改为链接网址

       string text = System.IO.File.ReadAllText("网址大全.txt", Encoding.Default);
            string regex1 = @".+";
            text = Regex.Replace(text, regex1, "<a href=\"$0\">$0</a>");//直接加链接
            System.IO.File.WriteAllText(@"E:\上课\第12天\课堂代码\课堂练习\bin\Debug\结果.txt1", text);

            string regex = @"(https://)?(.+)";
            text=Regex.Replace(text,regex,"<a href=\"http://$2\">$2</a>");//让所有链接前都有一个http://
            System.IO.File.WriteAllText(@"E:\上课\第12天\课堂代码\课堂练习\bin\Debug\结果.txt", text);

 3.给文件名加后一个缀

        Console.WriteLine("请输入一个文件名");
            string str = Console.ReadLine();
            str=Regex.Replace(str,@"(.+)\.(.+)","$1_mim.$2");
            Console.WriteLine(str);
            Console.ReadKey();

 


 

posted @ 2014-02-03 12:35  我叫小菜  阅读(456)  评论(0编辑  收藏  举报