[C#] 正则表达式

正则表达式是判断、匹配和分解一定模式的字符串的法宝。C#强大的基础类库提供的System.Text.RegularExpressions命名空间里包含的Regex类,可以充分发挥正则表达式的威力。

需要查看C#中正则表达式里各个符号的意义的请查阅这里,不过看一遍也很难记住,可以用的时候再查询,用的次数多了就会记住那些使用频率较高的符号了。

0. 简单介绍C#里Regex类的用法

(1)检查一个字符串是否符合指定的模式,如pattern="^[0-9]*$"

Regex regex = new Regex(pattern);
if (regex.IsMatch(test)) 
  Console.WriteLine("{0} matches the pattern.", test); 
else Console.WriteLine("{0} does not match the pattern.", test);

(2)提取一定模式的字符串

Match match = regex.Match(text);
MatchCollection matches = regex.Matches(text);

如果能够确定text里面只有一个子串满足模式pattern,可以用Match;如果可能有多个匹配,则可以使用Matches。当然若我们只需要从包含多个pattern的text中找到第一个,那么也可以用Match
Match中两个重要的属性是ValueIndex,其中Value为满足设定模式的子串,Index为该子串在输入字符串中的开始位置。
另外,如果我们匹配的模式中有多个项,如"\b(?<word>\w+)\s+(\k<word>)\b"表示重复的单词,它的模式串中有两个word,我们最后得到的Match.Value可能是“dog dog”,而若我们想得到单个单词的话还需要进一步对这个子串进行划分。但是Match中还有一个重要的属性已经帮我们做好了这一步,Match.Groups获取由正则表达式匹配的组的集合,对于上面的例子Match.Groups["word"].Value就表示“dog”。

Regex rx = new Regex(@"\b(?<word>\w+)\s+(\k<word>)\b", RegexOptions.Compiled | RegexOptions.IgnoreCase);
string text = "The the quick brown fox  fox jumped over the lazy dog dog.";

Match singleMatch = rx.Match(text);
Console.WriteLine("Value: {0}", singleMatch.Value);
Console.WriteLine("Index: {0}", singleMatch.Index);

MatchCollection matches = rx.Matches(text);
foreach (Match match in matches)
{
    GroupCollection groups = match.Groups;
    Console.WriteLine("'{0}' repeated at positions {1} and {2}",
                       groups["word"].Value,
                       groups[0].Index,
                       groups[1].Index);
 }

(3)替换匹配的子串
string replactedText = rx.Replace(text, myEvaluator);
这里的参数myEvaluatorMatchEvaluator委托。下面的例子中我们专门定义了方法ReplaceCC2C()来将重复出现的单词去掉一个(即替换成单个单词)。MatchEvaluator委托的输入是Match,输出是要替换Match.Value的字符串。
如果替换的方法比较简单时,我们可以省去专门定义一个方法的力气,直接使用匿名方法,如

var test = rx.Replace(text, delegate(Match m) { return m.Groups["word"].Value; });
public void ExtractMatches()
{
    Regex rx = new Regex(@"\b(?<word>\w+)\s+(\k<word>)\b", RegexOptions.Compiled | RegexOptions.IgnoreCase);
    string text = "The the quick brown fox  fox jumped over the lazy dog dog.";

    RegexExample ex = new RegexExample();
    MatchEvaluator myEvaluator = new MatchEvaluator(ex.ReplaceCC2C);
    var replactedText = rx.Replace(text, myEvaluator);
    //var replactedText = rx.Replace(text, delegate(Match m) { return m.Groups["word"].Value; });
    Console.WriteLine(text);
    Console.WriteLine(replactedText);
}

public string ReplaceCC2C(Match m)
{
    return m.Groups["word"].Value;
}

输出结果:

The the quick brown fox  fox jumped over the lazy dog dog.
The quick brown fox jumped over the lazy dog.

(4)拆分字符串
string[] parts = rx.Split(text);
虽然C#string也有Split函数,但是它的参数只能是确定的字符数组,而RegexSplit函数则是利用正则表达式模式将输入字符串拆分成子字符串数组。

下面我们从一些常见的例子来尝试使用正则表达式,上面已经介绍的Regex的用法,下面的例子都是检查是否满足某模式串,都是用IsMatch,所以接下来我只会列出模式串。

1. 验证我国的座机电话号码

首先我们列出所有想要支持的电话号码的格式,然后再分类看怎么写正则表达式。
规则:我国的座机号码分为区号和号码,区号与号码之间用短横‘-’或者括号‘()’来分割,另外最前面还可能会加上我国的区号86或者+86,下面列举出一些合法的电话号码:
027-88888888
0722-7777777
(027)88888888
(0722)7777777
86(027)88888888
+86(0722)7777777
所以最后写出的表达式为:
"^(\+)?(86)?0\d{2,3}-\d{7,8}$|^(\+)?(86)?\(0\d{2,3}\)\d{7,8}$"

另外,我看到有人这样写"0\d{2,3}-\d{7,8}|\(0\d{2,3}\)\d{7,8}",虽然上面的例子都能通过,但是它可能会使得027-888888889999也是合法的,因为上述表达式是在输入字符串里找到一个子串满足我们的模式要求,只要存在这样的子串IsMatch就会返回true。所以我们需要在对模式串加上开始字符^和结束字符$来避免这种情况。

2. 验证手机电话号码

规则
手机号码为11位数字,首位一定是1,到目前为止第二位数字可以是3,4,5,7,8,其他位可以是0-9的任意数字。格式上可以是所有数字连写,也可以在中间加上短横分割。
正例
13012345678
15887654321
178-23760930
178-2376-0930
反例
1301234567
158876543212
178-2376-0930-
正则表达式"^1[34578]+\d{1}((-)?\d{4}){2}$"
逐字符解释^表示电话号码的开始,1表示第一位字符一定要是1,[34578]+表示第二位字符是3,4,5,7,8中的至少一个,\d{1}表示一个数字,同理\d{4}表示4个数字,(-)?表示短横可以出现0次或1次,((-)?\d{4}){2}表示(-)?\d{4}出现两次,$表示电话号码结束。

3. 验证电子邮箱

规则:必须以字母开始(有人说有邮箱可以以下划线开始,比如说Yahoo,但是我专门去注册试了试,人家要求一定要以字母开始);电子邮箱中包含且仅包含一个@字符,该字符将邮箱划分为两个部分;前部分为用户名,可以由字母、数字、下划线、短横或点组成;不能出现多个短横或点相连的情况;
正例
12345@qq.com
test.Name@163.com
full_name@google.com
__12-345@qq.com
反例
__12--345@qq.com
正则表达式"^[A-Za-z]+([-.]\w+)*@\w+([-.]\w+)*\.[a-z]{2,3}$"
逐字符解释^[A-Za-z]+表示至少有一个字母,\w+表示至少一个包括下划线的任何单词字符,等价于[A-Za-z0-9_]+([-.]\w+)*表示零个或者多个[-.]\w+,而[-.]\w+表示-或者.加上包括下划线的任何单词字符,这样写正好可以把多个短横或者点号用字符分隔开,@字符后面的前半部分与之前的类似,最后一部分为\.[a-z]{2,3},表示后缀为.加上2个或者2个小写字母。
:有人可能会说qq邮箱前面应该全部都是数字,所以上面的表达式不松了。这就看大家的使用环境了,比如说有些邮箱对用户名的长度也有限制,所以还是不要想一劳永逸了吧。只要在使用的时候把应用场景下的所有情况列举出来,能够满足要求即可。

4. 验证身份证号码

规则:早期的身份证号码是15位的全数字;现在的身份证号码为18位,由17位数字和1位校验字符组成;最后的校验字符可能0-10,其中10由字母X或者x表示。
正例
429001197806281
429001197806281234
42900119780628123X
42900119780628123x
反例
42900119780628123a
4290011978062812
4290011978062812345
正则表达式"^\d{15}$|^\d{18}$|^\d{17}[Xx]+$"
逐字符解释:以上分了三种情况,\d{15}表示15位数字,\d{17}[Xx]+表示17位数字再加上一位X或者x。
:其实现在身份证上的前面17位数字也不是任意的0-9的数字,比如说出生日期里的年月日都应该是有限制的,上述表达式并没有严格考虑这些,大家根据需要增加更严格的验证。

5. 验证IP地址

规则:点分十进制的IPV4地址,格式为X.X.X.X,其中X为0-255。
正例
127.0.0.1
255.255.255.255
0.0.0.0
248.250.198.23
反例
256.0.0.0
00.0.0.1
127.012.032.1234
正则表达式"^((25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)$"
逐字符解释:为了更好地表示IP地址中对数字的限制,这里我们将0-255划分成了多个区段250-255,240-249,100-199,10-99,0-9

小结一些常见的模式

^,匹配字符串的开始
\d,用来匹配0到9的数字
\w,用来匹配字母数字或者下划线
(xx)?,用来表示xx出现零次或者一次
(xx)+,用来表示xx至少出现一次
(xx)*,用来表示xx出现零次或者多次
(xx){2,3},用来表示xx出现两次或者三次
[123abc],其中[]用来表示里面出现的字符是或的关系
$,匹配字符串的结束

参考文献:

  1. Regex 类
  2. C#中正则表达式的使用
  3. C#正则表达式Regex类的用法
  4. C#正则表达式语法大全
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容