- 元字符[和]用来定义一个字符集合,其含义是必须匹配该集合里面的字符之一。
- \w:任何一个字母数字字符(大小写均可)或下划线字符(等价于[a-zA-Z0-9_]),只能匹配单个字符。
- \W: 任何一个非字母数字或非下划线字符。
- \s:任何一个空白字符(等价于[\f\n\r\t\v])。
- \S:任何一个非空白字符(等价于[^\f\n\r\t\v])。
- https? 这个正则表达式的意思是:既可以匹配http,也可以匹配https,?在这里的含义是:我前面的字符(s)要么不出现,要么最多出现一次。
- {}大括号,它的最后一种用法是给出一个最小的重复次数(但不必给出一个最大值).比如说,{3,}表示至少重复3次,与之等价的说法就是”必须重复3次或更多次”。
- 懒惰型匹配法: *? (它是*的懒惰型版本)。
- 常用的贪婪型元字符和他们的懒惰型版本
贪婪型元字符 |
懒惰型元字符 |
* |
*? |
+ |
+? |
{n,} |
{n,}? |
- 边界限定
如利用cat可以匹配cat,但是也可以匹配category,如果我们只想匹配cat,则只需要加上\bcat\b即可。(b为boundary(边界)的意思),如果不匹配一个单词的边界,可以使用\B.
- .*表示匹配的任意字符(.的零次或多次重复出现)
- 匹配JavaScript的注释行的方法如下:
(?m)^\s*//.*$ |
解释如下:
(?m) 单行匹配
^…$ 匹配的开始和结束符号
\s* 匹配零个或多个空白字符
// 匹配注释符号
.* 匹配注释符号后的任意字符
- 子表达式为(和),它将被视为一个独立元素,我们来看看匹配IP的正则表达式,一般的匹配方法如下:
\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}
简化如下:
(\d{1,3}.){3}\d{1,3}
但是会出现问题,那也就是999.999.999.999也会匹配,这个是不正确的,正确的IP应该有以下几个特性:
- 一位或两位 :这个可以随便匹配(\d{1,2})
- 三位,但是开头为1: 1\d{2}
- 三位,但是开头为2:2[0-4]\d
- 三位,但是开头为25:25[0-5]
那么将上面的规律综合起来,就是:
(((\d{1,2})|(1\d{2})|(2[0-4]\d)|( 25[0-5]))\.){3}((\d{1,2})|(1\d{2})|(2[0-4]\d)|( 25[0-5]))
- 回溯匹配
假设我们在一段文本中想找出有哪些字符是重复拼写的,这个应该如何来表示呢?
文本内容如下:
This is is a text, and I want to know know if there is any repeated words in it.
那么匹配的正则表达式如下:
[ ]+(\w+)[ ]+\1
这样就可以成功的找出我们需要的结果,如下:
is is
know know
它是如何做到这一点的呢?[ ]+匹配一个或多个空格,\w+匹配一个或多个字母数字字符,[ ]+匹配随后的空格。注意,\w+是括在括号里面的,它是一个子表达式。这个子表达式不是用来进行重复匹配的,这里根本不涉及重复匹配的问题。这个子表达式只是把整个模式的一部分单独划分出来以便在后面引用,这个模式的最后一部分是\1,这是一个回溯引用,而它引用的正是前面划分出来的那个子表达式:当(\w+)匹配到单词is的时候,\1也匹配单词is;当(\w+)匹配到单词know的时候,\1也匹配到know.
那么\1到底代表什么?它代表着模式里的第一个子表达式。\2代表第二个子表达式,\3代表着第三个子表达式,以此类推。于是在上面的那个例子里,[ ]+(\w+)[ ]+\1将匹配同一个单词的重复两次的出现。
- ==============================================================================
基本的元字符
. |
匹配任意单个字符 |
| |
逻辑或操作符 |
[] |
匹配字符集合中的一个字符 |
[^] |
对字符集合求非 |
- |
定义一个区间(例如A-Z) |
\ |
对下一个字符转义 |
数量元字符
* |
匹配前一个字符(子表达式)的零次或多次重复 |
*? |
*的懒惰型版本 |
+ |
匹配前一个字符(子表达式)的一次或多次重复 |
+? |
+的懒惰型版本 |
? |
匹配的前一个字符(子表达式)零次或一次重复 |
{n} |
匹配前一个字符(子表达式)的n次重复 |
{m,n} |
匹配前一个字符(子表达式)至少m次其至多n次重复 |
{n,} |
匹配前一个字符(子表达式)n次或更多次重复 |
{n,}? |
{n,}的懒惰型版本 |
位置元字符
^ |
匹配字符串的开头 |
\A |
匹配字符串的开头 |
$ |
匹配字符串的结束 |
\Z |
匹配字符串的结束 |
\< |
匹配单词的开头 |
\> |
匹配单词的结束 |
\b |
匹配单词边界(开头和结束) |
\B |
\b的反义 |
特殊字符元字符
[\b] |
退格字符 |
\c |
匹配一个控制字符 |
\d |
匹配任意数字字符 |
\D |
\d的反义 |
\f |
换页符 |
\n |
换行符 |
\r |
回车符 |
\s |
匹配一个空白字符 |
\S |
\s的反义 |
\t |
制表符(Tab字符) |
\v |
垂直制表符 |
\w |
匹配任意字母数字字符或下划线字符 |
\W |
\w的反义 |
\x |
匹配一个十六进制数字 |
\o |
匹配一个八进制数字 |
回溯引用和前后查找
() |
定义一个子表达式 |
\1 |
匹配第一个子表达式,\2代表第二个子表达式,以此类推 |
?= |
向前查找 |
?<= |
向后查找 |
?! |
负向前查找 |
?<! |
负向后查找 |
?() |
条件(if then) |
?()| |
条件(if then else) |
大小写转换
\E |
结束\L或者\U的转换 |
\l |
把下一个字符转换为小写 |
\L |
把后面的字符转换为小写,直到遇见\E为止 |
\u |
把下一个字符转换为大写 |
\U |
把后面的字符转换为大写,知道遇见\E为止 |
匹配模式
(?m) |
分行匹配模式 |
- 向前查找
从语法上看,一个向前查找模式其实就是一个以?=开头的子表达式,需要匹配的文本跟在=的后面。
比如我们需要知道一些URL用的是http还是https,则可以利用向前查找:
http://www.cnblogs.com
正则匹配为:.+(?=:)
结果为: http
如果利用.+(:) ,则为 http:
- 向后查找
也就是查找出现在被匹配文本之前的字符(但不消费它),操作符是?<=
文本为:ABC0: $12.56
匹配为: (?<=\$)[0-9.]+
如果不佳?<=,结果为$12.56,反之为12.56
- 向前向后查找集合
例如以下文本:
<head>
<title>Ben Forta’s HomePage</title>
</head>
这里我们如果想得到<title>与</title>标签内的内容,但是不包含<title>和</title>标签,如果不利用向前向后查找的话,将显得异常麻烦。利用向前向后匹配,只需要一个正则表达式就可以搞定:
正则匹配为:(?<=<title>).*?(?=</title>)
刚刚说道的向前向后查找,说准确点应该叫做正向前查找和正向后查找。当然,这里还存在这负向前查找和负向后查找:
操作符 |
说明 |
(?=) |
正向前查找 |
(?!) |
负向前查找 |
(?<=) |
正向后查找 |
(?<!) |
负向后查找 |
- 负向后查找
文本为:I paid $30 for 100 apples.
匹配为:\b(?<!\$)\d+\b
这个的意思是查找不带有$符号的数字,这里的匹配结果是100
当然,负向前查找和这个使用方式类似,暂略。
- 正则表达式中的嵌入条件
它是正则表达式中威力强大但是不经常使用的。所谓的条件就是利用?来定义。嵌入条件无外乎以下两种情况:
- 根据一个回溯引用来进行条件处理
- 根据一个前后查找来进行条件处理
首先针对回溯引用条件:如果你需要将一段文本中的<img>标签全部都找出来,不仅如此,若整个<img>标签,是一个链接(被包括在<A>和</A>标签之间)的话,你还要把整个链接标签匹配出来。
用来定义这种条件的语法是?(backreference)true-regex, 其中?表明这是一个条件,括号里的backreference是一个回溯引用,true-regex是一个只在backreference存在时才被执行的表达式,例子:
<!-- Nav Bar --> <TD> <a href='/home'><img src='/images/home.gif'></a> <img src=/images/spacer.gif'> <a href='/search'><img src='/images/search.gif'></a> <img src='/images/spacer.gif'> <a href='/help'><img src='/images/help.gif'></a> </td>
|
正则表达式为:
(<[Aa]\s+[^>]+>\s*)?<[Ii][Mm][Gg]\s+[^>]+>(?(1)\s*</[Aa]>)
解释如下:
(<[Aa]\s+[^>]+>\s*)?是匹配一个可有可无的<A>或者<a>标签
<[Ii][Mm][Gg]\s+[^>]+>匹配一个<img>以及其任意属性的语法。
(?(1)\s*</[Aa]>)是一个回溯引用条件,其中?(1)的含义是:若第一个回溯引用(具体到本实例,就是<A>标签)存在,则使用\s*</[Aa]>继续进行匹配(换句话说,只有当前面的<A>标签匹配成功,才继续进行后面的匹配)。如果(1)存在,\s*</[Aa]>将匹配结束标签</A>之后出现的任意空白字符。
附上在.NET中实现的代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Drawing;
namespace RegularExpressionConsoleApplication
{
class Program
{
static void Main(string[] args)
{
//LoopBackMatch(); //回溯匹配(涉及子表达式)
//LoopForwardMatch(); //向前匹配(?=)
//LoopAfterwardMatch(); //向后匹配(?<=)
//LoopForwardAndAfterwardMatch(); //向前向后匹配结合
ConditionItems(); //嵌入条件之回溯匹配
}
/// <summary>
/// 向前匹配
/// </summary>
static void LoopForwardMatch()
{
string text = " http://www.cnblogs.com";
string expression = @".+(?=:)";
Regex reg = new Regex(expression, RegexOptions.IgnoreCase);
MatchCollection mc = reg.Matches(text);
foreach (Match match in mc)
{
Console.WriteLine(match.Value);
}
Console.ReadKey();
}
/// <summary>
/// 向后匹配
/// </summary>
static void LoopAfterwardMatch()
{
string text = "ABC01: $12.56";
string expression = @"(?<=\$)[0-9.]+";
Regex reg = new Regex(expression, RegexOptions.IgnoreCase);
MatchCollection mc = reg.Matches(text);
foreach (Match match in mc)
{
Console.WriteLine(match.Value);
}
Console.ReadKey();
}
/// <summary>
/// 向前向后匹配结合
/// </summary>
static void LoopForwardAndAfterwardMatch()
{
StringBuilder sbStr = new StringBuilder();
sbStr.Append("<Head>");
sbStr.Append("\r\n ");
sbStr.Append("<Title>Ben Forta's HomePage</Title>");
sbStr.Append("\r\n");
sbStr.Append("</Head>");
string expression = @"(?<=<title>).*?(?=</title>)";
Regex reg = new Regex(expression, RegexOptions.IgnoreCase);
MatchCollection mc = reg.Matches(sbStr.ToString());
foreach (Match match in mc)
{
Console.WriteLine(match.Value);
}
Console.ReadKey();
}
/// <summary>
/// 注意,这里利用了回溯匹配法
/// </summary>
/// <param name="args"></param>
static void LoopBackMatch()
{
string text = " this is is a text, and I want to know know if there is any repeated words in it.";
string expression = @"[ ]+(\w+)[ ]+\1";
Regex reg = new Regex(expression, RegexOptions.IgnoreCase);
MatchCollection mc = reg.Matches(text);
foreach (Match match in mc)
{
Console.WriteLine(match.Value);
}
Console.ReadKey();
}
/// <summary>
/// 嵌入条件,待匹配文本如下:
/*********************************************************
* <!-- Nav Bar -->
* <TD>
* <a href='/home'><img src='/images/home.gif'></a>
* <img src=/images/spacer.gif'>
* <a href='/search'><img src='/images/search.gif'></a>
* <img src='/images/spacer.gif'>
* <a href='/help'><img src='/images/help.gif'></a>
* </td>
*********************************************************/
/// </summary>
static void ConditionItems()
{
StringBuilder text = new StringBuilder();
text.Append("<!-- Nav Bar -->");
text.Append("\r\n");
text.Append("<TD>");
text.Append("\r\n");
text.Append("<a href='/home'><img src='/images/home.gif'></a>");
text.Append("\r\n");
text.Append("<img src=/images/spacer.gif'>");
text.Append("\r\n");
text.Append("<a href='/search'><img src='/images/search.gif'></a>");
text.Append("\r\n");
text.Append("<img src='/images/spacer.gif'>");
text.Append("\r\n");
text.Append("<a href='/help'><img src='/images/help.gif'></a>");
text.Append("\r\n");
text.Append("</td>");
string expression = @"(<a\s+[^>]+>\s*)?<img\s+[^>]+>(?(1)\s*</a>)";
Regex reg = new Regex(expression, RegexOptions.IgnoreCase);
MatchCollection mc = reg.Matches(text.ToString());
//foreach (Match match in mc)
//{
// Console.WriteLine(match.Value);
//}
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("the total count of match result is: " + mc.Count);
for (int i = 0; i < mc.Count; i++)
{
Match match = mc[i];
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("|-Match["+i+"] is :"+match.Value);
GroupCollection groups = match.Groups;
for (int j = 0; j < groups.Count; j++)
{
Console.ForegroundColor = ConsoleColor.DarkGreen;
Console.WriteLine("|---Groups[" + j + "] is :" + groups[j]);
}
}
Console.ReadKey();
}
}
}
参考书籍:正则表达式必知必会