断言

  判断某个位置左侧或者右侧是否符合匹配。常见断言有三种,单词边界、行起始/结束位置、环视。阅读本章前,建议先下载我于CSDN上传的示例代码,下载无需分数,下载链接

1.单词边界

  正则表达式"\b"用于匹配单词的边界。实际上他匹配一个这样的位置,这个位置一边匹配"\w",一边不匹配"\w"。例如,输入字符串"I Love China!",若用正则表达式"\b\w+\b"来匹配,则能捕获子字符串"I","Love","China"。

 

2.行起始/结束位置

  行起始位置可用"^"匹配,行结束位置可用"$"匹配。这个看起来十分简单,但是有一点需要十分注意。当他们在单行模式下和在多行模式下能匹配的位置是不同的。起初在我学习正则表达式时,误以为单行模式是将输入的字符串看成是单行的,多行模式则看成多行。其实后来通过代码的测试并且看了一些其他的博客,才理解其实单行模式和多行模式,只是影响某一些正则表达式字符所能匹配的字符串而已。下面做出我对单行模式和多行模式的理解。

  首先,通过看资料得知在Windows平台下,每一行的换行符总是"\r\n"这样的。而其他平台的换行符则是"\n"这样的。

  单行模式下,"^"比较简单,即匹配文本的最开始的位置,不管你这个输入文本是单行的还是多行的。多行模式下,"^"除了具有单行模式的能力外,还能匹配"\n"之后的位置。但是不能匹配"\r"后面的位置,当时看了某篇博文误导了好久。Regex类下的Replace方法,对匹配的字符串替换成你想要的字符串。若匹配的是位置,这实际上是在该位置上添加你想要的字符串。  

Regex regex = new Regex("^");//默认所有都为单行模式
string replace = regex.Replace("abc\n", "|");//"|abc\n" 该值通过断点得出而非控制台
regex = new Regex("^", RegexOptions.Multiline);//多行模式
replace = regex.Replace("abc\n", "|");//"|abc\n|" 该值通过断点得出而非控制台

  单行模式下,"$"匹配文本的最后位置,其实这里说最后位置是不准确的,我无法给出一个准确的词语描述,只能同过代码来让你们注意某些事情。多行模式下,同样除了具有单行模式的能力外还能匹配"\n"之前的位置,并且也是不能匹配"\r"前面的位置。

Regex regex = new Regex("$");//默认所有都为单行模式
string replace = regex.Replace("abc\nabc\n", "|");//"abc\nabc|\n|" 该值通过断点得出而非控制台
replace = regex.Replace("abc\nabc\nabc", "|");//"abc\nabc\nabc|" 该值通过断点得出而非控制台
regex = new Regex("$", RegexOptions.Multiline);//多行模式
replace = regex.Replace("abc\nabc\n", "|");//"abc|\nabc|\n|" 该值通过断点得出而非控制台

  看到以上结果了吗,单行模式下的"$",如果是以"\n","\r\n"结尾的,除了匹配他们之后的位置,也会匹配他们之前的位置,导致得出第一个replace的结果。如果不以"\n","\r\n"结尾的则没有争议的在最后位置上匹配了。多行模式下同样有这个问题。为了解决这个问题,可以用"\z"来保证匹配字符串的最后一个位置,但是也不再有多行模式下的能力了。

Regex regex = new Regex(@"\z");//默认所有都为单行模式
string replace = regex.Replace("abc\nabc\n", "|");//"abc\nabc\n|" 该值通过断点得出而非控制台
replace = regex.Replace("abc\nabc\nabc", "|");//"abc\nabc\nabc|" 该值通过断点得出而非控制台
 regex = new Regex(@"\z", RegexOptions.Multiline);//多行模式
replace = regex.Replace("abc\nabc\n", "|");//"abc\nabc\n|" 该值通过断点得出而非控制台

 

3.环视

  环视的作用是,当匹配到某个位置的时候,我们要确定该位置的左边或右边是否匹配我们设定的正则表达式,若是,则该位置匹配,继续完成后面的匹配,若否,则不匹配,也无需继续后面的匹配了。环视语法:往右环视,即顺序环视"(?=regStr)","(?!regStr)",其中"="表示肯定,"!"表示否定,即要不要匹配的问题。往左环视,即逆序环视"(?<=regStr)","(?<!regStr)"。

  例子1:外国人的数字通常喜欢用","去分隔,如有一个数字在中国是这样的"123456789",在过外就变成这样了"123,456,789"。我们可以总结出这样的规律,在这样一个位置上添加",",该位置的右边必须是3倍个数的数字,该位置的左边还必须有数字。这样,我们写出了正则表达式"(?<=\d)(?=(?:\d{3})+)"

Regex regex = new Regex(@"(?<=\d)(?=(?:\d{3})+)");
Console.WriteLine(regex.Replace("123456789", ","));//1,2,3,4,5,6,789

  嗯,看来我们的思维还不够缜密,并不能一次编写正确的正则表达式,不过没关系,出现bug后我们可以立刻修正bug。导致这个bug的原因在于顺序环视时,当我们环视匹配完3倍个数的数字后,还必须保证他的后面再也没有数字了。找到问题的关键,我们来写出新的正则表达式"(?<=\d)(?=(?:\d{3})+(?!\d))"。在环视内部可以再嵌套一个环视。

Regex regex = new Regex(@"(?<=\d)(?=(?:\d{3})+(?!\d))");
Console.WriteLine(regex.Replace("123456789", ","));//123,456,789
Console.WriteLine(regex.Replace("1234567890", ","));//1,234,567,890

  例子2:当我们得到一个这样无序的,包含数字和字母的字符串"1sf2sdf333sf55ew2156we98552af3sd5fas6654sdfa65assfd56"时,我们可以相当容易的用"\d+"来捕获其中的数字,但如果我不要求捕获这些数字,而是要求将数字用括号括起来,并将字符串返回怎么办。思路,我们要匹配的是一个位置,并在这个位置上加上括号。这个位置可以很简单的想到,他的一边是数字,一边不是数字。当然,我们要分两步实现这个功能,第一步,先用正则表达式"(?<!\d)(?=\d)"匹配数字开始的位置。第二部,用正则表达式"(?<=\d)(?!\d)"匹配数字结束的位置。是不是十分方便,用这么少的代码便完成了我们以前不敢想的功能。

Regex regex = new Regex(@"(?<!\d)(?=\d)");//匹配数字开头
string replace = "1sf2sdf333sf55ew2156we98552af3sd5fas6654sdfa65assfd56";
replace = regex.Replace(replace,"(");//"(1sf(2sdf(333sf(55ew(2156we(98552af(3sd(5fas(6654sdfa(65assfd(56"
regex = new Regex(@"(?<=\d)(?!\d)");//匹配数字结尾
replace = regex.Replace(replace, ")");//"(1)sf(2)sdf(333)sf(55)ew(2156)we(98552)af(3)sd(5)fas(6654)sdfa(65)assfd(56)"
Console.WriteLine(replace);

 

  断言暂时介绍这么多,谢谢!