中学网站模板下载,360建筑网撤销自己的简历怎么撤销,移动网站建设动态,wordpress functions.php 在哪1 概述 这或许会是一个让人迷惑#xff0c;甚至感到混乱的话题#xff0c;但也正因为如此#xff0c;才有了讨论的必要。 在正则中#xff0c;一些具有特殊意义的字符#xff0c;或是字符序列#xff0c;被称作元字符#xff0c;如“?”表示被修饰的子表达式匹配…1 概述 这或许会是一个让人迷惑甚至感到混乱的话题但也正因为如此才有了讨论的必要。 在正则中一些具有特殊意义的字符或是字符序列被称作元字符如“?”表示被修饰的子表达式匹配0次或1次“(?i)”表示忽略大小写的匹配模式等等。而当这些元字符被要求匹配其本身时就要进行转义处理了。 不同的语言或应用场景下正则定义方式、元字符出现的位置不同转义的方式也是林林总总不一而同。 2 .NET正则中的字符转义 2.1 .NET正则中的转义符 绝大多数语言中“\”都被作为转义符用来转义一些具有特殊意义的字符或字符序列比如“\n”表示换行“\t”表示水平制表符等。而这样的转义应用到正则中又会有一些意想不到的变化。 话题由C#中一个正则问题引出 string[] test new string[]{\\, \\\\}; Regex reg new Regex(^\\\\$); foreach (string s in test) { richTextBox2.Text 源字符串 s.PadRight(5, ) 匹配结果 reg.IsMatch(s) \n; } /*--------输出-------- 源字符串 \ 匹配结果 True 源字符串 \\ 匹配结果 False */ 对于这个结果或许有人会感到迷惑字符串中的“\\”不是代表一个经过转义的“\”字符吗而“\\\\”不就应该代表两个经过转义的“\”字符吗那么上面正则匹配的结果应该是第一个为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) \n; } /*--------输出-------- 源字符串 \ 匹配结果 True 源字符串 \\ 匹配结果 False */ 这样就简洁多了也符合通常的理解。 但同时也带来另一个问题就是双引号的转义处理。在普通的字符串声明中可以用“\””对双引号进行转义。 string test a href\www.test.com\only a test/a; 但是在字符串前加了“”后“\”会被识别为“\”字符本身这样就不能用“\””对双引号进行转义了需要用“”””对双引号进行转义。 string test a hrefwww.test.comonly 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\b).)*)* (?(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 \n; } /*--------输出-------- . $ ^ { [ ( | ) * ? \ */ 但是在正则书写时字符组中的“[”还是建议使用“\[”对其转义的正则本身就已经是非常抽象可读性很低的了如果在字符组中再掺杂进这样不经转义的“[”会使得可读性更差。而且在出现不正确的嵌套时可能会导致正则编译异常以下正则在编译时就会抛异常的。 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 \n; } /*--------输出-------- 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 */ 正则表达式中明明没有“$”为什么匹配结果会是“True”呢 []支持用连字符“-”连接两个字符来表示一个字符范围。需要注意的是“-”前后的两个字符是有顺序的在使用相同的编码时后面的字符码位应大于或等于前面字符的码位。 for (int i #; i *; i) { richTextBox2.Text (char)i \n; } /*--------输出-------- # $ % ( ) * */ 由于“#”和“*”符合要求“[#-*]”可以表示一个字符范围其中就包含了字符“$”所以上面的正则是可以匹配“$”的如果只是把“-”当作一个普通字符处理那么要么换个位置要么把“-”转义。 Regex reg1 new Regex([#*%-]); Regex reg2 new Regex([#\-*%]); 这两种方式都表示匹配字符组中列举的字符中的任意一个。 在字符组中还有一个比较特殊的转义字符“\b”出现在正则表达式中一般位置时表示单词边界也就是一侧为组成单词的字符另一侧不是而当“\b”出现在字符组中时表示的是退格符与普通字符串中出现的“\b”意义是一样的。 同样的还有一个容易被忽视而且经常被忽视的转义符“|”当“|”出现在正则表达式中一般位置时表示左右两侧“或”的关系而当“|”出现在字符组中时它仅仅表示“|”字符本身没有任何特殊意义所以如果不是要匹配“|”本身而试图在字符组中使用“|”时是错误的。比如正则表达式“[a|b]”表示的是“a”、“b”、“|”中的任意一个而不是“a”或“b”。 2.4 .NET正则应用中不可见字符转义处理 对于一些不可见字符要在字符串中表示时需要用转义字符比较常见的有“\r”、“\n”、“\t”等等而这些字符在正则中应用就变得有些神奇了先看一段代码。 string test one line. \n another line.; ListRegex list new ListRegex(); list.Add(new Regex(\n)); list.Add(new Regex(\\n)); list.Add(new Regex(\n)); list.Add(new Regex(\\n)); foreach (Regex reg in list) { richTextBox2.Text 正则表达式 reg.ToString(); MatchCollection mc reg.Matches(test); foreach (Match m in mc) { richTextBox2.Text 匹配内容 m.Value 匹配起始位置 m.Index 匹配长度 m.Length; } richTextBox2.Text 匹配总数 reg.Matches(test).Count \n----------------\n; } /*--------输出-------- 正则表达式 匹配内容 匹配起始位置10 匹配长度1 匹配总数1 ---------------- 正则表达式\n 匹配内容 匹配起始位置10 匹配长度1 匹配总数1 ---------------- 正则表达式\n 匹配内容 匹配起始位置10 匹配长度1 匹配总数1 ---------------- 正则表达式\\n 匹配总数0 ---------------- */ 可以看到前三种写法输出的正则虽不同但执行结果却是完全相同的只有最后一种是没有匹配的。 正则表达式一Regex(\n)其实就是以普通字符串形式来声明正则的与用Regex(a)来匹配字符“a”是同样的道理是不经过正则引擎转义的。 正则表达式二Regex(\\n)是以正则表达式形式来声明正则的正如正则中的“\\\\”就等同于字符串中的“\\”一样正则中的“\\n”就等同于字符串中的“\n”是经过正则引擎转义的。 正则表达式三Regex(\n)与正则表达式二等价是字符串前加“”的写法。 正则表达式四Regex(\\n)其实这个表示的是字符“\”后面跟一个字符“n”是两个字符这个在源字符串中自然是找不到匹配项的。 这里需要特别注意的还是“\b”不同的声明方式“\b”的意义是不同的。 string test one line. \n another line.; ListRegex list new ListRegex(); list.Add(new Regex(line\b)); list.Add(new Regex(line\\b)); list.Add(new Regex(line\b)); list.Add(new Regex(line\\b)); foreach (Regex reg in list) { richTextBox2.Text 正则表达式 reg.ToString() \n; MatchCollection mc reg.Matches(test); foreach (Match m in mc) { richTextBox2.Text 匹配内容 m.Value 匹配起始位置 m.Index 匹配长度 m.Length \n; } richTextBox2.Text 匹配总数 reg.Matches(test).Count \n----------------\n; } /*--------输出-------- 正则表达式line_ 匹配总数0 ---------------- 正则表达式line\b 匹配内容line 匹配起始位置4 匹配长度4 匹配内容line 匹配起始位置20 匹配长度4 匹配总数2 ---------------- 正则表达式line\b 匹配内容line 匹配起始位置4 匹配长度4 匹配内容line 匹配起始位置20 匹配长度4 匹配总数2 ---------------- 正则表达式line\\b 匹配总数0 ---------------- */ 正则表达式一Regex(line\b)这里的“\b”是退格符是不经过正则引擎转义的。源字符串中是没有的所以匹配结果为0。 正则表达式二Regex(line\\b)是以正则表达式形式来声明正则的这里的“\\b”是单词边界是经过正则引擎转义的。 正则表达式三Regex(line\b)与正则表达式二等价指单词边界。 正则表达式四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 */ 3 JavaScript及Java中的转义符 JavaScript及Java中正则的转义符处理以字符串形式声明时基本上都是与.NET中一致的简单的介绍一下。 在JavaScript中以字符串形式声明正则与C#中的表现是一样的同样会显得很笨拙。 script typetext/javascript var data [\\, \\\\]; var reg new RegExp(^\\\\$, ); for(var i0;idata.length;i) { document.write(源字符串 data[i] 匹配结果 reg.test(data[i]) br /); } /script /*--------输出-------- 源字符串\ 匹配结果true 源字符串\\ 匹配结果false */ JavaScript中虽然没有提供C#中这种“”方式的字符串声明方式但提供了另一种正则表达式的专有声明方式。 script typetext/javascript var data [\\, \\\\]; var reg /^\\$/; for(var i0;idata.length;i) { document.write(源字符串 data[i] 匹配结果 reg.test(data[i]) br /); } /script /*--------输出-------- 源字符串\ 匹配结果true 源字符串\\ 匹配结果false */ JavaScript中 var reg /Expression/igm; 这种声明方式一样可以简化含有转义符的正则。 当然以这种形式声明正则时“/”自然也就成为了元字符正则中出现这一字符时必须进行转义处理。比如匹配链接中域名的正则 var reg /http:\/\/:([^\/])/ig; 很不幸的是在Java中目前只提供了一种正则声明方式也就是字符串形式的声明方式 String test[] new String[]{\\, \\\\ }; String reg ^\\\\$; for(int i0;itest.length ;i) { System.out.println(源字符串 test[i] 匹配结果 Pattern.compile(reg).matcher(test[i]).find()); } /*--------输出-------- 源字符串\ 匹配结果true 源字符串\\ 匹配结果false */ 只能期待Java的后续版本能提供这方面的优化了。