对于这个结果,或许有人会感到迷惑,字符串中的“\”不是代表一个经过转义的“”字符吗?而“\\”不就应该代表两个经过转义的“”字符吗?那么上面正则匹配的结果应该是第一个为False,第二个为True才对啊? 对于这一问题,直接解释或许不太容易理解,还是换种方式来解释吧。 比如要匹配的字符是这样的 string test = "("; 那么正则如何写呢?因为“(”在正则中是有特殊意义的,所以写正则时必须对它进行转义,也就是“(”,而在字符串中,要使用“\” 来表示“”本身,也就是 Regex reg = new Regex("^\($"); 这个如果理解了,那再把“(”换回“”,同样道理,在字符串中,要使用“\” 来表示“”本身,也就是 Regex reg = new Regex("^\\$"); 通过这样的分析,可以看出,其实在以字符串形式声明的正则中,“\\”匹配的实际上就是单独的一个“”字符。总结一下它们之间的关系: 输出到控制台或界面的字符串: 程序中声明的字符串:string test = "\"; 程序中声明的正则:Regex reg = new Regex("^\\$"); 这样解释是不是已经可以理解了,那么是不是感觉这样很笨拙?是的,在程序中以字符串形式声明的正则,涉及到转义符时就是这样笨拙的。 所以在C#中,还提供了另一种字符串声明方式,在字符串前加个“@”,就可以忽略转义。 复制代码 代码如下: string[] test = new string[] { @"", @"\" }; Regex reg = new Regex(@"^\$"); foreach (string s in test) { richTextBox2.Text += "源字符串: " + s.PadRight(5, " ") + "匹配结果: " + reg.IsMatch(s) + "
"; } /*--------输出-------- 源字符串: 匹配结果: True 源字符串: \ 匹配结果: False */
这样就简洁多了,也符合通常的理解。 但同时也带来另一个问题,就是双引号的转义处理。在普通的字符串声明中,可以用“””对双引号进行转义。 string test = "<a href="www.test.com">only a test</a>"; 但是在字符串前加了“@”后,“”会被识别为“”字符本身,这样就不能用“””对双引号进行转义了,需要用“”””对双引号进行转义。 string test = @"<a href=""www.test.com"">only a test</a>"; 而在VB.NET中,正则的定义只有一种形式,与C#中加了“@”后的定义方式是一致的。 复制代码 代码如下: Dim test As String() = New String() {"", "\"} Dim reg As Regex = New Regex("^\$") For Each s As String In test RichTextBox2.Text += "源字符串:" & s.PadRight(5, " "c) & "匹配结果:" & reg.IsMatch(s) & vbCrLf Next "--------输出-------- "源字符串: 匹配结果:True "源字符串:\ 匹配结果:False "--------------------
2.2 .NET正则中需要转义的元字符 在MSDN中,以下字符作为正则中的元字符,在匹配其本身时,需要对其进行转义 . $ ^ { [ ( | ) * + ? 但实际应用中,还要根据实际情况来判断,以上字符可能不需要转义,也可能不止以上字符需要转义。 在正常的正则书写过程中,以上字符的转义通常都能被编写人员正常处理,但是在动态生成正则时,就需要格外的注意,否则变量中包含元字符时,动态生成的正则在编译时可能会抛异常。好在.NET中提供了Regex.Escape方法来处理这一问题。比如根据动态获取的id来提取相应的div标签内容。 string id = Regex.Escape(textBox1.Text); Regex reg = new Regex(@"(?is)<div(?:(?!id=).)*id=(["""]?)" + id + @"1[^>]*>(?><div[^>]*>(?<o>)|</div>(?<-o>)|(?:(?!</?div).)*)* (?(o)(?!))</div>"); 如果不做转义处理,那么动态获取的id如果为“abc(def”这种形式,程序运行过程中就会抛出异常了。 2.3 .NET正则中字符组的转义 在字符组[]中,元字符通常是不需要转义的,甚至于“[”也是不需要转义的。 复制代码 代码如下: string test = @"the test string: . $ ^ { [ ( | ) * + ? "; Regex reg = new Regex(@"[.$^{[(|)*+?\]"); MatchCollection mc = reg.Matches(test); foreach (Match m in mc) { richTextBox2.Text += m.Value + "
"; } /*--------输出-------- . $ ^ { [ (
) * + ?
*/
但是在正则书写时,字符组中的“[”还是建议使用“[”对其转义的,正则本身就已经是非常抽象,可读性很低的了,如果在字符组中再掺杂进这样不经转义的“[”,会使得可读性更差。而且在出现不正确的嵌套时,可能会导致正则编译异常,以下正则在编译时就会抛异常的。 Regex reg = new Regex(@"[.$^{[(]|)*+?\]"); 然而,.NET的字符组中,是支持集合减法的,在这种正常语法形式下,是允许字符组嵌套的。 复制代码 代码如下: string test = @"abcdefghijklmnopqrstuvwxyz"; Regex reg = new Regex(@"[a-z-[aeiou]]+"); MatchCollection mc = reg.Matches(test); foreach (Match m in mc) { richTextBox2.Text += m.Value + "
"; } /*--------输出-------- bcd fgh jklmn pqrst vwxyz */
这种用法可读性很差,应用也很少见,即使有这种需求也可以通过其它方式实现,了解一下即可,不必深究。 话题再回到转义上,字符组中必须转义的只有“”,而“[”和“]”出现在字符组中时,也是建议一定做转义处理的。另外有两个字符“^”和“-”,出现在字符组中特定位置时,如果要匹配其本身,也是需要转义的。 “^”出现在字符组开始位置,表示排除型字符组,“[^Char]”也就是匹配除字符组中包含的字符之外的任意一个字符,比如“[^0-9]”表示除数字外的任意一个字符。所以在字符组中,要匹配“^”字符本身,要么不放在字符组开始位置,要么用“^”进行转义。 Regex reg1 = new Regex(@"[0-9^]"); Regex reg2 = new Regex(@"[^0-9]"); 这两种方式都表达匹配任意一个数字或普通字符“^”。 至于“-”在字符组中特殊性,举一个例子。 复制代码 代码如下: string test = @"$"; Regex reg = new Regex(@"[#-*%&]"); richTextBox2.Text = "匹配结果:" + reg.IsMatch(test); /*--------输出-------- 匹配结果:True */
正则表达式一Regex("line"),这里的“”是退格符,是不经过正则引擎转义的。源字符串中是没有的,所以匹配结果为0。 正则表达式二Regex("line\b"),是以正则表达式形式来声明正则的,这里的“\b”是单词边界,是经过正则引擎转义的。 正则表达式三Regex(@"line"),与正则表达式二等价,指单词边界。 正则表达式四Regex(@"line\b"),其实这个表示的是字符“”后面跟一个字符“b”,是两个字符,这个在源字符串中自然是找不到匹配项的。 2.5 .NET正则应用中其它转义处理 .NET正则应用中还有一些其它转义方式,虽然用得不多,但也顺便提一下吧。 需求:把字符串中“<”和“>”之间的数字前加上“$” 复制代码 代码如下: string test = "one test <123>, another test <321>"; Regex reg = new Regex(@"<(d+)>"); string result = reg.Replace(test, "<$$1>"); richTextBox2.Text = result; /*--------输出-------- one test <$1>, another test <$1> */ 也许你会惊奇的发现,替换结果不是在数字前加了“$”,而是将所有数字都替换为“$1”了。 为什么会这样呢,这是因为在替换结构中,“$”是有特殊意义的,在它后面接数字,表示对对应编号捕获组匹配结果的引用,而有些情况下,需要在替换结果中出现“$”字符本身,但它后面又跟了数字,这时候就需要用“$$”对它进行转义了。而上面这个例子却恰恰是由于这种转义效果导致出现了异常结果,要规避这一问题,可以使替换结果中不出现对捕获组的引用。 string test = "one test <123>, another test <321>"; Regex reg = new Regex(@"(?<=<)(?=d+>)"); string result = reg.Replace(test, "$"); richTextBox2.Text = result; /*--------输出-------- one test <$123>, another test <$321> */